blob: 56d839633ab5457c52331385de8d56a1649cc66a [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;
Alex Salo49489762019-06-03 16:23:30 -070043import com.android.server.wm.WindowManagerInternal;
Lucas Dupin0a5d7972019-01-16 18:52:30 -080044
45import java.io.PrintWriter;
Alex Saloeeba0232019-03-14 18:56:54 -070046import java.util.concurrent.atomic.AtomicBoolean;
Alex Salod7c3eef2019-01-25 14:43:27 -080047import java.util.concurrent.atomic.AtomicLong;
Lucas Dupin0a5d7972019-01-16 18:52:30 -080048
49/**
50 * Class responsible for checking if the user is currently paying attention to the phone and
51 * notifying {@link PowerManagerService} that user activity should be renewed.
52 *
53 * This class also implements a limit of how long the extension should be, to avoid security
54 * issues where the device would never be locked.
55 */
56public class AttentionDetector {
57
58 private static final String TAG = "AttentionDetector";
59 private static final boolean DEBUG = false;
60
Yi Jiangf8291bb2019-05-24 16:32:10 -070061 private Context mContext;
62
Alex Salo9af68f12019-02-15 13:50:11 -080063 private boolean mIsSettingEnabled;
64
Lucas Dupin0a5d7972019-01-16 18:52:30 -080065 /**
66 * Invoked whenever user attention is detected.
67 */
68 private final Runnable mOnUserAttention;
69
70 /**
71 * The maximum time, in millis, that the phone can stay unlocked because of attention events,
72 * triggered by any user.
73 */
74 @VisibleForTesting
75 protected long mMaximumExtensionMillis;
76
77 private final Object mLock;
78
79 /**
Alex Saloeeba0232019-03-14 18:56:54 -070080 * If we're currently waiting for an attention callback
81 */
82 private final AtomicBoolean mRequested;
83
Alex Salocbe610a2019-05-01 14:18:46 -070084 private long mLastActedOnNextScreenDimming;
85
Alex Saloeeba0232019-03-14 18:56:54 -070086 /**
Alex Salo69ff3212019-04-06 16:24:00 -070087 * Monotonously increasing ID for the requests sent.
88 */
89 @VisibleForTesting
90 protected int mRequestId;
91
92 /**
Lucas Dupin0a5d7972019-01-16 18:52:30 -080093 * {@link android.service.attention.AttentionService} API timeout.
94 */
95 private long mMaxAttentionApiTimeoutMillis;
96
97 /**
98 * Last known user activity.
99 */
100 private long mLastUserActivityTime;
101
102 @VisibleForTesting
103 protected AttentionManagerInternal mAttentionManager;
104
Alex Salo2b7dbe82019-04-11 14:45:42 -0700105 @VisibleForTesting
Alex Salo49489762019-06-03 16:23:30 -0700106 protected WindowManagerInternal mWindowManager;
107
108 @VisibleForTesting
Alex Salo2b7dbe82019-04-11 14:45:42 -0700109 protected PackageManager mPackageManager;
110
111 @VisibleForTesting
112 protected ContentResolver mContentResolver;
113
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800114 /**
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800115 * Current wakefulness of the device. {@see PowerManagerInternal}
116 */
117 private int mWakefulness;
118
Alex Salod7c3eef2019-01-25 14:43:27 -0800119 /**
120 * Describes how many times in a row was the timeout extended.
121 */
122 private AtomicLong mConsecutiveTimeoutExtendedCount = new AtomicLong(0);
123
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800124 @VisibleForTesting
Alex Salo69ff3212019-04-06 16:24:00 -0700125 AttentionCallbackInternalImpl mCallback;
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800126
127 public AttentionDetector(Runnable onUserAttention, Object lock) {
128 mOnUserAttention = onUserAttention;
129 mLock = lock;
Alex Saloeeba0232019-03-14 18:56:54 -0700130 mRequested = new AtomicBoolean(false);
Alex Salo69ff3212019-04-06 16:24:00 -0700131 mRequestId = 0;
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) {
Yi Jiangf8291bb2019-05-24 16:32:10 -0700144 mContext = context;
Alex Salo9af68f12019-02-15 13:50:11 -0800145 updateEnabledFromSettings(context);
Alex Salo2b7dbe82019-04-11 14:45:42 -0700146 mPackageManager = context.getPackageManager();
147 mContentResolver = context.getContentResolver();
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800148 mAttentionManager = LocalServices.getService(AttentionManagerInternal.class);
Alex Salo49489762019-06-03 16:23:30 -0700149 mWindowManager = LocalServices.getService(WindowManagerInternal.class);
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800150 mMaximumExtensionMillis = context.getResources().getInteger(
151 com.android.internal.R.integer.config_attentionMaximumExtension);
152 mMaxAttentionApiTimeoutMillis = context.getResources().getInteger(
153 com.android.internal.R.integer.config_attentionApiTimeout);
Alex Salo9af68f12019-02-15 13:50:11 -0800154
Yi Jiangf8291bb2019-05-24 16:32:10 -0700155 try {
156 final UserSwitchObserver observer = new UserSwitchObserver();
157 ActivityManager.getService().registerUserSwitchObserver(observer, TAG);
158 } catch (RemoteException e) {
159 // Shouldn't happen since in-process.
160 }
161
Alex Salo9af68f12019-02-15 13:50:11 -0800162 context.getContentResolver().registerContentObserver(Settings.System.getUriFor(
163 Settings.System.ADAPTIVE_SLEEP),
164 false, new ContentObserver(new Handler()) {
165 @Override
166 public void onChange(boolean selfChange) {
167 updateEnabledFromSettings(context);
168 }
169 }, UserHandle.USER_ALL);
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800170 }
171
172 public long updateUserActivity(long nextScreenDimming) {
Alex Salo49489762019-06-03 16:23:30 -0700173 if (nextScreenDimming == mLastActedOnNextScreenDimming
174 || !mIsSettingEnabled
175 || !isAttentionServiceSupported()
176 || mWindowManager.isKeyguardShowingAndNotOccluded()) {
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800177 return nextScreenDimming;
178 }
179
Alex Salo2b7dbe82019-04-11 14:45:42 -0700180 if (!serviceHasSufficientPermissions()) {
181 Settings.System.putInt(mContentResolver, ADAPTIVE_SLEEP, 0);
182 return nextScreenDimming;
183 }
184
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800185 final long now = SystemClock.uptimeMillis();
186 final long whenToCheck = nextScreenDimming - getAttentionTimeout();
187 final long whenToStopExtending = mLastUserActivityTime + mMaximumExtensionMillis;
188 if (now < whenToCheck) {
189 if (DEBUG) {
190 Slog.d(TAG, "Do not check for attention yet, wait " + (whenToCheck - now));
191 }
Alex Salo1761fce2019-02-13 15:54:50 -0800192 return whenToCheck;
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800193 } else if (whenToStopExtending < whenToCheck) {
194 if (DEBUG) {
195 Slog.d(TAG, "Let device sleep to avoid false results and improve security "
196 + (whenToCheck - whenToStopExtending));
197 }
198 return nextScreenDimming;
Alex Saloeeba0232019-03-14 18:56:54 -0700199 } else if (mRequested.get()) {
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800200 if (DEBUG) {
Alex Salo69ff3212019-04-06 16:24:00 -0700201 Slog.d(TAG, "Pending attention callback with ID=" + mCallback.mId + ", wait.");
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800202 }
203 return whenToCheck;
204 }
205
206 // Ideally we should attribute mRequested to the result of #checkAttention, but the
207 // callback might arrive before #checkAttention returns (if there are cached results.)
208 // This means that we must assume that the request was successful, and then cancel it
209 // afterwards if AttentionManager couldn't deliver it.
Alex Saloeeba0232019-03-14 18:56:54 -0700210 mRequested.set(true);
Alex Salo69ff3212019-04-06 16:24:00 -0700211 mRequestId++;
Alex Salocbe610a2019-05-01 14:18:46 -0700212 mLastActedOnNextScreenDimming = nextScreenDimming;
Alex Salo69ff3212019-04-06 16:24:00 -0700213 mCallback = new AttentionCallbackInternalImpl(mRequestId);
Alex Salocbe610a2019-05-01 14:18:46 -0700214 Slog.v(TAG, "Checking user attention, ID: " + mRequestId);
Alex Saloeeba0232019-03-14 18:56:54 -0700215 final boolean sent = mAttentionManager.checkAttention(getAttentionTimeout(), mCallback);
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800216 if (!sent) {
Alex Saloeeba0232019-03-14 18:56:54 -0700217 mRequested.set(false);
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800218 }
219
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800220 return whenToCheck;
221 }
222
223 /**
224 * Handles user activity by cancelling any pending attention requests and keeping track of when
225 * the activity happened.
226 *
227 * @param eventTime Activity time, in uptime millis.
Alex Salo9af68f12019-02-15 13:50:11 -0800228 * @param event Activity type as defined in {@link PowerManager}.
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800229 * @return 0 when activity was ignored, 1 when handled, -1 when invalid.
230 */
231 public int onUserActivity(long eventTime, int event) {
232 switch (event) {
233 case PowerManager.USER_ACTIVITY_EVENT_ATTENTION:
Alex Salod7c3eef2019-01-25 14:43:27 -0800234 mConsecutiveTimeoutExtendedCount.incrementAndGet();
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800235 return 0;
236 case PowerManager.USER_ACTIVITY_EVENT_OTHER:
237 case PowerManager.USER_ACTIVITY_EVENT_BUTTON:
238 case PowerManager.USER_ACTIVITY_EVENT_TOUCH:
239 case PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY:
240 cancelCurrentRequestIfAny();
241 mLastUserActivityTime = eventTime;
Alex Salod7c3eef2019-01-25 14:43:27 -0800242 resetConsecutiveExtensionCount();
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800243 return 1;
244 default:
245 if (DEBUG) {
246 Slog.d(TAG, "Attention not reset. Unknown activity event: " + event);
247 }
248 return -1;
249 }
250 }
251
252 public void onWakefulnessChangeStarted(int wakefulness) {
253 mWakefulness = wakefulness;
254 if (wakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE) {
255 cancelCurrentRequestIfAny();
Alex Salod7c3eef2019-01-25 14:43:27 -0800256 resetConsecutiveExtensionCount();
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800257 }
258 }
259
260 private void cancelCurrentRequestIfAny() {
Alex Saloeeba0232019-03-14 18:56:54 -0700261 if (mRequested.get()) {
262 mAttentionManager.cancelAttentionCheck(mCallback);
263 mRequested.set(false);
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800264 }
265 }
266
Alex Salod7c3eef2019-01-25 14:43:27 -0800267 private void resetConsecutiveExtensionCount() {
268 final long previousCount = mConsecutiveTimeoutExtendedCount.getAndSet(0);
269 if (previousCount > 0) {
270 StatsLog.write(StatsLog.SCREEN_TIMEOUT_EXTENSION_REPORTED, previousCount);
271 }
272 }
273
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800274 @VisibleForTesting
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800275 long getAttentionTimeout() {
276 return mMaxAttentionApiTimeoutMillis;
277 }
278
279 /**
280 * {@see AttentionManagerInternal#isAttentionServiceSupported}
281 */
282 @VisibleForTesting
283 boolean isAttentionServiceSupported() {
Alex Salo7a6e3a62019-02-27 15:08:15 -0800284 return mAttentionManager != null && mAttentionManager.isAttentionServiceSupported();
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800285 }
286
Alex Salo2b7dbe82019-04-11 14:45:42 -0700287 /**
288 * Returns {@code true} if the attention service has sufficient permissions, disables the
289 * depending features otherwise.
290 */
291 @VisibleForTesting
292 boolean serviceHasSufficientPermissions() {
293 final String attentionPackage = mPackageManager.getAttentionServicePackageName();
294 return attentionPackage != null && mPackageManager.checkPermission(
295 Manifest.permission.CAMERA, attentionPackage)
296 == PackageManager.PERMISSION_GRANTED;
297 }
298
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800299 public void dump(PrintWriter pw) {
Alex Salo75b0a992019-04-30 15:00:30 -0700300 pw.println("AttentionDetector:");
Alex Saloa5085f62019-06-04 18:20:12 -0700301 pw.println(" mIsSettingEnabled=" + mIsSettingEnabled);
Alex Salo75b0a992019-04-30 15:00:30 -0700302 pw.println(" mMaximumExtensionMillis=" + mMaximumExtensionMillis);
303 pw.println(" mMaxAttentionApiTimeoutMillis=" + mMaxAttentionApiTimeoutMillis);
304 pw.println(" mLastUserActivityTime(excludingAttention)=" + mLastUserActivityTime);
305 pw.println(" mAttentionServiceSupported=" + isAttentionServiceSupported());
306 pw.println(" mRequested=" + mRequested);
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800307 }
Alex Salo69ff3212019-04-06 16:24:00 -0700308
309 @VisibleForTesting
310 final class AttentionCallbackInternalImpl extends AttentionCallbackInternal {
311 private final int mId;
312
313 AttentionCallbackInternalImpl(int id) {
314 this.mId = id;
315 }
316
317 @Override
318 public void onSuccess(int result, long timestamp) {
319 Slog.v(TAG, "onSuccess: " + result + ", ID: " + mId);
320 // If we don't check for request ID it's possible to get into a loop: success leads
321 // to the onUserAttention(), which in turn triggers updateUserActivity(), which will
322 // call back onSuccess() instantaneously if there is a cached value, and circle repeats.
323 if (mId == mRequestId && mRequested.getAndSet(false)) {
324 synchronized (mLock) {
325 if (mWakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE) {
326 if (DEBUG) Slog.d(TAG, "Device slept before receiving callback.");
327 return;
328 }
329 if (result == AttentionService.ATTENTION_SUCCESS_PRESENT) {
330 mOnUserAttention.run();
331 } else {
332 resetConsecutiveExtensionCount();
333 }
334 }
335 }
336 }
337
338 @Override
339 public void onFailure(int error) {
340 Slog.i(TAG, "Failed to check attention: " + error + ", ID: " + mId);
341 mRequested.set(false);
342 }
343 }
Yi Jiangf8291bb2019-05-24 16:32:10 -0700344
345 private final class UserSwitchObserver extends SynchronousUserSwitchObserver {
346 @Override
347 public void onUserSwitching(int newUserId) throws RemoteException {
348 updateEnabledFromSettings(mContext);
349 }
350 }
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800351}