blob: d9d21babe210b79c2eb4606af39fda2bf8404f01 [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 Saloeeba0232019-03-14 18:56:54 -070037import java.util.concurrent.atomic.AtomicBoolean;
Alex Salod7c3eef2019-01-25 14:43:27 -080038import java.util.concurrent.atomic.AtomicLong;
Lucas Dupin0a5d7972019-01-16 18:52:30 -080039
40/**
41 * Class responsible for checking if the user is currently paying attention to the phone and
42 * notifying {@link PowerManagerService} that user activity should be renewed.
43 *
44 * This class also implements a limit of how long the extension should be, to avoid security
45 * issues where the device would never be locked.
46 */
47public class AttentionDetector {
48
49 private static final String TAG = "AttentionDetector";
50 private static final boolean DEBUG = false;
51
Alex Salo9af68f12019-02-15 13:50:11 -080052 private boolean mIsSettingEnabled;
53
Lucas Dupin0a5d7972019-01-16 18:52:30 -080054 /**
55 * Invoked whenever user attention is detected.
56 */
57 private final Runnable mOnUserAttention;
58
59 /**
60 * The maximum time, in millis, that the phone can stay unlocked because of attention events,
61 * triggered by any user.
62 */
63 @VisibleForTesting
64 protected long mMaximumExtensionMillis;
65
66 private final Object mLock;
67
68 /**
Alex Saloeeba0232019-03-14 18:56:54 -070069 * If we're currently waiting for an attention callback
70 */
71 private final AtomicBoolean mRequested;
72
73 /**
Lucas Dupin0a5d7972019-01-16 18:52:30 -080074 * {@link android.service.attention.AttentionService} API timeout.
75 */
76 private long mMaxAttentionApiTimeoutMillis;
77
78 /**
79 * Last known user activity.
80 */
81 private long mLastUserActivityTime;
82
83 @VisibleForTesting
84 protected AttentionManagerInternal mAttentionManager;
85
86 /**
Lucas Dupin0a5d7972019-01-16 18:52:30 -080087 * Current wakefulness of the device. {@see PowerManagerInternal}
88 */
89 private int mWakefulness;
90
Alex Salod7c3eef2019-01-25 14:43:27 -080091 /**
92 * Describes how many times in a row was the timeout extended.
93 */
94 private AtomicLong mConsecutiveTimeoutExtendedCount = new AtomicLong(0);
95
Lucas Dupin0a5d7972019-01-16 18:52:30 -080096 @VisibleForTesting
97 final AttentionCallbackInternal mCallback = new AttentionCallbackInternal() {
Lucas Dupin0a5d7972019-01-16 18:52:30 -080098 @Override
Alex Saloeeba0232019-03-14 18:56:54 -070099 public void onSuccess(int result, long timestamp) {
100 Slog.v(TAG, "onSuccess: " + result);
101 if (mRequested.getAndSet(false)) {
102 synchronized (mLock) {
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800103 if (mWakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE) {
104 if (DEBUG) Slog.d(TAG, "Device slept before receiving callback.");
105 return;
106 }
107 if (result == AttentionService.ATTENTION_SUCCESS_PRESENT) {
108 mOnUserAttention.run();
Alex Salod7c3eef2019-01-25 14:43:27 -0800109 } else {
110 resetConsecutiveExtensionCount();
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800111 }
112 }
113 }
114 }
115
116 @Override
Alex Saloeeba0232019-03-14 18:56:54 -0700117 public void onFailure(int error) {
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800118 Slog.i(TAG, "Failed to check attention: " + error);
Alex Saloeeba0232019-03-14 18:56:54 -0700119 mRequested.set(false);
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800120 }
121 };
122
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 Salo4d9e4312019-02-13 14:59:42 -0800127
128 // Device starts with an awake state upon boot.
129 mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800130 }
131
Alex Salo9af68f12019-02-15 13:50:11 -0800132 @VisibleForTesting
133 void updateEnabledFromSettings(Context context) {
134 mIsSettingEnabled = Settings.System.getIntForUser(context.getContentResolver(),
135 Settings.System.ADAPTIVE_SLEEP, 0, UserHandle.USER_CURRENT) == 1;
136 }
137
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800138 public void systemReady(Context context) {
Alex Salo9af68f12019-02-15 13:50:11 -0800139 updateEnabledFromSettings(context);
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800140 mAttentionManager = LocalServices.getService(AttentionManagerInternal.class);
141 mMaximumExtensionMillis = context.getResources().getInteger(
142 com.android.internal.R.integer.config_attentionMaximumExtension);
143 mMaxAttentionApiTimeoutMillis = context.getResources().getInteger(
144 com.android.internal.R.integer.config_attentionApiTimeout);
Alex Salo9af68f12019-02-15 13:50:11 -0800145
146 context.getContentResolver().registerContentObserver(Settings.System.getUriFor(
147 Settings.System.ADAPTIVE_SLEEP),
148 false, new ContentObserver(new Handler()) {
149 @Override
150 public void onChange(boolean selfChange) {
151 updateEnabledFromSettings(context);
152 }
153 }, UserHandle.USER_ALL);
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800154 }
155
156 public long updateUserActivity(long nextScreenDimming) {
Alex Salo9af68f12019-02-15 13:50:11 -0800157 if (!mIsSettingEnabled) {
158 return nextScreenDimming;
159 }
160
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800161 if (!isAttentionServiceSupported()) {
162 return nextScreenDimming;
163 }
164
165 final long now = SystemClock.uptimeMillis();
166 final long whenToCheck = nextScreenDimming - getAttentionTimeout();
167 final long whenToStopExtending = mLastUserActivityTime + mMaximumExtensionMillis;
168 if (now < whenToCheck) {
169 if (DEBUG) {
170 Slog.d(TAG, "Do not check for attention yet, wait " + (whenToCheck - now));
171 }
Alex Salo1761fce2019-02-13 15:54:50 -0800172 return whenToCheck;
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800173 } else if (whenToStopExtending < whenToCheck) {
174 if (DEBUG) {
175 Slog.d(TAG, "Let device sleep to avoid false results and improve security "
176 + (whenToCheck - whenToStopExtending));
177 }
178 return nextScreenDimming;
Alex Saloeeba0232019-03-14 18:56:54 -0700179 } else if (mRequested.get()) {
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800180 if (DEBUG) {
Alex Saloeeba0232019-03-14 18:56:54 -0700181 // TODO(b/128134941): consider adding a member ID increasing counter in
182 // AttentionCallbackInternal to track this better.
183 Slog.d(TAG, "Pending attention callback, wait.");
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800184 }
185 return whenToCheck;
186 }
187
188 // Ideally we should attribute mRequested to the result of #checkAttention, but the
189 // callback might arrive before #checkAttention returns (if there are cached results.)
190 // This means that we must assume that the request was successful, and then cancel it
191 // afterwards if AttentionManager couldn't deliver it.
Alex Saloeeba0232019-03-14 18:56:54 -0700192 mRequested.set(true);
193 final boolean sent = mAttentionManager.checkAttention(getAttentionTimeout(), mCallback);
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800194 if (!sent) {
Alex Saloeeba0232019-03-14 18:56:54 -0700195 mRequested.set(false);
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800196 }
197
Alex Saloeeba0232019-03-14 18:56:54 -0700198 Slog.v(TAG, "Checking user attention");
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800199 return whenToCheck;
200 }
201
202 /**
203 * Handles user activity by cancelling any pending attention requests and keeping track of when
204 * the activity happened.
205 *
206 * @param eventTime Activity time, in uptime millis.
Alex Salo9af68f12019-02-15 13:50:11 -0800207 * @param event Activity type as defined in {@link PowerManager}.
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800208 * @return 0 when activity was ignored, 1 when handled, -1 when invalid.
209 */
210 public int onUserActivity(long eventTime, int event) {
211 switch (event) {
212 case PowerManager.USER_ACTIVITY_EVENT_ATTENTION:
Alex Salod7c3eef2019-01-25 14:43:27 -0800213 mConsecutiveTimeoutExtendedCount.incrementAndGet();
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800214 return 0;
215 case PowerManager.USER_ACTIVITY_EVENT_OTHER:
216 case PowerManager.USER_ACTIVITY_EVENT_BUTTON:
217 case PowerManager.USER_ACTIVITY_EVENT_TOUCH:
218 case PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY:
219 cancelCurrentRequestIfAny();
220 mLastUserActivityTime = eventTime;
Alex Salod7c3eef2019-01-25 14:43:27 -0800221 resetConsecutiveExtensionCount();
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800222 return 1;
223 default:
224 if (DEBUG) {
225 Slog.d(TAG, "Attention not reset. Unknown activity event: " + event);
226 }
227 return -1;
228 }
229 }
230
231 public void onWakefulnessChangeStarted(int wakefulness) {
232 mWakefulness = wakefulness;
233 if (wakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE) {
234 cancelCurrentRequestIfAny();
Alex Salod7c3eef2019-01-25 14:43:27 -0800235 resetConsecutiveExtensionCount();
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800236 }
237 }
238
239 private void cancelCurrentRequestIfAny() {
Alex Saloeeba0232019-03-14 18:56:54 -0700240 if (mRequested.get()) {
241 mAttentionManager.cancelAttentionCheck(mCallback);
242 mRequested.set(false);
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800243 }
244 }
245
Alex Salod7c3eef2019-01-25 14:43:27 -0800246 private void resetConsecutiveExtensionCount() {
247 final long previousCount = mConsecutiveTimeoutExtendedCount.getAndSet(0);
248 if (previousCount > 0) {
249 StatsLog.write(StatsLog.SCREEN_TIMEOUT_EXTENSION_REPORTED, previousCount);
250 }
251 }
252
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800253 @VisibleForTesting
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800254 long getAttentionTimeout() {
255 return mMaxAttentionApiTimeoutMillis;
256 }
257
258 /**
259 * {@see AttentionManagerInternal#isAttentionServiceSupported}
260 */
261 @VisibleForTesting
262 boolean isAttentionServiceSupported() {
Alex Salo7a6e3a62019-02-27 15:08:15 -0800263 return mAttentionManager != null && mAttentionManager.isAttentionServiceSupported();
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800264 }
265
266 public void dump(PrintWriter pw) {
267 pw.print("AttentionDetector:");
268 pw.print(" mMaximumExtensionMillis=" + mMaximumExtensionMillis);
269 pw.print(" mMaxAttentionApiTimeoutMillis=" + mMaxAttentionApiTimeoutMillis);
270 pw.print(" mLastUserActivityTime(excludingAttention)=" + mLastUserActivityTime);
271 pw.print(" mAttentionServiceSupported=" + isAttentionServiceSupported());
272 pw.print(" mRequested=" + mRequested);
273 }
274}