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.Settings.System.ADAPTIVE_SLEEP;
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.Settings;
37 import android.service.attention.AttentionService;
38 import android.util.Slog;
39 import android.util.StatsLog;
40 
41 import com.android.internal.annotations.VisibleForTesting;
42 import com.android.server.LocalServices;
43 import com.android.server.wm.WindowManagerInternal;
44 
45 import java.io.PrintWriter;
46 import java.util.concurrent.atomic.AtomicBoolean;
47 import java.util.concurrent.atomic.AtomicLong;
48 
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  */
56 public class AttentionDetector {
57 
58     private static final String TAG = "AttentionDetector";
59     private static final boolean DEBUG = false;
60 
61     private Context mContext;
62 
63     private boolean mIsSettingEnabled;
64 
65     /**
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     /**
80      * If we're currently waiting for an attention callback
81      */
82     private final AtomicBoolean mRequested;
83 
84     private long mLastActedOnNextScreenDimming;
85 
86     /**
87      * Monotonously increasing ID for the requests sent.
88      */
89     @VisibleForTesting
90     protected int mRequestId;
91 
92     /**
93      * {@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 
105     @VisibleForTesting
106     protected WindowManagerInternal mWindowManager;
107 
108     @VisibleForTesting
109     protected PackageManager mPackageManager;
110 
111     @VisibleForTesting
112     protected ContentResolver mContentResolver;
113 
114     /**
115      * Current wakefulness of the device. {@see PowerManagerInternal}
116      */
117     private int mWakefulness;
118 
119     /**
120      * Describes how many times in a row was the timeout extended.
121      */
122     private AtomicLong mConsecutiveTimeoutExtendedCount = new AtomicLong(0);
123 
124     @VisibleForTesting
125     AttentionCallbackInternalImpl mCallback;
126 
AttentionDetector(Runnable onUserAttention, Object lock)127     public AttentionDetector(Runnable onUserAttention, Object lock) {
128         mOnUserAttention = onUserAttention;
129         mLock = lock;
130         mRequested = new AtomicBoolean(false);
131         mRequestId = 0;
132 
133         // Device starts with an awake state upon boot.
134         mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
135     }
136 
137     @VisibleForTesting
updateEnabledFromSettings(Context context)138     void updateEnabledFromSettings(Context context) {
139         mIsSettingEnabled = Settings.System.getIntForUser(context.getContentResolver(),
140                 Settings.System.ADAPTIVE_SLEEP, 0, UserHandle.USER_CURRENT) == 1;
141     }
142 
systemReady(Context context)143     public void systemReady(Context context) {
144         mContext = context;
145         updateEnabledFromSettings(context);
146         mPackageManager = context.getPackageManager();
147         mContentResolver = context.getContentResolver();
148         mAttentionManager = LocalServices.getService(AttentionManagerInternal.class);
149         mWindowManager = LocalServices.getService(WindowManagerInternal.class);
150         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);
154 
155         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 
162         context.getContentResolver().registerContentObserver(Settings.System.getUriFor(
163                 Settings.System.ADAPTIVE_SLEEP),
164                 false, new ContentObserver(new Handler(context.getMainLooper())) {
165                     @Override
166                     public void onChange(boolean selfChange) {
167                         updateEnabledFromSettings(context);
168                     }
169                 }, UserHandle.USER_ALL);
170     }
171 
updateUserActivity(long nextScreenDimming)172     public long updateUserActivity(long nextScreenDimming) {
173         if (nextScreenDimming == mLastActedOnNextScreenDimming
174                 || !mIsSettingEnabled
175                 || !isAttentionServiceSupported()
176                 || mWindowManager.isKeyguardShowingAndNotOccluded()) {
177             return nextScreenDimming;
178         }
179 
180         if (!serviceHasSufficientPermissions()) {
181             Settings.System.putInt(mContentResolver, ADAPTIVE_SLEEP, 0);
182             return nextScreenDimming;
183         }
184 
185         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             }
192             return whenToCheck;
193         } 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;
199         } else if (mRequested.get()) {
200             if (DEBUG) {
201                 Slog.d(TAG, "Pending attention callback with ID=" + mCallback.mId + ", wait.");
202             }
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.
210         mRequested.set(true);
211         mRequestId++;
212         mLastActedOnNextScreenDimming = nextScreenDimming;
213         mCallback = new AttentionCallbackInternalImpl(mRequestId);
214         Slog.v(TAG, "Checking user attention, ID: " + mRequestId);
215         final boolean sent = mAttentionManager.checkAttention(getAttentionTimeout(), mCallback);
216         if (!sent) {
217             mRequested.set(false);
218         }
219 
220         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.
228      * @param event     Activity type as defined in {@link PowerManager}.
229      * @return 0 when activity was ignored, 1 when handled, -1 when invalid.
230      */
onUserActivity(long eventTime, int event)231     public int onUserActivity(long eventTime, int event) {
232         switch (event) {
233             case PowerManager.USER_ACTIVITY_EVENT_ATTENTION:
234                 mConsecutiveTimeoutExtendedCount.incrementAndGet();
235                 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;
242                 resetConsecutiveExtensionCount();
243                 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 
onWakefulnessChangeStarted(int wakefulness)252     public void onWakefulnessChangeStarted(int wakefulness) {
253         mWakefulness = wakefulness;
254         if (wakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE) {
255             cancelCurrentRequestIfAny();
256             resetConsecutiveExtensionCount();
257         }
258     }
259 
cancelCurrentRequestIfAny()260     private void cancelCurrentRequestIfAny() {
261         if (mRequested.get()) {
262             mAttentionManager.cancelAttentionCheck(mCallback);
263             mRequested.set(false);
264         }
265     }
266 
resetConsecutiveExtensionCount()267     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 
274     @VisibleForTesting
getAttentionTimeout()275     long getAttentionTimeout() {
276         return mMaxAttentionApiTimeoutMillis;
277     }
278 
279     /**
280      * {@see AttentionManagerInternal#isAttentionServiceSupported}
281      */
282     @VisibleForTesting
isAttentionServiceSupported()283     boolean isAttentionServiceSupported() {
284         return mAttentionManager != null && mAttentionManager.isAttentionServiceSupported();
285     }
286 
287     /**
288      * Returns {@code true} if the attention service has sufficient permissions, disables the
289      * depending features otherwise.
290      */
291     @VisibleForTesting
serviceHasSufficientPermissions()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 
dump(PrintWriter pw)299     public void dump(PrintWriter pw) {
300         pw.println("AttentionDetector:");
301         pw.println(" mIsSettingEnabled=" + mIsSettingEnabled);
302         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);
307     }
308 
309     @VisibleForTesting
310     final class AttentionCallbackInternalImpl extends AttentionCallbackInternal {
311         private final int mId;
312 
AttentionCallbackInternalImpl(int id)313         AttentionCallbackInternalImpl(int id) {
314             this.mId = id;
315         }
316 
317         @Override
onSuccess(int result, long timestamp)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
onFailure(int error)339         public void onFailure(int error) {
340             Slog.i(TAG, "Failed to check attention: " + error + ", ID: " + mId);
341             mRequested.set(false);
342         }
343     }
344 
345     private final class UserSwitchObserver extends SynchronousUserSwitchObserver {
346         @Override
onUserSwitching(int newUserId)347         public void onUserSwitching(int newUserId) throws RemoteException {
348             updateEnabledFromSettings(mContext);
349         }
350     }
351 }
352