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