blob: ed11fd45ec39b3502932ed8f9329c71301603c5c [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
Alex Salo2b7dbe82019-04-11 14:45:42 -070019import static android.provider.Settings.System.ADAPTIVE_SLEEP;
20
21import android.Manifest;
Yi Jiangf8291bb2019-05-24 16:32:10 -070022import android.app.ActivityManager;
23import android.app.SynchronousUserSwitchObserver;
Lucas Dupin0a5d7972019-01-16 18:52:30 -080024import android.attention.AttentionManagerInternal;
25import android.attention.AttentionManagerInternal.AttentionCallbackInternal;
Alex Salo2b7dbe82019-04-11 14:45:42 -070026import android.content.ContentResolver;
Lucas Dupin0a5d7972019-01-16 18:52:30 -080027import android.content.Context;
Alex Salo2b7dbe82019-04-11 14:45:42 -070028import android.content.pm.PackageManager;
Alex Salo9af68f12019-02-15 13:50:11 -080029import android.database.ContentObserver;
30import android.os.Handler;
Lucas Dupin0a5d7972019-01-16 18:52:30 -080031import android.os.PowerManager;
32import android.os.PowerManagerInternal;
Yi Jiangf8291bb2019-05-24 16:32:10 -070033import android.os.RemoteException;
Lucas Dupin0a5d7972019-01-16 18:52:30 -080034import android.os.SystemClock;
Alex Salo9af68f12019-02-15 13:50:11 -080035import android.os.UserHandle;
36import android.provider.Settings;
Lucas Dupin0a5d7972019-01-16 18:52:30 -080037import android.service.attention.AttentionService;
38import android.util.Slog;
Alex Salod7c3eef2019-01-25 14:43:27 -080039import android.util.StatsLog;
Lucas Dupin0a5d7972019-01-16 18:52:30 -080040
41import com.android.internal.annotations.VisibleForTesting;
42import com.android.server.LocalServices;
43
44import java.io.PrintWriter;
Alex Saloeeba0232019-03-14 18:56:54 -070045import java.util.concurrent.atomic.AtomicBoolean;
Alex Salod7c3eef2019-01-25 14:43:27 -080046import java.util.concurrent.atomic.AtomicLong;
Lucas Dupin0a5d7972019-01-16 18:52:30 -080047
48/**
49 * Class responsible for checking if the user is currently paying attention to the phone and
50 * notifying {@link PowerManagerService} that user activity should be renewed.
51 *
52 * This class also implements a limit of how long the extension should be, to avoid security
53 * issues where the device would never be locked.
54 */
55public class AttentionDetector {
56
57 private static final String TAG = "AttentionDetector";
58 private static final boolean DEBUG = false;
59
Yi Jiangf8291bb2019-05-24 16:32:10 -070060 private Context mContext;
61
Alex Salo9af68f12019-02-15 13:50:11 -080062 private boolean mIsSettingEnabled;
63
Lucas Dupin0a5d7972019-01-16 18:52:30 -080064 /**
65 * Invoked whenever user attention is detected.
66 */
67 private final Runnable mOnUserAttention;
68
69 /**
70 * The maximum time, in millis, that the phone can stay unlocked because of attention events,
71 * triggered by any user.
72 */
73 @VisibleForTesting
74 protected long mMaximumExtensionMillis;
75
76 private final Object mLock;
77
78 /**
Alex Saloeeba0232019-03-14 18:56:54 -070079 * If we're currently waiting for an attention callback
80 */
81 private final AtomicBoolean mRequested;
82
Alex Salocbe610a2019-05-01 14:18:46 -070083 private long mLastActedOnNextScreenDimming;
84
Alex Saloeeba0232019-03-14 18:56:54 -070085 /**
Alex Salo69ff3212019-04-06 16:24:00 -070086 * Monotonously increasing ID for the requests sent.
87 */
88 @VisibleForTesting
89 protected int mRequestId;
90
91 /**
Lucas Dupin0a5d7972019-01-16 18:52:30 -080092 * {@link android.service.attention.AttentionService} API timeout.
93 */
94 private long mMaxAttentionApiTimeoutMillis;
95
96 /**
97 * Last known user activity.
98 */
99 private long mLastUserActivityTime;
100
101 @VisibleForTesting
102 protected AttentionManagerInternal mAttentionManager;
103
Alex Salo2b7dbe82019-04-11 14:45:42 -0700104 @VisibleForTesting
105 protected PackageManager mPackageManager;
106
107 @VisibleForTesting
108 protected ContentResolver mContentResolver;
109
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800110 /**
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800111 * Current wakefulness of the device. {@see PowerManagerInternal}
112 */
113 private int mWakefulness;
114
Alex Salod7c3eef2019-01-25 14:43:27 -0800115 /**
116 * Describes how many times in a row was the timeout extended.
117 */
118 private AtomicLong mConsecutiveTimeoutExtendedCount = new AtomicLong(0);
119
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800120 @VisibleForTesting
Alex Salo69ff3212019-04-06 16:24:00 -0700121 AttentionCallbackInternalImpl mCallback;
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800122
123 public AttentionDetector(Runnable onUserAttention, Object lock) {
124 mOnUserAttention = onUserAttention;
125 mLock = lock;
Alex Saloeeba0232019-03-14 18:56:54 -0700126 mRequested = new AtomicBoolean(false);
Alex Salo69ff3212019-04-06 16:24:00 -0700127 mRequestId = 0;
Alex Salo4d9e4312019-02-13 14:59:42 -0800128
129 // Device starts with an awake state upon boot.
130 mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800131 }
132
Alex Salo9af68f12019-02-15 13:50:11 -0800133 @VisibleForTesting
134 void updateEnabledFromSettings(Context context) {
135 mIsSettingEnabled = Settings.System.getIntForUser(context.getContentResolver(),
136 Settings.System.ADAPTIVE_SLEEP, 0, UserHandle.USER_CURRENT) == 1;
137 }
138
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800139 public void systemReady(Context context) {
Yi Jiangf8291bb2019-05-24 16:32:10 -0700140 mContext = context;
Alex Salo9af68f12019-02-15 13:50:11 -0800141 updateEnabledFromSettings(context);
Alex Salo2b7dbe82019-04-11 14:45:42 -0700142 mPackageManager = context.getPackageManager();
143 mContentResolver = context.getContentResolver();
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800144 mAttentionManager = LocalServices.getService(AttentionManagerInternal.class);
145 mMaximumExtensionMillis = context.getResources().getInteger(
146 com.android.internal.R.integer.config_attentionMaximumExtension);
147 mMaxAttentionApiTimeoutMillis = context.getResources().getInteger(
148 com.android.internal.R.integer.config_attentionApiTimeout);
Alex Salo9af68f12019-02-15 13:50:11 -0800149
Yi Jiangf8291bb2019-05-24 16:32:10 -0700150 try {
151 final UserSwitchObserver observer = new UserSwitchObserver();
152 ActivityManager.getService().registerUserSwitchObserver(observer, TAG);
153 } catch (RemoteException e) {
154 // Shouldn't happen since in-process.
155 }
156
Alex Salo9af68f12019-02-15 13:50:11 -0800157 context.getContentResolver().registerContentObserver(Settings.System.getUriFor(
158 Settings.System.ADAPTIVE_SLEEP),
159 false, new ContentObserver(new Handler()) {
160 @Override
161 public void onChange(boolean selfChange) {
162 updateEnabledFromSettings(context);
163 }
164 }, UserHandle.USER_ALL);
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800165 }
166
167 public long updateUserActivity(long nextScreenDimming) {
Alex Salocbe610a2019-05-01 14:18:46 -0700168 if (nextScreenDimming == mLastActedOnNextScreenDimming) {
169 return nextScreenDimming;
170 }
Alex Salo9af68f12019-02-15 13:50:11 -0800171 if (!mIsSettingEnabled) {
172 return nextScreenDimming;
173 }
174
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800175 if (!isAttentionServiceSupported()) {
176 return nextScreenDimming;
177 }
178
Alex Salo2b7dbe82019-04-11 14:45:42 -0700179 if (!serviceHasSufficientPermissions()) {
180 Settings.System.putInt(mContentResolver, ADAPTIVE_SLEEP, 0);
181 return nextScreenDimming;
182 }
183
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800184 final long now = SystemClock.uptimeMillis();
185 final long whenToCheck = nextScreenDimming - getAttentionTimeout();
186 final long whenToStopExtending = mLastUserActivityTime + mMaximumExtensionMillis;
187 if (now < whenToCheck) {
188 if (DEBUG) {
189 Slog.d(TAG, "Do not check for attention yet, wait " + (whenToCheck - now));
190 }
Alex Salo1761fce2019-02-13 15:54:50 -0800191 return whenToCheck;
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800192 } else if (whenToStopExtending < whenToCheck) {
193 if (DEBUG) {
194 Slog.d(TAG, "Let device sleep to avoid false results and improve security "
195 + (whenToCheck - whenToStopExtending));
196 }
197 return nextScreenDimming;
Alex Saloeeba0232019-03-14 18:56:54 -0700198 } else if (mRequested.get()) {
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800199 if (DEBUG) {
Alex Salo69ff3212019-04-06 16:24:00 -0700200 Slog.d(TAG, "Pending attention callback with ID=" + mCallback.mId + ", wait.");
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800201 }
202 return whenToCheck;
203 }
204
205 // Ideally we should attribute mRequested to the result of #checkAttention, but the
206 // callback might arrive before #checkAttention returns (if there are cached results.)
207 // This means that we must assume that the request was successful, and then cancel it
208 // afterwards if AttentionManager couldn't deliver it.
Alex Saloeeba0232019-03-14 18:56:54 -0700209 mRequested.set(true);
Alex Salo69ff3212019-04-06 16:24:00 -0700210 mRequestId++;
Alex Salocbe610a2019-05-01 14:18:46 -0700211 mLastActedOnNextScreenDimming = nextScreenDimming;
Alex Salo69ff3212019-04-06 16:24:00 -0700212 mCallback = new AttentionCallbackInternalImpl(mRequestId);
Alex Salocbe610a2019-05-01 14:18:46 -0700213 Slog.v(TAG, "Checking user attention, ID: " + mRequestId);
Alex Saloeeba0232019-03-14 18:56:54 -0700214 final boolean sent = mAttentionManager.checkAttention(getAttentionTimeout(), mCallback);
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800215 if (!sent) {
Alex Saloeeba0232019-03-14 18:56:54 -0700216 mRequested.set(false);
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800217 }
218
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800219 return whenToCheck;
220 }
221
222 /**
223 * Handles user activity by cancelling any pending attention requests and keeping track of when
224 * the activity happened.
225 *
226 * @param eventTime Activity time, in uptime millis.
Alex Salo9af68f12019-02-15 13:50:11 -0800227 * @param event Activity type as defined in {@link PowerManager}.
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800228 * @return 0 when activity was ignored, 1 when handled, -1 when invalid.
229 */
230 public int onUserActivity(long eventTime, int event) {
231 switch (event) {
232 case PowerManager.USER_ACTIVITY_EVENT_ATTENTION:
Alex Salod7c3eef2019-01-25 14:43:27 -0800233 mConsecutiveTimeoutExtendedCount.incrementAndGet();
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800234 return 0;
235 case PowerManager.USER_ACTIVITY_EVENT_OTHER:
236 case PowerManager.USER_ACTIVITY_EVENT_BUTTON:
237 case PowerManager.USER_ACTIVITY_EVENT_TOUCH:
238 case PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY:
239 cancelCurrentRequestIfAny();
240 mLastUserActivityTime = eventTime;
Alex Salod7c3eef2019-01-25 14:43:27 -0800241 resetConsecutiveExtensionCount();
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800242 return 1;
243 default:
244 if (DEBUG) {
245 Slog.d(TAG, "Attention not reset. Unknown activity event: " + event);
246 }
247 return -1;
248 }
249 }
250
251 public void onWakefulnessChangeStarted(int wakefulness) {
252 mWakefulness = wakefulness;
253 if (wakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE) {
254 cancelCurrentRequestIfAny();
Alex Salod7c3eef2019-01-25 14:43:27 -0800255 resetConsecutiveExtensionCount();
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800256 }
257 }
258
259 private void cancelCurrentRequestIfAny() {
Alex Saloeeba0232019-03-14 18:56:54 -0700260 if (mRequested.get()) {
261 mAttentionManager.cancelAttentionCheck(mCallback);
262 mRequested.set(false);
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800263 }
264 }
265
Alex Salod7c3eef2019-01-25 14:43:27 -0800266 private void resetConsecutiveExtensionCount() {
267 final long previousCount = mConsecutiveTimeoutExtendedCount.getAndSet(0);
268 if (previousCount > 0) {
269 StatsLog.write(StatsLog.SCREEN_TIMEOUT_EXTENSION_REPORTED, previousCount);
270 }
271 }
272
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800273 @VisibleForTesting
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800274 long getAttentionTimeout() {
275 return mMaxAttentionApiTimeoutMillis;
276 }
277
278 /**
279 * {@see AttentionManagerInternal#isAttentionServiceSupported}
280 */
281 @VisibleForTesting
282 boolean isAttentionServiceSupported() {
Alex Salo7a6e3a62019-02-27 15:08:15 -0800283 return mAttentionManager != null && mAttentionManager.isAttentionServiceSupported();
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800284 }
285
Alex Salo2b7dbe82019-04-11 14:45:42 -0700286 /**
287 * Returns {@code true} if the attention service has sufficient permissions, disables the
288 * depending features otherwise.
289 */
290 @VisibleForTesting
291 boolean serviceHasSufficientPermissions() {
292 final String attentionPackage = mPackageManager.getAttentionServicePackageName();
293 return attentionPackage != null && mPackageManager.checkPermission(
294 Manifest.permission.CAMERA, attentionPackage)
295 == PackageManager.PERMISSION_GRANTED;
296 }
297
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800298 public void dump(PrintWriter pw) {
Alex Salo75b0a992019-04-30 15:00:30 -0700299 pw.println("AttentionDetector:");
300 pw.println(" mMaximumExtensionMillis=" + mMaximumExtensionMillis);
301 pw.println(" mMaxAttentionApiTimeoutMillis=" + mMaxAttentionApiTimeoutMillis);
302 pw.println(" mLastUserActivityTime(excludingAttention)=" + mLastUserActivityTime);
303 pw.println(" mAttentionServiceSupported=" + isAttentionServiceSupported());
304 pw.println(" mRequested=" + mRequested);
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800305 }
Alex Salo69ff3212019-04-06 16:24:00 -0700306
307 @VisibleForTesting
308 final class AttentionCallbackInternalImpl extends AttentionCallbackInternal {
309 private final int mId;
310
311 AttentionCallbackInternalImpl(int id) {
312 this.mId = id;
313 }
314
315 @Override
316 public void onSuccess(int result, long timestamp) {
317 Slog.v(TAG, "onSuccess: " + result + ", ID: " + mId);
318 // If we don't check for request ID it's possible to get into a loop: success leads
319 // to the onUserAttention(), which in turn triggers updateUserActivity(), which will
320 // call back onSuccess() instantaneously if there is a cached value, and circle repeats.
321 if (mId == mRequestId && mRequested.getAndSet(false)) {
322 synchronized (mLock) {
323 if (mWakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE) {
324 if (DEBUG) Slog.d(TAG, "Device slept before receiving callback.");
325 return;
326 }
327 if (result == AttentionService.ATTENTION_SUCCESS_PRESENT) {
328 mOnUserAttention.run();
329 } else {
330 resetConsecutiveExtensionCount();
331 }
332 }
333 }
334 }
335
336 @Override
337 public void onFailure(int error) {
338 Slog.i(TAG, "Failed to check attention: " + error + ", ID: " + mId);
339 mRequested.set(false);
340 }
341 }
Yi Jiangf8291bb2019-05-24 16:32:10 -0700342
343 private final class UserSwitchObserver extends SynchronousUserSwitchObserver {
344 @Override
345 public void onUserSwitching(int newUserId) throws RemoteException {
346 updateEnabledFromSettings(mContext);
347 }
348 }
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800349}