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