1 /*
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 
17 package com.android.server.power;
18 
19 import static android.provider.DeviceConfig.NAMESPACE_ATTENTION_MANAGER_SERVICE;
20 
21 import android.annotation.NonNull;
22 import android.app.ActivityManager;
23 import android.app.SynchronousUserSwitchObserver;
24 import android.attention.AttentionManagerInternal;
25 import android.attention.AttentionManagerInternal.AttentionCallbackInternal;
26 import android.content.ContentResolver;
27 import android.content.Context;
28 import android.database.ContentObserver;
29 import android.os.Handler;
30 import android.os.PowerManager;
31 import android.os.PowerManagerInternal;
32 import android.os.RemoteException;
33 import android.os.SystemClock;
34 import android.os.UserHandle;
35 import android.provider.DeviceConfig;
36 import android.provider.Settings;
37 import android.service.attention.AttentionService;
38 import android.util.Slog;
39 
40 import com.android.internal.annotations.VisibleForTesting;
41 import com.android.internal.util.FrameworkStatsLog;
42 import com.android.server.LocalServices;
43 import com.android.server.wm.WindowManagerInternal;
44 
45 import java.io.PrintWriter;
46 import java.util.Set;
47 import java.util.concurrent.atomic.AtomicBoolean;
48 import java.util.concurrent.atomic.AtomicLong;
49 
50 /**
51  * Class responsible for checking if the user is currently paying attention to the phone and
52  * notifying {@link PowerManagerService} that user activity should be renewed.
53  *
54  * This class also implements a limit of how long the extension should be, to avoid security
55  * issues where the device would never be locked.
56  */
57 public class AttentionDetector {
58 
59     private static final String TAG = "AttentionDetector";
60     private static final boolean DEBUG = false;
61 
62     /**
63      * DeviceConfig flag name, describes how much in advance to start checking attention before the
64      * dim event.
65      */
66     static final String KEY_PRE_DIM_CHECK_DURATION_MILLIS = "pre_dim_check_duration_millis";
67 
68     /** Default value in absence of {@link DeviceConfig} override. */
69     static final long DEFAULT_PRE_DIM_CHECK_DURATION_MILLIS = 2_000;
70 
71     @VisibleForTesting
72     protected long mPreDimCheckDurationMillis;
73 
74     /** DeviceConfig flag name, describes how long to run the check beyond the screen dim event. */
75     static final String KEY_POST_DIM_CHECK_DURATION_MILLIS =
76             "post_dim_check_duration_millis";
77 
78     /** Default value in absence of {@link DeviceConfig} override. */
79     static final long DEFAULT_POST_DIM_CHECK_DURATION_MILLIS = 0;
80 
81     private long mRequestedPostDimTimeoutMillis;
82 
83     /**
84      * Keep the last used post dim timeout for the dumpsys (it can't be longer the dim duration).
85      */
86     private long mEffectivePostDimTimeoutMillis;
87 
88     /**
89      * DeviceConfig flag name, describes the limit of how long the device can remain unlocked due to
90      * attention checking.
91      */
92     static final String KEY_MAX_EXTENSION_MILLIS = "max_extension_millis";
93 
94     /**
95      * The default value for the maximum time, in millis, that the phone can stay unlocked because
96      * of attention events, triggered by any user.
97      */
98     @VisibleForTesting
99     protected long mDefaultMaximumExtensionMillis;
100 
101     private long mMaximumExtensionMillis;
102 
103     private Context mContext;
104 
105     private boolean mIsSettingEnabled;
106 
107     /**
108      * Invoked whenever user attention is detected.
109      */
110     private final Runnable mOnUserAttention;
111 
112     private final Object mLock;
113 
114     /**
115      * If we're currently waiting for an attention callback
116      */
117     private final AtomicBoolean mRequested;
118 
119     private long mLastActedOnNextScreenDimming;
120 
121     /**
122      * Monotonously increasing ID for the requests sent.
123      */
124     @VisibleForTesting
125     protected int mRequestId;
126 
127     /**
128      * Last known user activity.
129      */
130     private long mLastUserActivityTime;
131 
132     @VisibleForTesting
133     protected AttentionManagerInternal mAttentionManager;
134 
135     @VisibleForTesting
136     protected WindowManagerInternal mWindowManager;
137 
138     @VisibleForTesting
139     protected ContentResolver mContentResolver;
140 
141     /**
142      * Current wakefulness of the device. {@see PowerManagerInternal}
143      */
144     private int mWakefulness;
145 
146     /**
147      * Describes how many times in a row was the timeout extended.
148      */
149     private AtomicLong mConsecutiveTimeoutExtendedCount = new AtomicLong(0);
150 
151     @VisibleForTesting
152     AttentionCallbackInternalImpl mCallback;
153 
AttentionDetector(Runnable onUserAttention, Object lock)154     public AttentionDetector(Runnable onUserAttention, Object lock) {
155         mOnUserAttention = onUserAttention;
156         mLock = lock;
157         mRequested = new AtomicBoolean(false);
158         mRequestId = 0;
159 
160         // Device starts with an awake state upon boot.
161         mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
162     }
163 
164     @VisibleForTesting
updateEnabledFromSettings(Context context)165     void updateEnabledFromSettings(Context context) {
166         mIsSettingEnabled = Settings.Secure.getIntForUser(context.getContentResolver(),
167                 Settings.Secure.ADAPTIVE_SLEEP, 0, UserHandle.USER_CURRENT) == 1;
168     }
169 
systemReady(Context context)170     public void systemReady(Context context) {
171         mContext = context;
172         updateEnabledFromSettings(context);
173         mContentResolver = context.getContentResolver();
174         mAttentionManager = LocalServices.getService(AttentionManagerInternal.class);
175         mWindowManager = LocalServices.getService(WindowManagerInternal.class);
176         mDefaultMaximumExtensionMillis = context.getResources().getInteger(
177                 com.android.internal.R.integer.config_attentionMaximumExtension);
178 
179         try {
180             final UserSwitchObserver observer = new UserSwitchObserver();
181             ActivityManager.getService().registerUserSwitchObserver(observer, TAG);
182         } catch (RemoteException e) {
183             // Shouldn't happen since in-process.
184         }
185 
186         context.getContentResolver().registerContentObserver(Settings.Secure.getUriFor(
187                 Settings.Secure.ADAPTIVE_SLEEP),
188                 false, new ContentObserver(new Handler(context.getMainLooper())) {
189                     @Override
190                     public void onChange(boolean selfChange) {
191                         updateEnabledFromSettings(context);
192                     }
193                 }, UserHandle.USER_ALL);
194 
195         readValuesFromDeviceConfig();
196         DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_ATTENTION_MANAGER_SERVICE,
197                 context.getMainExecutor(),
198                 (properties) -> onDeviceConfigChange(properties.getKeyset()));
199     }
200 
201     /** To be called in {@link PowerManagerService#updateUserActivitySummaryLocked}. */
updateUserActivity(long nextScreenDimming, long dimDurationMillis)202     public long updateUserActivity(long nextScreenDimming, long dimDurationMillis) {
203         if (nextScreenDimming == mLastActedOnNextScreenDimming
204                 || !mIsSettingEnabled
205                 || !isAttentionServiceSupported()
206                 || mWindowManager.isKeyguardShowingAndNotOccluded()) {
207             return nextScreenDimming;
208         }
209 
210         final long now = SystemClock.uptimeMillis();
211         final long whenToCheck = nextScreenDimming - mPreDimCheckDurationMillis;
212         final long whenToStopExtending = mLastUserActivityTime + mMaximumExtensionMillis;
213         if (now < whenToCheck) {
214             if (DEBUG) {
215                 Slog.d(TAG, "Do not check for attention yet, wait " + (whenToCheck - now));
216             }
217             return whenToCheck;
218         } else if (whenToStopExtending < whenToCheck) {
219             if (DEBUG) {
220                 Slog.d(TAG, "Let device sleep to avoid false results and improve security "
221                         + (whenToCheck - whenToStopExtending));
222             }
223             return nextScreenDimming;
224         } else if (mRequested.get()) {
225             if (DEBUG) {
226                 Slog.d(TAG, "Pending attention callback with ID=" + mCallback.mId + ", wait.");
227             }
228             return whenToCheck;
229         }
230 
231         // Ideally we should attribute mRequested to the result of #checkAttention, but the
232         // callback might arrive before #checkAttention returns (if there are cached results.)
233         // This means that we must assume that the request was successful, and then cancel it
234         // afterwards if AttentionManager couldn't deliver it.
235         mRequested.set(true);
236         mRequestId++;
237         mLastActedOnNextScreenDimming = nextScreenDimming;
238         mCallback = new AttentionCallbackInternalImpl(mRequestId);
239         mEffectivePostDimTimeoutMillis = Math.min(mRequestedPostDimTimeoutMillis,
240                 dimDurationMillis);
241         Slog.v(TAG, "Checking user attention, ID: " + mRequestId);
242         final boolean sent = mAttentionManager.checkAttention(
243                 mPreDimCheckDurationMillis + mEffectivePostDimTimeoutMillis,
244                 mCallback);
245         if (!sent) {
246             mRequested.set(false);
247         }
248 
249         return whenToCheck;
250     }
251 
252     /**
253      * Handles user activity by cancelling any pending attention requests and keeping track of when
254      * the activity happened.
255      *
256      * @param eventTime Activity time, in uptime millis.
257      * @param event     Activity type as defined in {@link PowerManager}.
258      * @return 0 when activity was ignored, 1 when handled, -1 when invalid.
259      */
onUserActivity(long eventTime, int event)260     public int onUserActivity(long eventTime, int event) {
261         switch (event) {
262             case PowerManager.USER_ACTIVITY_EVENT_ATTENTION:
263                 mConsecutiveTimeoutExtendedCount.incrementAndGet();
264                 return 0;
265             case PowerManager.USER_ACTIVITY_EVENT_OTHER:
266             case PowerManager.USER_ACTIVITY_EVENT_BUTTON:
267             case PowerManager.USER_ACTIVITY_EVENT_TOUCH:
268             case PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY:
269                 cancelCurrentRequestIfAny();
270                 mLastUserActivityTime = eventTime;
271                 resetConsecutiveExtensionCount();
272                 return 1;
273             default:
274                 if (DEBUG) {
275                     Slog.d(TAG, "Attention not reset. Unknown activity event: " + event);
276                 }
277                 return -1;
278         }
279     }
280 
onWakefulnessChangeStarted(int wakefulness)281     public void onWakefulnessChangeStarted(int wakefulness) {
282         mWakefulness = wakefulness;
283         if (wakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE) {
284             cancelCurrentRequestIfAny();
285             resetConsecutiveExtensionCount();
286         }
287     }
288 
cancelCurrentRequestIfAny()289     private void cancelCurrentRequestIfAny() {
290         if (mRequested.get()) {
291             mAttentionManager.cancelAttentionCheck(mCallback);
292             mRequested.set(false);
293         }
294     }
295 
resetConsecutiveExtensionCount()296     private void resetConsecutiveExtensionCount() {
297         final long previousCount = mConsecutiveTimeoutExtendedCount.getAndSet(0);
298         if (previousCount > 0) {
299             FrameworkStatsLog.write(FrameworkStatsLog.SCREEN_TIMEOUT_EXTENSION_REPORTED,
300                     previousCount);
301         }
302     }
303 
304     /**
305      * {@see AttentionManagerInternal#isAttentionServiceSupported}
306      */
307     @VisibleForTesting
isAttentionServiceSupported()308     boolean isAttentionServiceSupported() {
309         return mAttentionManager != null && mAttentionManager.isAttentionServiceSupported();
310     }
311 
dump(PrintWriter pw)312     public void dump(PrintWriter pw) {
313         pw.println("AttentionDetector:");
314         pw.println(" mIsSettingEnabled=" + mIsSettingEnabled);
315         pw.println(" mMaxExtensionMillis=" + mMaximumExtensionMillis);
316         pw.println(" mPreDimCheckDurationMillis=" + mPreDimCheckDurationMillis);
317         pw.println(" mEffectivePostDimTimeout=" + mEffectivePostDimTimeoutMillis);
318         pw.println(" mLastUserActivityTime(excludingAttention)=" + mLastUserActivityTime);
319         pw.println(" mAttentionServiceSupported=" + isAttentionServiceSupported());
320         pw.println(" mRequested=" + mRequested);
321     }
322 
323     /** How long to check <b>before</b> the screen dims, capped at the dim duration. */
324     @VisibleForTesting
getPreDimCheckDurationMillis()325     protected long getPreDimCheckDurationMillis() {
326         final long millis = DeviceConfig.getLong(NAMESPACE_ATTENTION_MANAGER_SERVICE,
327                 KEY_PRE_DIM_CHECK_DURATION_MILLIS,
328                 DEFAULT_PRE_DIM_CHECK_DURATION_MILLIS);
329 
330         if (millis < 0 || millis > 13_000) {
331             Slog.w(TAG, "Bad flag value supplied for: " + KEY_PRE_DIM_CHECK_DURATION_MILLIS);
332             return DEFAULT_PRE_DIM_CHECK_DURATION_MILLIS;
333         }
334 
335         return millis;
336     }
337 
338     /** How long to check <b>after</b> the screen dims, capped at the dim duration. */
339     @VisibleForTesting
getPostDimCheckDurationMillis()340     protected long getPostDimCheckDurationMillis() {
341         final long millis = DeviceConfig.getLong(NAMESPACE_ATTENTION_MANAGER_SERVICE,
342                 KEY_POST_DIM_CHECK_DURATION_MILLIS,
343                 DEFAULT_POST_DIM_CHECK_DURATION_MILLIS);
344 
345         if (millis < 0 || millis > 10_000) {
346             Slog.w(TAG, "Bad flag value supplied for: " + KEY_POST_DIM_CHECK_DURATION_MILLIS);
347             return DEFAULT_POST_DIM_CHECK_DURATION_MILLIS;
348         }
349         return millis;
350     }
351 
352     /** How long the device can remain unlocked due to attention checking. */
353     @VisibleForTesting
getMaxExtensionMillis()354     protected long getMaxExtensionMillis() {
355         final long millis = DeviceConfig.getLong(NAMESPACE_ATTENTION_MANAGER_SERVICE,
356                 KEY_MAX_EXTENSION_MILLIS,
357                 mDefaultMaximumExtensionMillis);
358 
359         if (millis < 0 || millis > 60 * 60 * 1000) { // 1 hour
360             Slog.w(TAG, "Bad flag value supplied for: " + KEY_MAX_EXTENSION_MILLIS);
361             return mDefaultMaximumExtensionMillis;
362         }
363 
364         return millis;
365     }
366 
onDeviceConfigChange(@onNull Set<String> keys)367     private void onDeviceConfigChange(@NonNull Set<String> keys) {
368         for (String key : keys) {
369             switch (key) {
370                 case KEY_MAX_EXTENSION_MILLIS:
371                 case KEY_POST_DIM_CHECK_DURATION_MILLIS:
372                 case KEY_PRE_DIM_CHECK_DURATION_MILLIS:
373                     readValuesFromDeviceConfig();
374                     return;
375                 default:
376                     Slog.i(TAG, "Ignoring change on " + key);
377             }
378         }
379     }
380 
readValuesFromDeviceConfig()381     private void readValuesFromDeviceConfig() {
382         mMaximumExtensionMillis = getMaxExtensionMillis();
383         mPreDimCheckDurationMillis = getPreDimCheckDurationMillis();
384         mRequestedPostDimTimeoutMillis = getPostDimCheckDurationMillis();
385 
386         Slog.i(TAG, "readValuesFromDeviceConfig():"
387                 + "\nmMaximumExtensionMillis=" + mMaximumExtensionMillis
388                 + "\nmPreDimCheckDurationMillis=" + mPreDimCheckDurationMillis
389                 + "\nmRequestedPostDimTimeoutMillis=" + mRequestedPostDimTimeoutMillis);
390 
391     }
392 
393     @VisibleForTesting
394     final class AttentionCallbackInternalImpl extends AttentionCallbackInternal {
395         private final int mId;
396 
AttentionCallbackInternalImpl(int id)397         AttentionCallbackInternalImpl(int id) {
398             this.mId = id;
399         }
400 
401         @Override
onSuccess(int result, long timestamp)402         public void onSuccess(int result, long timestamp) {
403             Slog.v(TAG, "onSuccess: " + result + ", ID: " + mId);
404             // If we don't check for request ID it's possible to get into a loop: success leads
405             // to the onUserAttention(), which in turn triggers updateUserActivity(), which will
406             // call back onSuccess() instantaneously if there is a cached value, and circle repeats.
407             if (mId == mRequestId && mRequested.getAndSet(false)) {
408                 synchronized (mLock) {
409                     if (mWakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE) {
410                         if (DEBUG) Slog.d(TAG, "Device slept before receiving callback.");
411                         return;
412                     }
413                     if (result == AttentionService.ATTENTION_SUCCESS_PRESENT) {
414                         mOnUserAttention.run();
415                     } else {
416                         resetConsecutiveExtensionCount();
417                     }
418                 }
419             }
420         }
421 
422         @Override
onFailure(int error)423         public void onFailure(int error) {
424             Slog.i(TAG, "Failed to check attention: " + error + ", ID: " + mId);
425             mRequested.set(false);
426         }
427     }
428 
429     private final class UserSwitchObserver extends SynchronousUserSwitchObserver {
430         @Override
onUserSwitching(int newUserId)431         public void onUserSwitching(int newUserId) throws RemoteException {
432             updateEnabledFromSettings(mContext);
433         }
434     }
435 }
436