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