diff options
author | 2019-11-15 15:05:15 +0000 | |
---|---|---|
committer | 2019-11-15 15:05:15 +0000 | |
commit | 993e82eec7d79b7cc5a406f740e95aed9f950a99 (patch) | |
tree | fdee8083a141fc694e910b56f3d6920b65f4528f | |
parent | 76939b7e6f5d3c4eb5d04b3bcc7f5eac788cfb3b (diff) | |
parent | 5560f38654dc972700fdb0bfdc247ff3892a514c (diff) |
Merge "Add setting to go to sleep after long user inactivity"
23 files changed, 887 insertions, 74 deletions
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index dd1f8c31ebb1..f18b4db6dc51 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -426,9 +426,15 @@ public final class PowerManager { public static final int GO_TO_SLEEP_REASON_FORCE_SUSPEND = 8; /** + * Go to sleep reason code: Going to sleep due to user inattentiveness. * @hide */ - public static final int GO_TO_SLEEP_REASON_MAX = GO_TO_SLEEP_REASON_FORCE_SUSPEND; + public static final int GO_TO_SLEEP_REASON_INATTENTIVE = 9; + + /** + * @hide + */ + public static final int GO_TO_SLEEP_REASON_MAX = GO_TO_SLEEP_REASON_INATTENTIVE; /** * @hide @@ -444,6 +450,7 @@ public final class PowerManager { case GO_TO_SLEEP_REASON_SLEEP_BUTTON: return "sleep_button"; case GO_TO_SLEEP_REASON_ACCESSIBILITY: return "accessibility"; case GO_TO_SLEEP_REASON_FORCE_SUSPEND: return "force_suspend"; + case GO_TO_SLEEP_REASON_INATTENTIVE: return "inattentive"; default: return Integer.toString(sleepReason); } } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index ac53f1b27680..50dac46d0597 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -7707,6 +7707,20 @@ public final class Settings { public static final String SLEEP_TIMEOUT = "sleep_timeout"; /** + * The timeout in milliseconds before the device goes to sleep due to user inattentiveness, + * even if the system is holding wakelocks. It should generally be longer than {@code + * config_attentiveWarningDuration}, as otherwise the device will show the attentive + * warning constantly. Small timeouts are discouraged, as they will cause the device to + * go to sleep quickly after waking up. + * <p> + * Use -1 to disable this timeout. + * </p> + * + * @hide + */ + public static final String ATTENTIVE_TIMEOUT = "attentive_timeout"; + + /** * Controls whether double tap to wake is enabled. * @hide */ diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl index 73f549a31bac..20706bb4b21f 100644 --- a/core/java/com/android/internal/statusbar/IStatusBar.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl @@ -188,4 +188,14 @@ oneway interface IStatusBar * @param types the internal insets types of the bars are about to abort the transient state. */ void abortTransient(int displayId, in int[] types); + + /** + * Show a warning that the device is about to go to sleep due to user inactivity. + */ + void showInattentiveSleepWarning(); + + /** + * Dismiss the warning that the device is about to go to sleep due to user inactivity. + */ + void dismissInattentiveSleepWarning(); } diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl index 3f08710bb17f..76235a4a892e 100644 --- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl @@ -113,4 +113,14 @@ interface IStatusBarService void onBiometricError(int modality, int error, int vendorCode); // Used to hide the authentication dialog, e.g. when the application cancels authentication void hideAuthenticationDialog(); + + /** + * Show a warning that the device is about to go to sleep due to user inactivity. + */ + void showInattentiveSleepWarning(); + + /** + * Dismiss the warning that the device is about to go to sleep due to user inactivity. + */ + void dismissInattentiveSleepWarning(); } diff --git a/core/proto/android/server/powermanagerservice.proto b/core/proto/android/server/powermanagerservice.proto index 091e1c2bb05a..c03957087f26 100644 --- a/core/proto/android/server/powermanagerservice.proto +++ b/core/proto/android/server/powermanagerservice.proto @@ -172,6 +172,8 @@ message PowerManagerServiceDumpProto { repeated SuspendBlockerProto suspend_blockers = 48; optional WirelessChargerDetectorProto wireless_charger_detector = 49; optional BatterySaverStateMachineProto battery_saver_state_machine = 50; + // Attentive timeout in ms. The timeout is disabled if it is set to -1. + optional sint32 attentive_timeout_ms = 51; } // A com.android.server.power.PowerManagerService.SuspendBlockerImpl object. @@ -310,6 +312,12 @@ message PowerServiceSettingsAndConfigurationDumpProto { optional bool is_vr_mode_enabled = 35; // True if Sidekick is controlling the display and we shouldn't change its power mode. optional bool draw_wake_lock_override_from_sidekick = 36; + // The attentive timeout setting value in milliseconds. Default value is -1. + optional sint32 attentive_timeout_setting_ms = 37; + // The attentive timeout config value in milliseconds. + optional sint32 attentive_timeout_config_ms = 38; + // The attentive warning duration config value in milliseconds. + optional sint32 attentive_warning_duration_config_ms = 39; } message BatterySaverStateMachineProto { diff --git a/core/res/res/values-television/config.xml b/core/res/res/values-television/config.xml index 3ecb1dddd916..655d4dde5e57 100644 --- a/core/res/res/values-television/config.xml +++ b/core/res/res/values-television/config.xml @@ -42,4 +42,8 @@ <!-- Allow SystemUI to show the shutdown dialog --> <bool name="config_showSysuiShutdown">true</bool> + + <!-- The time in milliseconds of prolonged user inactivity after which device goes to sleep, + even if wakelocks are held. On TVs, this defaults to 4 hours. --> + <integer name="config_attentiveTimeout">14400000</integer> </resources> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index cb0b5993c420..e6f038a2829c 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -959,6 +959,14 @@ The default is false. --> <bool name="config_suspendWhenScreenOffDueToProximity">false</bool> + <!-- The time in milliseconds of prolonged user inactivity after which device goes to sleep, + even if wakelocks are held. --> + <integer name="config_attentiveTimeout">-1</integer> + + <!-- How long to show a warning message to user before the device goes to sleep after prolonged + user inactivity. --> + <integer name="config_attentiveWarningDuration">30000</integer> + <!-- Control the behavior when the user long presses the power button. 0 - Nothing 1 - Global actions menu diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 7854eedca600..42e62d740edd 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -5238,13 +5238,6 @@ <!-- Application name displayed in notifications [CHAR LIMIT=60] --> <string name="notification_app_name_settings">Settings</string> - <!-- Title of the overlay warning the user to interact with the device or it will go into standby. [CHAR LIMIT=25] --> - <string name="standby_warning_title">Standby</string> - <!-- Message of the overlay warning the user to interact with the device or it will go into standby. [CHAR LIMIT=NONE] --> - <string name="standby_warning_message" product="tv">The Android TV device will soon turn off; press a button to keep it on.</string> - <!-- Message of the overlay warning the user to interact with the device or it will go into standby. [CHAR LIMIT=NONE] --> - <string name="standby_warning_message" product="default">The device will soon turn off; press to keep it on.</string> - <!-- Active Permission - accessibility support --> <!-- Content description of the camera icon in the notification. [CHAR LIMIT=NONE] --> <string name="notification_appops_camera_active">Camera</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 28809daeaad8..e56bbf6fa08f 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2117,6 +2117,8 @@ <java-symbol type="integer" name="config_minimumScreenOffTimeout" /> <java-symbol type="integer" name="config_maximumScreenDimDuration" /> <java-symbol type="fraction" name="config_maximumScreenDimRatio" /> + <java-symbol type="integer" name="config_attentiveTimeout" /> + <java-symbol type="integer" name="config_attentiveWarningDuration" /> <java-symbol type="string" name="config_customAdbPublicKeyConfirmationComponent" /> <java-symbol type="string" name="config_customAdbPublicKeyConfirmationSecondaryUserComponent" /> <java-symbol type="string" name="config_customVpnConfirmDialogComponent" /> diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 10d990a3d07f..289ac802ccb0 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -605,6 +605,7 @@ public class SettingsBackupTest { Settings.Secure.ASSIST_GESTURE_SETUP_COMPLETE, Settings.Secure.ASSIST_SCREENSHOT_ENABLED, Settings.Secure.ASSIST_STRUCTURE_ENABLED, + Settings.Secure.ATTENTIVE_TIMEOUT, Settings.Secure.AUTOFILL_FEATURE_FIELD_CLASSIFICATION, Settings.Secure.AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT, Settings.Secure.AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE, diff --git a/packages/SystemUI/res/layout-television/inattentive_sleep_warning.xml b/packages/SystemUI/res/layout-television/inattentive_sleep_warning.xml new file mode 100644 index 000000000000..eb21c43aaa1c --- /dev/null +++ b/packages/SystemUI/res/layout-television/inattentive_sleep_warning.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<FrameLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/sleep_warning_dialog_container" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:theme="@android:style/Theme.DeviceDefault.Dialog" + android:focusable="true"> + <View + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@android:color/black" + android:alpha="?android:backgroundDimAmount" /> + <LinearLayout + android:layout_width="380dp" + android:layout_height="wrap_content" + android:background="@drawable/rounded_bg_full" + android:padding="16dp" + android:layout_margin="32dp" + android:layout_gravity="bottom|right" + android:orientation="vertical"> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/inattentive_sleep_warning_title" + android:layout_marginBottom="8dp" + android:textColor="?android:attr/textColorPrimary" + android:textAppearance="@android:style/TextAppearance.DeviceDefault.Large"/> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/inattentive_sleep_warning_message" + android:textColor="?android:attr/textColorSecondary" + android:textAppearance="@android:style/TextAppearance.DeviceDefault"/> + </LinearLayout> +</FrameLayout> diff --git a/packages/SystemUI/res/layout/inattentive_sleep_warning.xml b/packages/SystemUI/res/layout/inattentive_sleep_warning.xml new file mode 100644 index 000000000000..f1f9b1fcf74e --- /dev/null +++ b/packages/SystemUI/res/layout/inattentive_sleep_warning.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<FrameLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/sleep_warning_dialog_container" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:theme="@android:style/Theme.Material.Dialog" + android:focusable="true"> + <View + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@android:color/black" + android:alpha="?android:backgroundDimAmount" /> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="@drawable/rounded_bg_full" + android:layout_margin="8dp" + android:padding="16dp" + android:layout_gravity="top" + android:orientation="vertical"> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/inattentive_sleep_warning_title" + android:layout_marginBottom="8dp" + android:textColor="?android:attr/textColorPrimary" + android:textAppearance="@android:style/TextAppearance.Material.Large"/> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/inattentive_sleep_warning_message" + android:textColor="?android:attr/textColorSecondary" + android:textAppearance="@android:style/TextAppearance.Material"/> + </LinearLayout> +</FrameLayout> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index dff21cfd2c22..c3f410ea4e55 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -281,6 +281,7 @@ <item>com.android.systemui.statusbar.phone.StatusBar</item> <item>com.android.systemui.usb.StorageNotification</item> <item>com.android.systemui.power.PowerUI</item> + <item>com.android.systemui.power.InattentiveSleepWarningController</item> <item>com.android.systemui.media.RingtonePlayer</item> <item>com.android.systemui.keyboard.KeyboardUI</item> <item>com.android.systemui.pip.PipUI</item> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 19daa9039f55..313854701d69 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2507,4 +2507,11 @@ <!-- Notification content text when switching to a default launcher that supports gesture navigation [CHAR LIMIT=NONE] --> <string name="notification_content_gesture_nav_available">Go to Settings to update system navigation</string> + <!-- Title of the overlay warning the user to interact with the device or it will go to sleep. [CHAR LIMIT=25] --> + <string name="inattentive_sleep_warning_title">Standby</string> + <!-- Message of the overlay warning the user to interact with the device or it will go to sleep. [CHAR LIMIT=NONE] --> + <string name="inattentive_sleep_warning_message" product="tv">The Android TV device will soon turn off; press a button to keep it on.</string> + <!-- Message of the overlay warning the user to interact with the device or it will go to sleep. [CHAR LIMIT=NONE] --> + <string name="inattentive_sleep_warning_message" product="default">The device will soon turn off; press to keep it on.</string> + </resources> diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java index 3cf14d65e5b8..25986c5f4160 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java @@ -25,6 +25,7 @@ import com.android.systemui.biometrics.AuthController; import com.android.systemui.globalactions.GlobalActionsComponent; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.pip.PipUI; +import com.android.systemui.power.InattentiveSleepWarningController; import com.android.systemui.power.PowerUI; import com.android.systemui.recents.Recents; import com.android.systemui.recents.RecentsModule; @@ -102,6 +103,13 @@ public abstract class SystemUIBinder { @ClassKey(PowerUI.class) public abstract SystemUI bindPowerUI(PowerUI sysui); + /** Inject into InattentiveSleepWarningController. */ + @Binds + @IntoMap + @ClassKey(InattentiveSleepWarningController.class) + public abstract SystemUI bindInattentiveSleepWarningController( + InattentiveSleepWarningController sysui); + /** Inject into Recents. */ @Binds @IntoMap diff --git a/packages/SystemUI/src/com/android/systemui/power/InattentiveSleepWarningController.java b/packages/SystemUI/src/com/android/systemui/power/InattentiveSleepWarningController.java new file mode 100644 index 000000000000..716943164b61 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/power/InattentiveSleepWarningController.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.power; + +import android.content.Context; + +import com.android.systemui.SystemUI; +import com.android.systemui.statusbar.CommandQueue; + +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * Receives messages sent from {@link com.android.server.power.InattentiveSleepWarningController} + * and shows the appropriate inattentive sleep UI (e.g. {@link InattentiveSleepWarningView}). + */ +@Singleton +public class InattentiveSleepWarningController extends SystemUI implements CommandQueue.Callbacks { + private final CommandQueue mCommandQueue; + private InattentiveSleepWarningView mOverlayView; + + @Inject + public InattentiveSleepWarningController(Context context, CommandQueue commandQueue) { + super(context); + mCommandQueue = commandQueue; + } + + @Override + public void start() { + mCommandQueue.addCallback(this); + } + + @Override + public void showInattentiveSleepWarning() { + if (mOverlayView == null) { + mOverlayView = new InattentiveSleepWarningView(mContext); + } + + mOverlayView.show(); + } + + @Override + public void dismissInattentiveSleepWarning() { + if (mOverlayView != null) { + mOverlayView.dismiss(); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/power/InattentiveSleepWarningView.java b/packages/SystemUI/src/com/android/systemui/power/InattentiveSleepWarningView.java new file mode 100644 index 000000000000..8ccc679d32b0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/power/InattentiveSleepWarningView.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.power; + +import android.content.Context; +import android.graphics.PixelFormat; +import android.os.Binder; +import android.os.IBinder; +import android.view.LayoutInflater; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.widget.FrameLayout; + +import com.android.systemui.R; + +/** + * View that shows a warning shortly before the device goes into sleep + * after prolonged user inactivity when bound to. + */ +public class InattentiveSleepWarningView extends FrameLayout { + private final IBinder mWindowToken = new Binder(); + private final WindowManager mWindowManager; + + InattentiveSleepWarningView(Context context) { + super(context); + mWindowManager = mContext.getSystemService(WindowManager.class); + + final LayoutInflater layoutInflater = LayoutInflater.from(mContext); + layoutInflater.inflate(R.layout.inattentive_sleep_warning, this, true /* attachToRoot */); + + setFocusable(true); + setOnKeyListener((v, keyCode, event) -> { + // overlay consumes key presses + return true; + }); + } + + /** + * Show the warning. + */ + public void show() { + if (getParent() == null) { + mWindowManager.addView(this, getLayoutParams(mWindowToken)); + } + } + + /** + * Dismiss the warning. + */ + public void dismiss() { + if (getParent() != null) { + mWindowManager.removeView(this); + } + } + + /** + * @param windowToken token for the window + */ + private WindowManager.LayoutParams getLayoutParams(IBinder windowToken) { + final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT, + WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, + WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN, + PixelFormat.TRANSLUCENT); + lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; + lp.setTitle("InattentiveSleepWarning"); + lp.token = windowToken; + return lp; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index 621f101cd8af..88b6fdda3dd5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -70,53 +70,55 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< private static final int OP_SET_ICON = 1; private static final int OP_REMOVE_ICON = 2; - private static final int MSG_ICON = 1 << MSG_SHIFT; - private static final int MSG_DISABLE = 2 << MSG_SHIFT; - private static final int MSG_EXPAND_NOTIFICATIONS = 3 << MSG_SHIFT; - private static final int MSG_COLLAPSE_PANELS = 4 << MSG_SHIFT; - private static final int MSG_EXPAND_SETTINGS = 5 << MSG_SHIFT; - private static final int MSG_SYSTEM_BAR_APPEARANCE_CHANGED = 6 << MSG_SHIFT; - private static final int MSG_DISPLAY_READY = 7 << MSG_SHIFT; - private static final int MSG_SHOW_IME_BUTTON = 8 << MSG_SHIFT; - private static final int MSG_TOGGLE_RECENT_APPS = 9 << MSG_SHIFT; - private static final int MSG_PRELOAD_RECENT_APPS = 10 << MSG_SHIFT; - private static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 11 << MSG_SHIFT; - private static final int MSG_SET_WINDOW_STATE = 12 << MSG_SHIFT; - private static final int MSG_SHOW_RECENT_APPS = 13 << MSG_SHIFT; - private static final int MSG_HIDE_RECENT_APPS = 14 << MSG_SHIFT; - private static final int MSG_SHOW_SCREEN_PIN_REQUEST = 18 << MSG_SHIFT; - private static final int MSG_APP_TRANSITION_PENDING = 19 << MSG_SHIFT; - private static final int MSG_APP_TRANSITION_CANCELLED = 20 << MSG_SHIFT; - private static final int MSG_APP_TRANSITION_STARTING = 21 << MSG_SHIFT; - private static final int MSG_ASSIST_DISCLOSURE = 22 << MSG_SHIFT; - private static final int MSG_START_ASSIST = 23 << MSG_SHIFT; - private static final int MSG_CAMERA_LAUNCH_GESTURE = 24 << MSG_SHIFT; - private static final int MSG_TOGGLE_KEYBOARD_SHORTCUTS = 25 << MSG_SHIFT; - private static final int MSG_SHOW_PICTURE_IN_PICTURE_MENU = 26 << MSG_SHIFT; - private static final int MSG_ADD_QS_TILE = 27 << MSG_SHIFT; - private static final int MSG_REMOVE_QS_TILE = 28 << MSG_SHIFT; - private static final int MSG_CLICK_QS_TILE = 29 << MSG_SHIFT; - private static final int MSG_TOGGLE_APP_SPLIT_SCREEN = 30 << MSG_SHIFT; - private static final int MSG_APP_TRANSITION_FINISHED = 31 << MSG_SHIFT; - private static final int MSG_DISMISS_KEYBOARD_SHORTCUTS = 32 << MSG_SHIFT; - private static final int MSG_HANDLE_SYSTEM_KEY = 33 << MSG_SHIFT; - private static final int MSG_SHOW_GLOBAL_ACTIONS = 34 << MSG_SHIFT; - private static final int MSG_TOGGLE_PANEL = 35 << MSG_SHIFT; - private static final int MSG_SHOW_SHUTDOWN_UI = 36 << MSG_SHIFT; - private static final int MSG_SET_TOP_APP_HIDES_STATUS_BAR = 37 << MSG_SHIFT; - private static final int MSG_ROTATION_PROPOSAL = 38 << MSG_SHIFT; - private static final int MSG_BIOMETRIC_SHOW = 39 << MSG_SHIFT; - private static final int MSG_BIOMETRIC_AUTHENTICATED = 40 << MSG_SHIFT; - private static final int MSG_BIOMETRIC_HELP = 41 << MSG_SHIFT; - private static final int MSG_BIOMETRIC_ERROR = 42 << MSG_SHIFT; - private static final int MSG_BIOMETRIC_HIDE = 43 << MSG_SHIFT; - private static final int MSG_SHOW_CHARGING_ANIMATION = 44 << MSG_SHIFT; - private static final int MSG_SHOW_PINNING_TOAST_ENTER_EXIT = 45 << MSG_SHIFT; - private static final int MSG_SHOW_PINNING_TOAST_ESCAPE = 46 << MSG_SHIFT; - private static final int MSG_RECENTS_ANIMATION_STATE_CHANGED = 47 << MSG_SHIFT; - private static final int MSG_SHOW_TRANSIENT = 48 << MSG_SHIFT; - private static final int MSG_ABORT_TRANSIENT = 49 << MSG_SHIFT; - private static final int MSG_TOP_APP_WINDOW_CHANGED = 50 << MSG_SHIFT; + private static final int MSG_ICON = 1 << MSG_SHIFT; + private static final int MSG_DISABLE = 2 << MSG_SHIFT; + private static final int MSG_EXPAND_NOTIFICATIONS = 3 << MSG_SHIFT; + private static final int MSG_COLLAPSE_PANELS = 4 << MSG_SHIFT; + private static final int MSG_EXPAND_SETTINGS = 5 << MSG_SHIFT; + private static final int MSG_SYSTEM_BAR_APPEARANCE_CHANGED = 6 << MSG_SHIFT; + private static final int MSG_DISPLAY_READY = 7 << MSG_SHIFT; + private static final int MSG_SHOW_IME_BUTTON = 8 << MSG_SHIFT; + private static final int MSG_TOGGLE_RECENT_APPS = 9 << MSG_SHIFT; + private static final int MSG_PRELOAD_RECENT_APPS = 10 << MSG_SHIFT; + private static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 11 << MSG_SHIFT; + private static final int MSG_SET_WINDOW_STATE = 12 << MSG_SHIFT; + private static final int MSG_SHOW_RECENT_APPS = 13 << MSG_SHIFT; + private static final int MSG_HIDE_RECENT_APPS = 14 << MSG_SHIFT; + private static final int MSG_SHOW_SCREEN_PIN_REQUEST = 18 << MSG_SHIFT; + private static final int MSG_APP_TRANSITION_PENDING = 19 << MSG_SHIFT; + private static final int MSG_APP_TRANSITION_CANCELLED = 20 << MSG_SHIFT; + private static final int MSG_APP_TRANSITION_STARTING = 21 << MSG_SHIFT; + private static final int MSG_ASSIST_DISCLOSURE = 22 << MSG_SHIFT; + private static final int MSG_START_ASSIST = 23 << MSG_SHIFT; + private static final int MSG_CAMERA_LAUNCH_GESTURE = 24 << MSG_SHIFT; + private static final int MSG_TOGGLE_KEYBOARD_SHORTCUTS = 25 << MSG_SHIFT; + private static final int MSG_SHOW_PICTURE_IN_PICTURE_MENU = 26 << MSG_SHIFT; + private static final int MSG_ADD_QS_TILE = 27 << MSG_SHIFT; + private static final int MSG_REMOVE_QS_TILE = 28 << MSG_SHIFT; + private static final int MSG_CLICK_QS_TILE = 29 << MSG_SHIFT; + private static final int MSG_TOGGLE_APP_SPLIT_SCREEN = 30 << MSG_SHIFT; + private static final int MSG_APP_TRANSITION_FINISHED = 31 << MSG_SHIFT; + private static final int MSG_DISMISS_KEYBOARD_SHORTCUTS = 32 << MSG_SHIFT; + private static final int MSG_HANDLE_SYSTEM_KEY = 33 << MSG_SHIFT; + private static final int MSG_SHOW_GLOBAL_ACTIONS = 34 << MSG_SHIFT; + private static final int MSG_TOGGLE_PANEL = 35 << MSG_SHIFT; + private static final int MSG_SHOW_SHUTDOWN_UI = 36 << MSG_SHIFT; + private static final int MSG_SET_TOP_APP_HIDES_STATUS_BAR = 37 << MSG_SHIFT; + private static final int MSG_ROTATION_PROPOSAL = 38 << MSG_SHIFT; + private static final int MSG_BIOMETRIC_SHOW = 39 << MSG_SHIFT; + private static final int MSG_BIOMETRIC_AUTHENTICATED = 40 << MSG_SHIFT; + private static final int MSG_BIOMETRIC_HELP = 41 << MSG_SHIFT; + private static final int MSG_BIOMETRIC_ERROR = 42 << MSG_SHIFT; + private static final int MSG_BIOMETRIC_HIDE = 43 << MSG_SHIFT; + private static final int MSG_SHOW_CHARGING_ANIMATION = 44 << MSG_SHIFT; + private static final int MSG_SHOW_PINNING_TOAST_ENTER_EXIT = 45 << MSG_SHIFT; + private static final int MSG_SHOW_PINNING_TOAST_ESCAPE = 46 << MSG_SHIFT; + private static final int MSG_RECENTS_ANIMATION_STATE_CHANGED = 47 << MSG_SHIFT; + private static final int MSG_SHOW_TRANSIENT = 48 << MSG_SHIFT; + private static final int MSG_ABORT_TRANSIENT = 49 << MSG_SHIFT; + private static final int MSG_TOP_APP_WINDOW_CHANGED = 50 << MSG_SHIFT; + private static final int MSG_SHOW_INATTENTIVE_SLEEP_WARNING = 51 << MSG_SHIFT; + private static final int MSG_DISMISS_INATTENTIVE_SLEEP_WARNING = 52 << MSG_SHIFT; public static final int FLAG_EXCLUDE_NONE = 0; public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0; @@ -294,6 +296,18 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< */ default void topAppWindowChanged(int displayId, boolean isFullscreen, boolean isImmersive) { } + + /** + * Called to notify System UI that a warning about the device going to sleep + * due to prolonged user inactivity should be shown. + */ + default void showInattentiveSleepWarning() { } + + /** + * Called to notify System UI that the warning about the device going to sleep + * due to prolonged user inactivity should be dismissed. + */ + default void dismissInattentiveSleepWarning() { } } public CommandQueue(Context context) { @@ -793,6 +807,22 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< } } + @Override + public void showInattentiveSleepWarning() { + synchronized (mLock) { + mHandler.obtainMessage(MSG_SHOW_INATTENTIVE_SLEEP_WARNING) + .sendToTarget(); + } + } + + @Override + public void dismissInattentiveSleepWarning() { + synchronized (mLock) { + mHandler.obtainMessage(MSG_DISMISS_INATTENTIVE_SLEEP_WARNING) + .sendToTarget(); + } + } + private void handleShowImeButton(int displayId, IBinder token, int vis, int backDisposition, boolean showImeSwitcher, boolean isMultiClientImeEnabled) { if (displayId == INVALID_DISPLAY) return; @@ -1138,6 +1168,16 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< args.recycle(); break; } + case MSG_SHOW_INATTENTIVE_SLEEP_WARNING: + for (int i = 0; i < mCallbacks.size(); i++) { + mCallbacks.get(i).showInattentiveSleepWarning(); + } + break; + case MSG_DISMISS_INATTENTIVE_SLEEP_WARNING: + for (int i = 0; i < mCallbacks.size(); i++) { + mCallbacks.get(i).dismissInattentiveSleepWarning(); + } + break; } } } diff --git a/services/core/java/com/android/server/power/InattentiveSleepWarningController.java b/services/core/java/com/android/server/power/InattentiveSleepWarningController.java new file mode 100644 index 000000000000..db8a63f44cbc --- /dev/null +++ b/services/core/java/com/android/server/power/InattentiveSleepWarningController.java @@ -0,0 +1,103 @@ +/** + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.power; + +import android.content.Context; +import android.os.Handler; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Log; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.statusbar.IStatusBarService; + +/** + * Communicates with System UI to show/hide the inattentive sleep warning overlay. + */ +@VisibleForTesting +public class InattentiveSleepWarningController { + private static final String TAG = "InattentiveSleepWarning"; + + private final Handler mHandler = new Handler(); + + private boolean mIsShown; + private IStatusBarService mStatusBarService; + + InattentiveSleepWarningController() { + } + + /** + * Returns true if the warning is currently being displayed, false otherwise. + */ + @GuardedBy("PowerManagerService.mLock") + public boolean isShown() { + return mIsShown; + } + + /** + * Show the warning. + */ + @GuardedBy("PowerManagerService.mLock") + public void show() { + if (isShown()) { + return; + } + + mHandler.post(this::showInternal); + mIsShown = true; + } + + private void showInternal() { + try { + getStatusBar().showInattentiveSleepWarning(); + } catch (RemoteException e) { + Log.e(TAG, "Failed to show inattentive sleep warning", e); + mIsShown = false; + } + } + + /** + * Dismiss the warning. + */ + @GuardedBy("PowerManagerService.mLock") + public void dismiss() { + if (!isShown()) { + return; + } + + mHandler.post(this::dismissInternal); + mIsShown = false; + } + + private void dismissInternal() { + try { + getStatusBar().dismissInattentiveSleepWarning(); + } catch (RemoteException e) { + Log.e(TAG, "Failed to dismiss inattentive sleep warning", e); + } + } + + private IStatusBarService getStatusBar() { + if (mStatusBarService == null) { + mStatusBarService = IStatusBarService.Stub.asInterface( + ServiceManager.getService(Context.STATUS_BAR_SERVICE)); + } + + return mStatusBarService; + } +} diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java index b67d9b285acd..7fc9fdc0180d 100644 --- a/services/core/java/com/android/server/power/Notifier.java +++ b/services/core/java/com/android/server/power/Notifier.java @@ -536,6 +536,7 @@ public class Notifier { case PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN: return WindowManagerPolicy.OFF_BECAUSE_OF_ADMIN; case PowerManager.GO_TO_SLEEP_REASON_TIMEOUT: + case PowerManager.GO_TO_SLEEP_REASON_INATTENTIVE: return WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT; default: return WindowManagerPolicy.OFF_BECAUSE_OF_USER; diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index befe4e968b12..00e0f7124714 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -128,6 +128,8 @@ public final class PowerManagerService extends SystemService private static final int MSG_SCREEN_BRIGHTNESS_BOOST_TIMEOUT = 3; // Message: Polling to look for long held wake locks. private static final int MSG_CHECK_FOR_LONG_WAKELOCKS = 4; + // Message: Sent when an attentive timeout occurs to update the power state. + private static final int MSG_ATTENTIVE_TIMEOUT = 5; // Dirty bit: mWakeLocks changed private static final int DIRTY_WAKE_LOCKS = 1 << 0; @@ -157,6 +159,8 @@ public final class PowerManagerService extends SystemService private static final int DIRTY_QUIESCENT = 1 << 12; // Dirty bit: VR Mode enabled changed private static final int DIRTY_VR_MODE_CHANGED = 1 << 13; + // Dirty bit: attentive timer may have timed out + private static final int DIRTY_ATTENTIVE = 1 << 14; // Summarizes the state of all active wakelocks. private static final int WAKE_LOCK_CPU = 1 << 0; @@ -249,6 +253,8 @@ public final class PowerManagerService extends SystemService private DreamManagerInternal mDreamManager; private Light mAttentionLight; + private InattentiveSleepWarningController mInattentiveSleepWarningOverlayController; + private final Object mLock = LockGuard.installNewLock(LockGuard.INDEX_POWER); // A bitfield that indicates what parts of the power state have @@ -381,6 +387,9 @@ public final class PowerManagerService extends SystemService // True if the device should suspend when the screen is off due to proximity. private boolean mSuspendWhenScreenOffDueToProximityConfig; + // Default value for attentive timeout. + private int mAttentiveTimeoutConfig; + // True if dreams are supported on this device. private boolean mDreamsSupportedConfig; @@ -441,9 +450,16 @@ public final class PowerManagerService extends SystemService // The screen off timeout setting value in milliseconds. private long mScreenOffTimeoutSetting; + // Default for attentive warning duration. + private long mAttentiveWarningDurationConfig; + // The sleep timeout setting value in milliseconds. private long mSleepTimeoutSetting; + // How long to show a warning message to user before the device goes to sleep + // after long user inactivity, even if wakelocks are held. + private long mAttentiveTimeoutSetting; + // The maximum allowable screen off timeout according to the device // administration policy. Overrides other settings. private long mMaximumScreenOffTimeoutFromDeviceAdmin = Long.MAX_VALUE; @@ -735,6 +751,10 @@ public final class PowerManagerService extends SystemService AmbientDisplayConfiguration createAmbientDisplayConfiguration(Context context) { return new AmbientDisplayConfiguration(context); } + + InattentiveSleepWarningController createInattentiveSleepWarningController() { + return new InattentiveSleepWarningController(); + } } final Constants mConstants; @@ -779,6 +799,9 @@ public final class PowerManagerService extends SystemService mBatterySaverStateMachine = new BatterySaverStateMachine( mLock, mContext, mBatterySaverController); + mInattentiveSleepWarningOverlayController = + mInjector.createInattentiveSleepWarningController(); + synchronized (mLock) { mWakeLockSuspendBlocker = mInjector.createSuspendBlocker(this, "PowerManagerService.WakeLocks"); @@ -902,6 +925,9 @@ public final class PowerManagerService extends SystemService resolver.registerContentObserver(Settings.Secure.getUriFor( Settings.Secure.SLEEP_TIMEOUT), false, mSettingsObserver, UserHandle.USER_ALL); + resolver.registerContentObserver(Settings.Secure.getUriFor( + Settings.Secure.ATTENTIVE_TIMEOUT), + false, mSettingsObserver, UserHandle.USER_ALL); resolver.registerContentObserver(Settings.Global.getUriFor( Settings.Global.STAY_ON_WHILE_PLUGGED_IN), false, mSettingsObserver, UserHandle.USER_ALL); @@ -966,6 +992,10 @@ public final class PowerManagerService extends SystemService com.android.internal.R.bool.config_allowTheaterModeWakeFromUnplug); mSuspendWhenScreenOffDueToProximityConfig = resources.getBoolean( com.android.internal.R.bool.config_suspendWhenScreenOffDueToProximity); + mAttentiveTimeoutConfig = resources.getInteger( + com.android.internal.R.integer.config_attentiveTimeout); + mAttentiveWarningDurationConfig = resources.getInteger( + com.android.internal.R.integer.config_attentiveWarningDuration); mDreamsSupportedConfig = resources.getBoolean( com.android.internal.R.bool.config_dreamsSupported); mDreamsEnabledByDefaultConfig = resources.getBoolean( @@ -1015,6 +1045,9 @@ public final class PowerManagerService extends SystemService mSleepTimeoutSetting = Settings.Secure.getIntForUser(resolver, Settings.Secure.SLEEP_TIMEOUT, DEFAULT_SLEEP_TIMEOUT, UserHandle.USER_CURRENT); + mAttentiveTimeoutSetting = Settings.Secure.getIntForUser(resolver, + Settings.Secure.ATTENTIVE_TIMEOUT, mAttentiveTimeoutConfig, + UserHandle.USER_CURRENT); mStayOnWhilePluggedInSetting = Settings.Global.getInt(resolver, Settings.Global.STAY_ON_WHILE_PLUGGED_IN, BatteryManager.BATTERY_PLUGGED_AC); mTheaterModeEnabled = Settings.Global.getInt(mContext.getContentResolver(), @@ -1700,6 +1733,7 @@ public final class PowerManagerService extends SystemService updateWakeLockSummaryLocked(dirtyPhase1); updateUserActivitySummaryLocked(now, dirtyPhase1); + updateAttentiveStateLocked(now, dirtyPhase1); if (!updateWakefulnessLocked(dirtyPhase1)) { break; } @@ -2042,8 +2076,10 @@ public final class PowerManagerService extends SystemService if (mWakefulness == WAKEFULNESS_AWAKE || mWakefulness == WAKEFULNESS_DREAMING || mWakefulness == WAKEFULNESS_DOZING) { - final long sleepTimeout = getSleepTimeoutLocked(); - final long screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout); + final long attentiveTimeout = getAttentiveTimeoutLocked(); + final long sleepTimeout = getSleepTimeoutLocked(attentiveTimeout); + final long screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout, + attentiveTimeout); final long screenDimDuration = getScreenDimDurationLocked(screenOffTimeout); final boolean userInactiveOverride = mUserInactiveOverrideFromWindowManager; final long nextProfileTimeout = getNextProfileTimeoutLocked(now); @@ -2134,6 +2170,12 @@ public final class PowerManagerService extends SystemService mHandler.sendMessageAtTime(msg, timeMs); } + private void scheduleAttentiveTimeout(long timeMs) { + final Message msg = mHandler.obtainMessage(MSG_ATTENTIVE_TIMEOUT); + msg.setAsynchronous(true); + mHandler.sendMessageAtTime(msg, timeMs); + } + /** * Finds the next profile timeout time or returns -1 if there are no profiles to be locked. */ @@ -2150,6 +2192,69 @@ public final class PowerManagerService extends SystemService return nextTimeout; } + private void updateAttentiveStateLocked(long now, int dirty) { + long attentiveTimeout = getAttentiveTimeoutLocked(); + long goToSleepTime = mLastUserActivityTime + attentiveTimeout; + long showWarningTime = goToSleepTime - mAttentiveWarningDurationConfig; + + boolean warningDismissed = maybeHideInattentiveSleepWarningLocked(now, showWarningTime); + + if (attentiveTimeout >= 0 && (warningDismissed + || (dirty & (DIRTY_ATTENTIVE | DIRTY_STAY_ON | DIRTY_SCREEN_BRIGHTNESS_BOOST + | DIRTY_PROXIMITY_POSITIVE | DIRTY_WAKEFULNESS | DIRTY_BOOT_COMPLETED + | DIRTY_SETTINGS)) != 0)) { + if (DEBUG_SPEW) { + Slog.d(TAG, "Updating attentive state"); + } + + mHandler.removeMessages(MSG_ATTENTIVE_TIMEOUT); + + if (isBeingKeptFromShowingInattentiveSleepWarningLocked()) { + return; + } + + long nextTimeout = -1; + + if (now < showWarningTime) { + nextTimeout = showWarningTime; + } else if (now < goToSleepTime) { + if (DEBUG) { + long timeToSleep = goToSleepTime - now; + Slog.d(TAG, "Going to sleep in " + timeToSleep + + "ms if there is no user activity"); + } + mInattentiveSleepWarningOverlayController.show(); + nextTimeout = goToSleepTime; + } else { + if (DEBUG && mWakefulness != WAKEFULNESS_ASLEEP) { + Slog.i(TAG, "Going to sleep now due to long user inactivity"); + } + } + + if (nextTimeout >= 0) { + scheduleAttentiveTimeout(nextTimeout); + } + } + } + + private boolean maybeHideInattentiveSleepWarningLocked(long now, long showWarningTime) { + long attentiveTimeout = getAttentiveTimeoutLocked(); + + if (mInattentiveSleepWarningOverlayController.isShown() && (attentiveTimeout < 0 + || isBeingKeptFromShowingInattentiveSleepWarningLocked() + || now < showWarningTime)) { + mInattentiveSleepWarningOverlayController.dismiss(); + return true; + } + + return false; + } + + private boolean isAttentiveTimeoutExpired(long now) { + long attentiveTimeout = getAttentiveTimeoutLocked(); + return attentiveTimeout >= 0 && now > mLastUserActivityTime + attentiveTimeout; + } + /** * Called when a user activity timeout has occurred. * Simply indicates that something about user activity has changed so that the new @@ -2169,15 +2274,38 @@ public final class PowerManagerService extends SystemService } } - private long getSleepTimeoutLocked() { - final long timeout = mSleepTimeoutSetting; + private void handleAttentiveTimeout() { // runs on handler thread + synchronized (mLock) { + if (DEBUG_SPEW) { + Slog.d(TAG, "handleAttentiveTimeout"); + } + + mDirty |= DIRTY_ATTENTIVE; + updatePowerStateLocked(); + } + } + + private long getAttentiveTimeoutLocked() { + long timeout = mAttentiveTimeoutSetting; + if (timeout <= 0) { + return -1; + } + + return Math.max(timeout, mMinimumScreenOffTimeoutConfig); + } + + private long getSleepTimeoutLocked(long attentiveTimeout) { + long timeout = mSleepTimeoutSetting; if (timeout <= 0) { return -1; } + if (attentiveTimeout >= 0) { + timeout = Math.min(timeout, attentiveTimeout); + } return Math.max(timeout, mMinimumScreenOffTimeoutConfig); } - private long getScreenOffTimeoutLocked(long sleepTimeout) { + private long getScreenOffTimeoutLocked(long sleepTimeout, long attentiveTimeout) { long timeout = mScreenOffTimeoutSetting; if (isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked()) { timeout = Math.min(timeout, mMaximumScreenOffTimeoutFromDeviceAdmin); @@ -2188,6 +2316,9 @@ public final class PowerManagerService extends SystemService if (sleepTimeout >= 0) { timeout = Math.min(timeout, sleepTimeout); } + if (attentiveTimeout >= 0) { + timeout = Math.min(timeout, attentiveTimeout); + } return Math.max(timeout, mMinimumScreenOffTimeoutConfig); } @@ -2209,13 +2340,16 @@ public final class PowerManagerService extends SystemService boolean changed = false; if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_BOOT_COMPLETED | DIRTY_WAKEFULNESS | DIRTY_STAY_ON | DIRTY_PROXIMITY_POSITIVE - | DIRTY_DOCK_STATE)) != 0) { + | DIRTY_DOCK_STATE | DIRTY_ATTENTIVE)) != 0) { if (mWakefulness == WAKEFULNESS_AWAKE && isItBedTimeYetLocked()) { if (DEBUG_SPEW) { Slog.d(TAG, "updateWakefulnessLocked: Bed time..."); } final long time = SystemClock.uptimeMillis(); - if (shouldNapAtBedTimeLocked()) { + if (isAttentiveTimeoutExpired(time)) { + changed = goToSleepNoUpdateLocked(time, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, + PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, Process.SYSTEM_UID); + } else if (shouldNapAtBedTimeLocked()) { changed = napNoUpdateLocked(time, Process.SYSTEM_UID); } else { changed = goToSleepNoUpdateLocked(time, @@ -2242,7 +2376,16 @@ public final class PowerManagerService extends SystemService * to being fully awake or else go to sleep for good. */ private boolean isItBedTimeYetLocked() { - return mBootCompleted && !isBeingKeptAwakeLocked(); + if (!mBootCompleted) { + return false; + } + + long now = SystemClock.uptimeMillis(); + if (isAttentiveTimeoutExpired(now)) { + return !isBeingKeptFromInattentiveSleepLocked(); + } else { + return !isBeingKeptAwakeLocked(); + } } /** @@ -2263,11 +2406,29 @@ public final class PowerManagerService extends SystemService } /** + * Returns true if the device is prevented from going into inattentive sleep by the stay on + * while powered setting. We also keep the device awake when the proximity sensor returns a + * positive result so that the device does not lock while in a phone call. This function only + * controls whether the device will go to sleep which is independent of whether it will be + * allowed to suspend. + */ + private boolean isBeingKeptFromInattentiveSleepLocked() { + return mStayOn || mScreenBrightnessBoostInProgress || mProximityPositive + || (mUserActivitySummary & (USER_ACTIVITY_SCREEN_BRIGHT + | USER_ACTIVITY_SCREEN_DIM)) != 0; + } + + private boolean isBeingKeptFromShowingInattentiveSleepWarningLocked() { + return mStayOn || mScreenBrightnessBoostInProgress || mProximityPositive || !mBootCompleted; + } + + /** * Determines whether to post a message to the sandman to update the dream state. */ private void updateDreamLocked(int dirty, boolean displayBecameReady) { if ((dirty & (DIRTY_WAKEFULNESS | DIRTY_USER_ACTIVITY + | DIRTY_ATTENTIVE | DIRTY_WAKE_LOCKS | DIRTY_BOOT_COMPLETED | DIRTY_SETTINGS @@ -2350,6 +2511,7 @@ public final class PowerManagerService extends SystemService } // Determine whether the dream should continue. + long now = SystemClock.uptimeMillis(); if (wakefulness == WAKEFULNESS_DREAMING) { if (isDreaming && canDreamLocked()) { if (mDreamsBatteryLevelDrainCutoffConfig >= 0 @@ -2371,11 +2533,15 @@ public final class PowerManagerService extends SystemService // Dream has ended or will be stopped. Update the power state. if (isItBedTimeYetLocked()) { - goToSleepNoUpdateLocked(SystemClock.uptimeMillis(), - PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID); + int flags = 0; + if (isAttentiveTimeoutExpired(now)) { + flags |= PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE; + } + goToSleepNoUpdateLocked(now, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, flags, + Process.SYSTEM_UID); updatePowerStateLocked(); } else { - wakeUpNoUpdateLocked(SystemClock.uptimeMillis(), + wakeUpNoUpdateLocked(now, PowerManager.WAKE_REASON_UNKNOWN, "android.server.power:DREAM_FINISHED", Process.SYSTEM_UID, mContext.getOpPackageName(), Process.SYSTEM_UID); @@ -2387,7 +2553,7 @@ public final class PowerManagerService extends SystemService } // Doze has ended or will be stopped. Update the power state. - reallyGoToSleepNoUpdateLocked(SystemClock.uptimeMillis(), Process.SYSTEM_UID); + reallyGoToSleepNoUpdateLocked(now, Process.SYSTEM_UID); updatePowerStateLocked(); } } @@ -3474,6 +3640,9 @@ public final class PowerManagerService extends SystemService pw.println(" mMinimumScreenOffTimeoutConfig=" + mMinimumScreenOffTimeoutConfig); pw.println(" mMaximumScreenDimDurationConfig=" + mMaximumScreenDimDurationConfig); pw.println(" mMaximumScreenDimRatioConfig=" + mMaximumScreenDimRatioConfig); + pw.println(" mAttentiveTimeoutConfig=" + mAttentiveTimeoutConfig); + pw.println(" mAttentiveTimeoutSetting=" + mAttentiveTimeoutSetting); + pw.println(" mAttentiveWarningDurationConfig=" + mAttentiveWarningDurationConfig); pw.println(" mScreenOffTimeoutSetting=" + mScreenOffTimeoutSetting); pw.println(" mSleepTimeoutSetting=" + mSleepTimeoutSetting); pw.println(" mMaximumScreenOffTimeoutFromDeviceAdmin=" @@ -3501,10 +3670,12 @@ public final class PowerManagerService extends SystemService pw.println(" mForegroundProfile=" + mForegroundProfile); pw.println(" mUserId=" + mUserId); - final long sleepTimeout = getSleepTimeoutLocked(); - final long screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout); + final long attentiveTimeout = getAttentiveTimeoutLocked(); + final long sleepTimeout = getSleepTimeoutLocked(attentiveTimeout); + final long screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout, attentiveTimeout); final long screenDimDuration = getScreenDimDurationLocked(screenOffTimeout); pw.println(); + pw.println("Attentive timeout: " + attentiveTimeout + " ms"); pw.println("Sleep timeout: " + sleepTimeout + " ms"); pw.println("Screen off timeout: " + screenOffTimeout + " ms"); pw.println("Screen dim duration: " + screenDimDuration + " ms"); @@ -3772,6 +3943,16 @@ public final class PowerManagerService extends SystemService PowerServiceSettingsAndConfigurationDumpProto.SLEEP_TIMEOUT_SETTING_MS, mSleepTimeoutSetting); proto.write( + PowerServiceSettingsAndConfigurationDumpProto.ATTENTIVE_TIMEOUT_SETTING_MS, + mAttentiveTimeoutSetting); + proto.write( + PowerServiceSettingsAndConfigurationDumpProto.ATTENTIVE_TIMEOUT_CONFIG_MS, + mAttentiveTimeoutConfig); + proto.write( + PowerServiceSettingsAndConfigurationDumpProto + .ATTENTIVE_WARNING_DURATION_CONFIG_MS, + mAttentiveWarningDurationConfig); + proto.write( PowerServiceSettingsAndConfigurationDumpProto .MAXIMUM_SCREEN_OFF_TIMEOUT_FROM_DEVICE_ADMIN_MS, // Clamp to int32 @@ -3853,9 +4034,11 @@ public final class PowerManagerService extends SystemService mIsVrModeEnabled); proto.end(settingsAndConfigurationToken); - final long sleepTimeout = getSleepTimeoutLocked(); - final long screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout); + final long attentiveTimeout = getAttentiveTimeoutLocked(); + final long sleepTimeout = getSleepTimeoutLocked(attentiveTimeout); + final long screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout, attentiveTimeout); final long screenDimDuration = getScreenDimDurationLocked(screenOffTimeout); + proto.write(PowerManagerServiceDumpProto.ATTENTIVE_TIMEOUT_MS, attentiveTimeout); proto.write(PowerManagerServiceDumpProto.SLEEP_TIMEOUT_MS, sleepTimeout); proto.write(PowerManagerServiceDumpProto.SCREEN_OFF_TIMEOUT_MS, screenOffTimeout); proto.write(PowerManagerServiceDumpProto.SCREEN_DIM_DURATION_MS, screenDimDuration); @@ -4009,6 +4192,9 @@ public final class PowerManagerService extends SystemService case MSG_CHECK_FOR_LONG_WAKELOCKS: checkForLongWakeLocks(); break; + case MSG_ATTENTIVE_TIMEOUT: + handleAttentiveTimeout(); + break; } } } diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index 25c41f5cdd6b..c256b8499ee8 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -1375,6 +1375,28 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D this, in, out, err, args, callback, resultReceiver); } + @Override + public void showInattentiveSleepWarning() { + enforceStatusBarService(); + if (mBar != null) { + try { + mBar.showInattentiveSleepWarning(); + } catch (RemoteException ex) { + } + } + } + + @Override + public void dismissInattentiveSleepWarning() { + enforceStatusBarService(); + if (mBar != null) { + try { + mBar.dismissInattentiveSleepWarning(); + } catch (RemoteException ex) { + } + } + } + public String[] getStatusBarIcons() { return mContext.getResources().getStringArray(R.array.config_statusBarIcons); } diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java index 88de250e4b0d..592f4ec7caab 100644 --- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java @@ -33,9 +33,11 @@ import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isA; import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -62,11 +64,13 @@ import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; import android.provider.Settings; +import android.test.mock.MockContentResolver; import android.view.Display; import androidx.test.InstrumentationRegistry; import com.android.internal.app.IBatteryStats; +import com.android.internal.util.test.FakeSettingsProvider; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.lights.LightsManager; @@ -109,6 +113,9 @@ public class PowerManagerServiceTest { @Mock private WirelessChargerDetector mWirelessChargerDetectorMock; @Mock private AmbientDisplayConfiguration mAmbientDisplayConfigurationMock; + @Mock + private InattentiveSleepWarningController mInattentiveSleepWarningControllerMock; + private PowerManagerService mService; private PowerSaveState mPowerSaveState; private DisplayPowerRequest mDisplayPowerRequest; @@ -149,6 +156,9 @@ public class PowerManagerServiceTest { when(mBatterySaverPolicyMock.getBatterySaverPolicy( eq(PowerManager.ServiceType.SCREEN_BRIGHTNESS))) .thenReturn(mPowerSaveState); + when(mBatteryManagerInternalMock.isPowered(anyInt())).thenReturn(false); + when(mInattentiveSleepWarningControllerMock.isShown()).thenReturn(false); + when(mDisplayManagerInternalMock.requestPowerState(any(), anyBoolean())).thenReturn(true); mDisplayPowerRequest = new DisplayPowerRequest(); addLocalServiceMock(LightsManager.class, mLightsManagerMock); @@ -161,7 +171,12 @@ public class PowerManagerServiceTest { mResourcesSpy = spy(mContextSpy.getResources()); when(mContextSpy.getResources()).thenReturn(mResourcesSpy); - when(mDisplayManagerInternalMock.requestPowerState(any(), anyBoolean())).thenReturn(true); + MockContentResolver cr = new MockContentResolver(mContextSpy); + cr.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); + when(mContextSpy.getContentResolver()).thenReturn(cr); + + Settings.Global.putInt(mContextSpy.getContentResolver(), + Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0); } private PowerManagerService createService() { @@ -198,6 +213,11 @@ public class PowerManagerServiceTest { AmbientDisplayConfiguration createAmbientDisplayConfiguration(Context context) { return mAmbientDisplayConfigurationMock; } + + @Override + InattentiveSleepWarningController createInattentiveSleepWarningController() { + return mInattentiveSleepWarningControllerMock; + } }); return mService; } @@ -208,8 +228,12 @@ public class PowerManagerServiceTest { LocalServices.removeServiceForTest(DisplayManagerInternal.class); LocalServices.removeServiceForTest(BatteryManagerInternal.class); LocalServices.removeServiceForTest(ActivityManagerInternal.class); + Settings.Global.putInt( mContextSpy.getContentResolver(), Settings.Global.THEATER_MODE_ON, 0); + setAttentiveTimeout(-1); + Settings.Global.putInt(mContextSpy.getContentResolver(), + Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0); } /** @@ -263,12 +287,30 @@ public class PowerManagerServiceTest { private void setPluggedIn(boolean isPluggedIn) { // Set the callback to return the new state - when(mBatteryManagerInternalMock.isPowered(BatteryManager.BATTERY_PLUGGED_ANY)) + when(mBatteryManagerInternalMock.isPowered(anyInt())) .thenReturn(isPluggedIn); // Trigger PowerManager to reread the plug-in state mBatteryReceiver.onReceive(mContextSpy, new Intent(Intent.ACTION_BATTERY_CHANGED)); } + private void setAttentiveTimeout(int attentiveTimeoutMillis) { + Settings.Secure.putInt( + mContextSpy.getContentResolver(), Settings.Secure.ATTENTIVE_TIMEOUT, + attentiveTimeoutMillis); + } + + private void setAttentiveWarningDuration(int attentiveWarningDurationMillis) { + when(mResourcesSpy.getInteger( + com.android.internal.R.integer.config_attentiveWarningDuration)) + .thenReturn(attentiveWarningDurationMillis); + } + + private void setMinimumScreenOffTimeoutConfig(int minimumScreenOffTimeoutConfigMillis) { + when(mResourcesSpy.getInteger( + com.android.internal.R.integer.config_minimumScreenOffTimeout)) + .thenReturn(minimumScreenOffTimeoutConfigMillis); + } + @Test public void testUpdatePowerScreenPolicy_UpdateDisplayPowerRequest() { createService(); @@ -615,4 +657,97 @@ public class PowerManagerServiceTest { .setDozeOverrideFromDreamManager(Display.STATE_ON, PowerManager.BRIGHTNESS_DEFAULT); assertTrue(isAcquired[0]); } + + @Test + public void testInattentiveSleep_hideWarningIfStayOnIsEnabledAndPluggedIn() throws Exception { + setAttentiveTimeout(15000); + Settings.Global.putInt(mContextSpy.getContentResolver(), + Settings.Global.STAY_ON_WHILE_PLUGGED_IN, BatteryManager.BATTERY_PLUGGED_AC); + + createService(); + startSystem(); + + verify(mInattentiveSleepWarningControllerMock, times(1)).show(); + verify(mInattentiveSleepWarningControllerMock, never()).dismiss(); + when(mInattentiveSleepWarningControllerMock.isShown()).thenReturn(true); + + setPluggedIn(true); + verify(mInattentiveSleepWarningControllerMock, atLeastOnce()).dismiss(); + } + + @Test + public void testInattentive_userActivityDismissesWarning() throws Exception { + setMinimumScreenOffTimeoutConfig(5); + setAttentiveWarningDuration(30); + setAttentiveTimeout(100); + + createService(); + startSystem(); + + mService.getBinderServiceInstance().userActivity(SystemClock.uptimeMillis(), + PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0); + verify(mInattentiveSleepWarningControllerMock, never()).show(); + + SystemClock.sleep(70); + verify(mInattentiveSleepWarningControllerMock, times(1)).show(); + verify(mInattentiveSleepWarningControllerMock, never()).dismiss(); + when(mInattentiveSleepWarningControllerMock.isShown()).thenReturn(true); + + mService.getBinderServiceInstance().userActivity(SystemClock.uptimeMillis(), + PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0); + verify(mInattentiveSleepWarningControllerMock, times(1)).dismiss(); + } + + @Test + public void testInattentiveSleep_warningHiddenAfterWakingUp() throws Exception { + setMinimumScreenOffTimeoutConfig(5); + setAttentiveWarningDuration(20); + setAttentiveTimeout(30); + + createService(); + startSystem(); + SystemClock.sleep(10); + verify(mInattentiveSleepWarningControllerMock, atLeastOnce()).show(); + when(mInattentiveSleepWarningControllerMock.isShown()).thenReturn(true); + SystemClock.sleep(30); + forceAwake(); + verify(mInattentiveSleepWarningControllerMock, atLeastOnce()).dismiss(); + } + + @Test + public void testInattentiveSleep_noWarningShownIfInattentiveSleepDisabled() throws Exception { + setAttentiveTimeout(-1); + createService(); + startSystem(); + verify(mInattentiveSleepWarningControllerMock, never()).show(); + } + + @Test + public void testInattentiveSleep_goesToSleepAfterTimeout() throws Exception { + setMinimumScreenOffTimeoutConfig(5); + setAttentiveTimeout(5); + createService(); + startSystem(); + SystemClock.sleep(8); + assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_ASLEEP); + } + + @Test + public void testInattentiveSleep_goesToSleepWithWakeLock() throws Exception { + final String pkg = mContextSpy.getOpPackageName(); + final Binder token = new Binder(); + final String tag = "sleep_testWithWakeLock"; + + setMinimumScreenOffTimeoutConfig(5); + setAttentiveTimeout(10); + createService(); + startSystem(); + + mService.getBinderServiceInstance().acquireWakeLock(token, + PowerManager.SCREEN_BRIGHT_WAKE_LOCK, tag, pkg, + null /* workSource */, null /* historyTag */); + + SystemClock.sleep(11); + assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_ASLEEP); + } } |