Add setting to go to sleep after long user inactivity
The European Commision prescribes an auto-standby feature for TV panels:
After some hours of inactivity, the device has to go from on-mode to
standby-mode or off-mode, or another condition not exceeding the
applicable requirements for standby-mode or for off-mode.
After a long time of no user activity the device should go to sleep,
even if wakelocks are held (eg. during video playback).
Test: 1. Set attentive timeout low, to 35s:
`adb shell settings put secure attentive_timeout 35000`
2. Play a YouTube video
3. Observe warning dialog appearing after 5s
4. Verify: Clicking a remote button or changing the setting higher hides
the warning. Remote button press is consumed.
5. Verify: After 35s of not pressing a button the device goes to sleep
6. Verify: If "Stay awake" developer option is enabled, then
warning is not displayed and device does not go to sleep after 35s
7. Verify: No warning or sleep if setting is set to -1
Test: `atest frameworks/base/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java`
Bug: 137633812
Change-Id: I551b6cffc336437fb1c5a00b4102f68ae0e003e9
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index dd1f8c3..f18b4db 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -426,9 +426,15 @@
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 @@
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 381d492..350cb0a 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7677,6 +7677,20 @@
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 73f549a..20706bb 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -188,4 +188,14 @@
* @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 3f08710..76235a4 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -113,4 +113,14 @@
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 091e1c2..c039570 100644
--- a/core/proto/android/server/powermanagerservice.proto
+++ b/core/proto/android/server/powermanagerservice.proto
@@ -172,6 +172,8 @@
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 @@
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 3ecb1dd..655d4dd 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 a810851..e425ef0 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 5f2bbac..f8eb90b 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 8d57a43c..a2df060 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 10d990a..289ac80 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -605,6 +605,7 @@
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 0000000..eb21c43
--- /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 0000000..f1f9b1f
--- /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 dff21cf..c3f410e 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 0082949..e06380e 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2501,4 +2501,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 3cf14d6..25986c5 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.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 @@
@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 0000000..7169431
--- /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 0000000..8ccc679
--- /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 621f101..88b6fdd 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 @@
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 @@
*/
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 @@
}
}
+ @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 @@
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 0000000..db8a63f
--- /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 b67d9b2..7fc9fdc0 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 @@
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 befe4e9..00e0f71 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 @@
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 @@
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 @@
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 @@
// 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 @@
// 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 @@
AmbientDisplayConfiguration createAmbientDisplayConfiguration(Context context) {
return new AmbientDisplayConfiguration(context);
}
+
+ InattentiveSleepWarningController createInattentiveSleepWarningController() {
+ return new InattentiveSleepWarningController();
+ }
}
final Constants mConstants;
@@ -779,6 +799,9 @@
mBatterySaverStateMachine = new BatterySaverStateMachine(
mLock, mContext, mBatterySaverController);
+ mInattentiveSleepWarningOverlayController =
+ mInjector.createInattentiveSleepWarningController();
+
synchronized (mLock) {
mWakeLockSuspendBlocker =
mInjector.createSuspendBlocker(this, "PowerManagerService.WakeLocks");
@@ -902,6 +925,9 @@
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 @@
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 @@
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 @@
updateWakeLockSummaryLocked(dirtyPhase1);
updateUserActivitySummaryLocked(now, dirtyPhase1);
+ updateAttentiveStateLocked(now, dirtyPhase1);
if (!updateWakefulnessLocked(dirtyPhase1)) {
break;
}
@@ -2042,8 +2076,10 @@
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 @@
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 @@
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 @@
}
}
- 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 @@
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 @@
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 @@
* 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 @@
}
/**
+ * 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 @@
}
// Determine whether the dream should continue.
+ long now = SystemClock.uptimeMillis();
if (wakefulness == WAKEFULNESS_DREAMING) {
if (isDreaming && canDreamLocked()) {
if (mDreamsBatteryLevelDrainCutoffConfig >= 0
@@ -2371,11 +2533,15 @@
// 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 @@
}
// 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 @@
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 @@
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 @@
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 @@
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 @@
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 25c41f5..c256b84 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 @@
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 88de250..592f4ec 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.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.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 @@
@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 @@
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 @@
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 @@
AmbientDisplayConfiguration createAmbientDisplayConfiguration(Context context) {
return mAmbientDisplayConfigurationMock;
}
+
+ @Override
+ InattentiveSleepWarningController createInattentiveSleepWarningController() {
+ return mInattentiveSleepWarningControllerMock;
+ }
});
return mService;
}
@@ -208,8 +228,12 @@
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 @@
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 @@
.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);
+ }
}