blob: 406cbc10a8aa3920cd0ec0b8b1e6d389104ab4eb [file] [log] [blame]
Lucas Dupin0a5d7972019-01-16 18:52:30 -08001/*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.power;
18
19import android.attention.AttentionManagerInternal;
20import android.attention.AttentionManagerInternal.AttentionCallbackInternal;
21import android.content.Context;
Alex Salo9af68f12019-02-15 13:50:11 -080022import android.database.ContentObserver;
23import android.os.Handler;
Lucas Dupin0a5d7972019-01-16 18:52:30 -080024import android.os.PowerManager;
25import android.os.PowerManagerInternal;
26import android.os.SystemClock;
Alex Salo9af68f12019-02-15 13:50:11 -080027import android.os.UserHandle;
28import android.provider.Settings;
Lucas Dupin0a5d7972019-01-16 18:52:30 -080029import android.service.attention.AttentionService;
30import android.util.Slog;
Alex Salod7c3eef2019-01-25 14:43:27 -080031import android.util.StatsLog;
Lucas Dupin0a5d7972019-01-16 18:52:30 -080032
33import com.android.internal.annotations.VisibleForTesting;
34import com.android.server.LocalServices;
35
36import java.io.PrintWriter;
Alex Salod7c3eef2019-01-25 14:43:27 -080037import java.util.concurrent.atomic.AtomicLong;
Lucas Dupin0a5d7972019-01-16 18:52:30 -080038
39/**
40 * Class responsible for checking if the user is currently paying attention to the phone and
41 * notifying {@link PowerManagerService} that user activity should be renewed.
42 *
43 * This class also implements a limit of how long the extension should be, to avoid security
44 * issues where the device would never be locked.
45 */
46public class AttentionDetector {
47
48 private static final String TAG = "AttentionDetector";
49 private static final boolean DEBUG = false;
50
Alex Salo9af68f12019-02-15 13:50:11 -080051 private boolean mIsSettingEnabled;
52
Lucas Dupin0a5d7972019-01-16 18:52:30 -080053 /**
54 * Invoked whenever user attention is detected.
55 */
56 private final Runnable mOnUserAttention;
57
58 /**
59 * The maximum time, in millis, that the phone can stay unlocked because of attention events,
60 * triggered by any user.
61 */
62 @VisibleForTesting
63 protected long mMaximumExtensionMillis;
64
65 private final Object mLock;
66
67 /**
68 * {@link android.service.attention.AttentionService} API timeout.
69 */
70 private long mMaxAttentionApiTimeoutMillis;
71
72 /**
73 * Last known user activity.
74 */
75 private long mLastUserActivityTime;
76
77 @VisibleForTesting
78 protected AttentionManagerInternal mAttentionManager;
79
80 /**
81 * If we're currently waiting for an attention callback
82 */
83 private boolean mRequested;
84
85 /**
86 * Current wakefulness of the device. {@see PowerManagerInternal}
87 */
88 private int mWakefulness;
89
Alex Salod7c3eef2019-01-25 14:43:27 -080090 /**
91 * Describes how many times in a row was the timeout extended.
92 */
93 private AtomicLong mConsecutiveTimeoutExtendedCount = new AtomicLong(0);
94
Lucas Dupin0a5d7972019-01-16 18:52:30 -080095 @VisibleForTesting
96 final AttentionCallbackInternal mCallback = new AttentionCallbackInternal() {
97
98 @Override
99 public void onSuccess(int requestCode, int result, long timestamp) {
100 Slog.v(TAG, "onSuccess: " + requestCode + ", " + result
101 + " - current requestCode: " + getRequestCode());
102 synchronized (mLock) {
103 if (requestCode == getRequestCode() && mRequested) {
104 mRequested = false;
105 if (mWakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE) {
106 if (DEBUG) Slog.d(TAG, "Device slept before receiving callback.");
107 return;
108 }
109 if (result == AttentionService.ATTENTION_SUCCESS_PRESENT) {
110 mOnUserAttention.run();
Alex Salod7c3eef2019-01-25 14:43:27 -0800111 } else {
112 resetConsecutiveExtensionCount();
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800113 }
114 }
115 }
116 }
117
118 @Override
119 public void onFailure(int requestCode, int error) {
120 Slog.i(TAG, "Failed to check attention: " + error);
121 synchronized (mLock) {
122 if (requestCode == getRequestCode()) {
123 mRequested = false;
124 }
125 }
126 }
127 };
128
129 public AttentionDetector(Runnable onUserAttention, Object lock) {
130 mOnUserAttention = onUserAttention;
131 mLock = lock;
Alex Salo4d9e4312019-02-13 14:59:42 -0800132
133 // Device starts with an awake state upon boot.
134 mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800135 }
136
Alex Salo9af68f12019-02-15 13:50:11 -0800137 @VisibleForTesting
138 void updateEnabledFromSettings(Context context) {
139 mIsSettingEnabled = Settings.System.getIntForUser(context.getContentResolver(),
140 Settings.System.ADAPTIVE_SLEEP, 0, UserHandle.USER_CURRENT) == 1;
141 }
142
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800143 public void systemReady(Context context) {
Alex Salo9af68f12019-02-15 13:50:11 -0800144 updateEnabledFromSettings(context);
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800145 mAttentionManager = LocalServices.getService(AttentionManagerInternal.class);
146 mMaximumExtensionMillis = context.getResources().getInteger(
147 com.android.internal.R.integer.config_attentionMaximumExtension);
148 mMaxAttentionApiTimeoutMillis = context.getResources().getInteger(
149 com.android.internal.R.integer.config_attentionApiTimeout);
Alex Salo9af68f12019-02-15 13:50:11 -0800150
151 context.getContentResolver().registerContentObserver(Settings.System.getUriFor(
152 Settings.System.ADAPTIVE_SLEEP),
153 false, new ContentObserver(new Handler()) {
154 @Override
155 public void onChange(boolean selfChange) {
156 updateEnabledFromSettings(context);
157 }
158 }, UserHandle.USER_ALL);
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800159 }
160
161 public long updateUserActivity(long nextScreenDimming) {
Alex Salo9af68f12019-02-15 13:50:11 -0800162 if (!mIsSettingEnabled) {
163 return nextScreenDimming;
164 }
165
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800166 if (!isAttentionServiceSupported()) {
167 return nextScreenDimming;
168 }
169
170 final long now = SystemClock.uptimeMillis();
171 final long whenToCheck = nextScreenDimming - getAttentionTimeout();
172 final long whenToStopExtending = mLastUserActivityTime + mMaximumExtensionMillis;
173 if (now < whenToCheck) {
174 if (DEBUG) {
175 Slog.d(TAG, "Do not check for attention yet, wait " + (whenToCheck - now));
176 }
Alex Salo1761fce2019-02-13 15:54:50 -0800177 return whenToCheck;
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800178 } else if (whenToStopExtending < whenToCheck) {
179 if (DEBUG) {
180 Slog.d(TAG, "Let device sleep to avoid false results and improve security "
181 + (whenToCheck - whenToStopExtending));
182 }
183 return nextScreenDimming;
184 } else if (mRequested) {
185 if (DEBUG) {
186 Slog.d(TAG, "Pending attention callback, wait. " + getRequestCode());
187 }
188 return whenToCheck;
189 }
190
191 // Ideally we should attribute mRequested to the result of #checkAttention, but the
192 // callback might arrive before #checkAttention returns (if there are cached results.)
193 // This means that we must assume that the request was successful, and then cancel it
194 // afterwards if AttentionManager couldn't deliver it.
195 mRequested = true;
196 final boolean sent = mAttentionManager.checkAttention(getRequestCode(),
197 getAttentionTimeout(), mCallback);
198 if (!sent) {
199 mRequested = false;
200 }
201
202 Slog.v(TAG, "Checking user attention with request code: " + getRequestCode());
203 return whenToCheck;
204 }
205
206 /**
207 * Handles user activity by cancelling any pending attention requests and keeping track of when
208 * the activity happened.
209 *
210 * @param eventTime Activity time, in uptime millis.
Alex Salo9af68f12019-02-15 13:50:11 -0800211 * @param event Activity type as defined in {@link PowerManager}.
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800212 * @return 0 when activity was ignored, 1 when handled, -1 when invalid.
213 */
214 public int onUserActivity(long eventTime, int event) {
215 switch (event) {
216 case PowerManager.USER_ACTIVITY_EVENT_ATTENTION:
Alex Salod7c3eef2019-01-25 14:43:27 -0800217 mConsecutiveTimeoutExtendedCount.incrementAndGet();
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800218 return 0;
219 case PowerManager.USER_ACTIVITY_EVENT_OTHER:
220 case PowerManager.USER_ACTIVITY_EVENT_BUTTON:
221 case PowerManager.USER_ACTIVITY_EVENT_TOUCH:
222 case PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY:
223 cancelCurrentRequestIfAny();
224 mLastUserActivityTime = eventTime;
Alex Salod7c3eef2019-01-25 14:43:27 -0800225 resetConsecutiveExtensionCount();
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800226 return 1;
227 default:
228 if (DEBUG) {
229 Slog.d(TAG, "Attention not reset. Unknown activity event: " + event);
230 }
231 return -1;
232 }
233 }
234
235 public void onWakefulnessChangeStarted(int wakefulness) {
236 mWakefulness = wakefulness;
237 if (wakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE) {
238 cancelCurrentRequestIfAny();
Alex Salod7c3eef2019-01-25 14:43:27 -0800239 resetConsecutiveExtensionCount();
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800240 }
241 }
242
243 private void cancelCurrentRequestIfAny() {
244 if (mRequested) {
245 mAttentionManager.cancelAttentionCheck(getRequestCode());
246 mRequested = false;
247 }
248 }
249
Alex Salod7c3eef2019-01-25 14:43:27 -0800250 private void resetConsecutiveExtensionCount() {
251 final long previousCount = mConsecutiveTimeoutExtendedCount.getAndSet(0);
252 if (previousCount > 0) {
253 StatsLog.write(StatsLog.SCREEN_TIMEOUT_EXTENSION_REPORTED, previousCount);
254 }
255 }
256
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800257 @VisibleForTesting
258 int getRequestCode() {
259 return (int) (mLastUserActivityTime % Integer.MAX_VALUE);
260 }
261
262 @VisibleForTesting
263 long getAttentionTimeout() {
264 return mMaxAttentionApiTimeoutMillis;
265 }
266
267 /**
268 * {@see AttentionManagerInternal#isAttentionServiceSupported}
269 */
270 @VisibleForTesting
271 boolean isAttentionServiceSupported() {
272 return mAttentionManager.isAttentionServiceSupported();
273 }
274
275 public void dump(PrintWriter pw) {
276 pw.print("AttentionDetector:");
277 pw.print(" mMaximumExtensionMillis=" + mMaximumExtensionMillis);
278 pw.print(" mMaxAttentionApiTimeoutMillis=" + mMaxAttentionApiTimeoutMillis);
279 pw.print(" mLastUserActivityTime(excludingAttention)=" + mLastUserActivityTime);
280 pw.print(" mAttentionServiceSupported=" + isAttentionServiceSupported());
281 pw.print(" mRequested=" + mRequested);
282 }
283}