blob: 8740256af04dd121c5e5970a5873c7734158402e [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;
22import android.os.PowerManager;
23import android.os.PowerManagerInternal;
24import android.os.SystemClock;
25import android.service.attention.AttentionService;
26import android.util.Slog;
Alex Salod7c3eef2019-01-25 14:43:27 -080027import android.util.StatsLog;
Lucas Dupin0a5d7972019-01-16 18:52:30 -080028
29import com.android.internal.annotations.VisibleForTesting;
30import com.android.server.LocalServices;
31
32import java.io.PrintWriter;
Alex Salod7c3eef2019-01-25 14:43:27 -080033import java.util.concurrent.atomic.AtomicLong;
Lucas Dupin0a5d7972019-01-16 18:52:30 -080034
35/**
36 * Class responsible for checking if the user is currently paying attention to the phone and
37 * notifying {@link PowerManagerService} that user activity should be renewed.
38 *
39 * This class also implements a limit of how long the extension should be, to avoid security
40 * issues where the device would never be locked.
41 */
42public class AttentionDetector {
43
44 private static final String TAG = "AttentionDetector";
45 private static final boolean DEBUG = false;
46
47 /**
48 * Invoked whenever user attention is detected.
49 */
50 private final Runnable mOnUserAttention;
51
52 /**
53 * The maximum time, in millis, that the phone can stay unlocked because of attention events,
54 * triggered by any user.
55 */
56 @VisibleForTesting
57 protected long mMaximumExtensionMillis;
58
59 private final Object mLock;
60
61 /**
62 * {@link android.service.attention.AttentionService} API timeout.
63 */
64 private long mMaxAttentionApiTimeoutMillis;
65
66 /**
67 * Last known user activity.
68 */
69 private long mLastUserActivityTime;
70
71 @VisibleForTesting
72 protected AttentionManagerInternal mAttentionManager;
73
74 /**
75 * If we're currently waiting for an attention callback
76 */
77 private boolean mRequested;
78
79 /**
80 * Current wakefulness of the device. {@see PowerManagerInternal}
81 */
82 private int mWakefulness;
83
Alex Salod7c3eef2019-01-25 14:43:27 -080084 /**
85 * Describes how many times in a row was the timeout extended.
86 */
87 private AtomicLong mConsecutiveTimeoutExtendedCount = new AtomicLong(0);
88
Lucas Dupin0a5d7972019-01-16 18:52:30 -080089 @VisibleForTesting
90 final AttentionCallbackInternal mCallback = new AttentionCallbackInternal() {
91
92 @Override
93 public void onSuccess(int requestCode, int result, long timestamp) {
94 Slog.v(TAG, "onSuccess: " + requestCode + ", " + result
95 + " - current requestCode: " + getRequestCode());
96 synchronized (mLock) {
97 if (requestCode == getRequestCode() && mRequested) {
98 mRequested = false;
99 if (mWakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE) {
100 if (DEBUG) Slog.d(TAG, "Device slept before receiving callback.");
101 return;
102 }
103 if (result == AttentionService.ATTENTION_SUCCESS_PRESENT) {
104 mOnUserAttention.run();
Alex Salod7c3eef2019-01-25 14:43:27 -0800105 } else {
106 resetConsecutiveExtensionCount();
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800107 }
108 }
109 }
110 }
111
112 @Override
113 public void onFailure(int requestCode, int error) {
114 Slog.i(TAG, "Failed to check attention: " + error);
115 synchronized (mLock) {
116 if (requestCode == getRequestCode()) {
117 mRequested = false;
118 }
119 }
120 }
121 };
122
123 public AttentionDetector(Runnable onUserAttention, Object lock) {
124 mOnUserAttention = onUserAttention;
125 mLock = lock;
Alex Salo4d9e4312019-02-13 14:59:42 -0800126
127 // Device starts with an awake state upon boot.
128 mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800129 }
130
131 public void systemReady(Context context) {
132 mAttentionManager = LocalServices.getService(AttentionManagerInternal.class);
133 mMaximumExtensionMillis = context.getResources().getInteger(
134 com.android.internal.R.integer.config_attentionMaximumExtension);
135 mMaxAttentionApiTimeoutMillis = context.getResources().getInteger(
136 com.android.internal.R.integer.config_attentionApiTimeout);
137 }
138
139 public long updateUserActivity(long nextScreenDimming) {
140 if (!isAttentionServiceSupported()) {
141 return nextScreenDimming;
142 }
143
144 final long now = SystemClock.uptimeMillis();
145 final long whenToCheck = nextScreenDimming - getAttentionTimeout();
146 final long whenToStopExtending = mLastUserActivityTime + mMaximumExtensionMillis;
147 if (now < whenToCheck) {
148 if (DEBUG) {
149 Slog.d(TAG, "Do not check for attention yet, wait " + (whenToCheck - now));
150 }
Alex Salo1761fce2019-02-13 15:54:50 -0800151 return whenToCheck;
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800152 } else if (whenToStopExtending < whenToCheck) {
153 if (DEBUG) {
154 Slog.d(TAG, "Let device sleep to avoid false results and improve security "
155 + (whenToCheck - whenToStopExtending));
156 }
157 return nextScreenDimming;
158 } else if (mRequested) {
159 if (DEBUG) {
160 Slog.d(TAG, "Pending attention callback, wait. " + getRequestCode());
161 }
162 return whenToCheck;
163 }
164
165 // Ideally we should attribute mRequested to the result of #checkAttention, but the
166 // callback might arrive before #checkAttention returns (if there are cached results.)
167 // This means that we must assume that the request was successful, and then cancel it
168 // afterwards if AttentionManager couldn't deliver it.
169 mRequested = true;
170 final boolean sent = mAttentionManager.checkAttention(getRequestCode(),
171 getAttentionTimeout(), mCallback);
172 if (!sent) {
173 mRequested = false;
174 }
175
176 Slog.v(TAG, "Checking user attention with request code: " + getRequestCode());
177 return whenToCheck;
178 }
179
180 /**
181 * Handles user activity by cancelling any pending attention requests and keeping track of when
182 * the activity happened.
183 *
184 * @param eventTime Activity time, in uptime millis.
185 * @param event Activity type as defined in {@link PowerManager}.
186 * @return 0 when activity was ignored, 1 when handled, -1 when invalid.
187 */
188 public int onUserActivity(long eventTime, int event) {
189 switch (event) {
190 case PowerManager.USER_ACTIVITY_EVENT_ATTENTION:
Alex Salod7c3eef2019-01-25 14:43:27 -0800191 mConsecutiveTimeoutExtendedCount.incrementAndGet();
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800192 return 0;
193 case PowerManager.USER_ACTIVITY_EVENT_OTHER:
194 case PowerManager.USER_ACTIVITY_EVENT_BUTTON:
195 case PowerManager.USER_ACTIVITY_EVENT_TOUCH:
196 case PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY:
197 cancelCurrentRequestIfAny();
198 mLastUserActivityTime = eventTime;
Alex Salod7c3eef2019-01-25 14:43:27 -0800199 resetConsecutiveExtensionCount();
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800200 return 1;
201 default:
202 if (DEBUG) {
203 Slog.d(TAG, "Attention not reset. Unknown activity event: " + event);
204 }
205 return -1;
206 }
207 }
208
209 public void onWakefulnessChangeStarted(int wakefulness) {
210 mWakefulness = wakefulness;
211 if (wakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE) {
212 cancelCurrentRequestIfAny();
Alex Salod7c3eef2019-01-25 14:43:27 -0800213 resetConsecutiveExtensionCount();
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800214 }
215 }
216
217 private void cancelCurrentRequestIfAny() {
218 if (mRequested) {
219 mAttentionManager.cancelAttentionCheck(getRequestCode());
220 mRequested = false;
221 }
222 }
223
Alex Salod7c3eef2019-01-25 14:43:27 -0800224 private void resetConsecutiveExtensionCount() {
225 final long previousCount = mConsecutiveTimeoutExtendedCount.getAndSet(0);
226 if (previousCount > 0) {
227 StatsLog.write(StatsLog.SCREEN_TIMEOUT_EXTENSION_REPORTED, previousCount);
228 }
229 }
230
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800231 @VisibleForTesting
232 int getRequestCode() {
233 return (int) (mLastUserActivityTime % Integer.MAX_VALUE);
234 }
235
236 @VisibleForTesting
237 long getAttentionTimeout() {
238 return mMaxAttentionApiTimeoutMillis;
239 }
240
241 /**
242 * {@see AttentionManagerInternal#isAttentionServiceSupported}
243 */
244 @VisibleForTesting
245 boolean isAttentionServiceSupported() {
246 return mAttentionManager.isAttentionServiceSupported();
247 }
248
249 public void dump(PrintWriter pw) {
250 pw.print("AttentionDetector:");
251 pw.print(" mMaximumExtensionMillis=" + mMaximumExtensionMillis);
252 pw.print(" mMaxAttentionApiTimeoutMillis=" + mMaxAttentionApiTimeoutMillis);
253 pw.print(" mLastUserActivityTime(excludingAttention)=" + mLastUserActivityTime);
254 pw.print(" mAttentionServiceSupported=" + isAttentionServiceSupported());
255 pw.print(" mRequested=" + mRequested);
256 }
257}