• 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.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 
44 import java.io.PrintWriter;
45 import java.util.concurrent.atomic.AtomicBoolean;
46 import java.util.concurrent.atomic.AtomicLong;
47 
48 /**
49  * Class responsible for checking if the user is currently paying attention to the phone and
50  * notifying {@link PowerManagerService} that user activity should be renewed.
51  *
52  * This class also implements a limit of how long the extension should be, to avoid security
53  * issues where the device would never be locked.
54  */
55 public class AttentionDetector {
56 
57     private static final String TAG = "AttentionDetector";
58     private static final boolean DEBUG = false;
59 
60     private Context mContext;
61 
62     private boolean mIsSettingEnabled;
63 
64     /**
65      * Invoked whenever user attention is detected.
66      */
67     private final Runnable mOnUserAttention;
68 
69     /**
70      * The maximum time, in millis, that the phone can stay unlocked because of attention events,
71      * triggered by any user.
72      */
73     @VisibleForTesting
74     protected long mMaximumExtensionMillis;
75 
76     private final Object mLock;
77 
78     /**
79      * If we're currently waiting for an attention callback
80      */
81     private final AtomicBoolean mRequested;
82 
83     private long mLastActedOnNextScreenDimming;
84 
85     /**
86      * Monotonously increasing ID for the requests sent.
87      */
88     @VisibleForTesting
89     protected int mRequestId;
90 
91     /**
92      * {@link android.service.attention.AttentionService} API timeout.
93      */
94     private long mMaxAttentionApiTimeoutMillis;
95 
96     /**
97      * Last known user activity.
98      */
99     private long mLastUserActivityTime;
100 
101     @VisibleForTesting
102     protected AttentionManagerInternal mAttentionManager;
103 
104     @VisibleForTesting
105     protected PackageManager mPackageManager;
106 
107     @VisibleForTesting
108     protected ContentResolver mContentResolver;
109 
110     /**
111      * Current wakefulness of the device. {@see PowerManagerInternal}
112      */
113     private int mWakefulness;
114 
115     /**
116      * Describes how many times in a row was the timeout extended.
117      */
118     private AtomicLong mConsecutiveTimeoutExtendedCount = new AtomicLong(0);
119 
120     @VisibleForTesting
121     AttentionCallbackInternalImpl mCallback;
122 
AttentionDetector(Runnable onUserAttention, Object lock)123     public AttentionDetector(Runnable onUserAttention, Object lock) {
124         mOnUserAttention = onUserAttention;
125         mLock = lock;
126         mRequested = new AtomicBoolean(false);
127         mRequestId = 0;
128 
129         // Device starts with an awake state upon boot.
130         mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
131     }
132 
133     @VisibleForTesting
updateEnabledFromSettings(Context context)134     void updateEnabledFromSettings(Context context) {
135         mIsSettingEnabled = Settings.System.getIntForUser(context.getContentResolver(),
136                 Settings.System.ADAPTIVE_SLEEP, 0, UserHandle.USER_CURRENT) == 1;
137     }
138 
systemReady(Context context)139     public void systemReady(Context context) {
140         mContext = context;
141         updateEnabledFromSettings(context);
142         mPackageManager = context.getPackageManager();
143         mContentResolver = context.getContentResolver();
144         mAttentionManager = LocalServices.getService(AttentionManagerInternal.class);
145         mMaximumExtensionMillis = context.getResources().getInteger(
146                 com.android.internal.R.integer.config_attentionMaximumExtension);
147         mMaxAttentionApiTimeoutMillis = context.getResources().getInteger(
148                 com.android.internal.R.integer.config_attentionApiTimeout);
149 
150         try {
151             final UserSwitchObserver observer = new UserSwitchObserver();
152             ActivityManager.getService().registerUserSwitchObserver(observer, TAG);
153         } catch (RemoteException e) {
154              // Shouldn't happen since in-process.
155         }
156 
157         context.getContentResolver().registerContentObserver(Settings.System.getUriFor(
158                 Settings.System.ADAPTIVE_SLEEP),
159                 false, new ContentObserver(new Handler()) {
160                     @Override
161                     public void onChange(boolean selfChange) {
162                         updateEnabledFromSettings(context);
163                     }
164                 }, UserHandle.USER_ALL);
165     }
166 
updateUserActivity(long nextScreenDimming)167     public long updateUserActivity(long nextScreenDimming) {
168         if (nextScreenDimming == mLastActedOnNextScreenDimming) {
169             return nextScreenDimming;
170         }
171         if (!mIsSettingEnabled) {
172             return nextScreenDimming;
173         }
174 
175         if (!isAttentionServiceSupported()) {
176             return nextScreenDimming;
177         }
178 
179         if (!serviceHasSufficientPermissions()) {
180             Settings.System.putInt(mContentResolver, ADAPTIVE_SLEEP, 0);
181             return nextScreenDimming;
182         }
183 
184         final long now = SystemClock.uptimeMillis();
185         final long whenToCheck = nextScreenDimming - getAttentionTimeout();
186         final long whenToStopExtending = mLastUserActivityTime + mMaximumExtensionMillis;
187         if (now < whenToCheck) {
188             if (DEBUG) {
189                 Slog.d(TAG, "Do not check for attention yet, wait " + (whenToCheck - now));
190             }
191             return whenToCheck;
192         } else if (whenToStopExtending < whenToCheck) {
193             if (DEBUG) {
194                 Slog.d(TAG, "Let device sleep to avoid false results and improve security "
195                         + (whenToCheck - whenToStopExtending));
196             }
197             return nextScreenDimming;
198         } else if (mRequested.get()) {
199             if (DEBUG) {
200                 Slog.d(TAG, "Pending attention callback with ID=" + mCallback.mId + ", wait.");
201             }
202             return whenToCheck;
203         }
204 
205         // Ideally we should attribute mRequested to the result of #checkAttention, but the
206         // callback might arrive before #checkAttention returns (if there are cached results.)
207         // This means that we must assume that the request was successful, and then cancel it
208         // afterwards if AttentionManager couldn't deliver it.
209         mRequested.set(true);
210         mRequestId++;
211         mLastActedOnNextScreenDimming = nextScreenDimming;
212         mCallback = new AttentionCallbackInternalImpl(mRequestId);
213         Slog.v(TAG, "Checking user attention, ID: " + mRequestId);
214         final boolean sent = mAttentionManager.checkAttention(getAttentionTimeout(), mCallback);
215         if (!sent) {
216             mRequested.set(false);
217         }
218 
219         return whenToCheck;
220     }
221 
222     /**
223      * Handles user activity by cancelling any pending attention requests and keeping track of when
224      * the activity happened.
225      *
226      * @param eventTime Activity time, in uptime millis.
227      * @param event     Activity type as defined in {@link PowerManager}.
228      * @return 0 when activity was ignored, 1 when handled, -1 when invalid.
229      */
onUserActivity(long eventTime, int event)230     public int onUserActivity(long eventTime, int event) {
231         switch (event) {
232             case PowerManager.USER_ACTIVITY_EVENT_ATTENTION:
233                 mConsecutiveTimeoutExtendedCount.incrementAndGet();
234                 return 0;
235             case PowerManager.USER_ACTIVITY_EVENT_OTHER:
236             case PowerManager.USER_ACTIVITY_EVENT_BUTTON:
237             case PowerManager.USER_ACTIVITY_EVENT_TOUCH:
238             case PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY:
239                 cancelCurrentRequestIfAny();
240                 mLastUserActivityTime = eventTime;
241                 resetConsecutiveExtensionCount();
242                 return 1;
243             default:
244                 if (DEBUG) {
245                     Slog.d(TAG, "Attention not reset. Unknown activity event: " + event);
246                 }
247                 return -1;
248         }
249     }
250 
onWakefulnessChangeStarted(int wakefulness)251     public void onWakefulnessChangeStarted(int wakefulness) {
252         mWakefulness = wakefulness;
253         if (wakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE) {
254             cancelCurrentRequestIfAny();
255             resetConsecutiveExtensionCount();
256         }
257     }
258 
cancelCurrentRequestIfAny()259     private void cancelCurrentRequestIfAny() {
260         if (mRequested.get()) {
261             mAttentionManager.cancelAttentionCheck(mCallback);
262             mRequested.set(false);
263         }
264     }
265 
resetConsecutiveExtensionCount()266     private void resetConsecutiveExtensionCount() {
267         final long previousCount = mConsecutiveTimeoutExtendedCount.getAndSet(0);
268         if (previousCount > 0) {
269             StatsLog.write(StatsLog.SCREEN_TIMEOUT_EXTENSION_REPORTED, previousCount);
270         }
271     }
272 
273     @VisibleForTesting
getAttentionTimeout()274     long getAttentionTimeout() {
275         return mMaxAttentionApiTimeoutMillis;
276     }
277 
278     /**
279      * {@see AttentionManagerInternal#isAttentionServiceSupported}
280      */
281     @VisibleForTesting
isAttentionServiceSupported()282     boolean isAttentionServiceSupported() {
283         return mAttentionManager != null && mAttentionManager.isAttentionServiceSupported();
284     }
285 
286     /**
287      * Returns {@code true} if the attention service has sufficient permissions, disables the
288      * depending features otherwise.
289      */
290     @VisibleForTesting
serviceHasSufficientPermissions()291     boolean serviceHasSufficientPermissions() {
292         final String attentionPackage = mPackageManager.getAttentionServicePackageName();
293         return attentionPackage != null && mPackageManager.checkPermission(
294                 Manifest.permission.CAMERA, attentionPackage)
295                 == PackageManager.PERMISSION_GRANTED;
296     }
297 
dump(PrintWriter pw)298     public void dump(PrintWriter pw) {
299         pw.println("AttentionDetector:");
300         pw.println(" mMaximumExtensionMillis=" + mMaximumExtensionMillis);
301         pw.println(" mMaxAttentionApiTimeoutMillis=" + mMaxAttentionApiTimeoutMillis);
302         pw.println(" mLastUserActivityTime(excludingAttention)=" + mLastUserActivityTime);
303         pw.println(" mAttentionServiceSupported=" + isAttentionServiceSupported());
304         pw.println(" mRequested=" + mRequested);
305     }
306 
307     @VisibleForTesting
308     final class AttentionCallbackInternalImpl extends AttentionCallbackInternal {
309         private final int mId;
310 
AttentionCallbackInternalImpl(int id)311         AttentionCallbackInternalImpl(int id) {
312             this.mId = id;
313         }
314 
315         @Override
onSuccess(int result, long timestamp)316         public void onSuccess(int result, long timestamp) {
317             Slog.v(TAG, "onSuccess: " + result + ", ID: " + mId);
318             // If we don't check for request ID it's possible to get into a loop: success leads
319             // to the onUserAttention(), which in turn triggers updateUserActivity(), which will
320             // call back onSuccess() instantaneously if there is a cached value, and circle repeats.
321             if (mId == mRequestId && mRequested.getAndSet(false)) {
322                 synchronized (mLock) {
323                     if (mWakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE) {
324                         if (DEBUG) Slog.d(TAG, "Device slept before receiving callback.");
325                         return;
326                     }
327                     if (result == AttentionService.ATTENTION_SUCCESS_PRESENT) {
328                         mOnUserAttention.run();
329                     } else {
330                         resetConsecutiveExtensionCount();
331                     }
332                 }
333             }
334         }
335 
336         @Override
onFailure(int error)337         public void onFailure(int error) {
338             Slog.i(TAG, "Failed to check attention: " + error + ", ID: " + mId);
339             mRequested.set(false);
340         }
341     }
342 
343     private final class UserSwitchObserver extends SynchronousUserSwitchObserver {
344         @Override
onUserSwitching(int newUserId)345         public void onUserSwitching(int newUserId) throws RemoteException {
346             updateEnabledFromSettings(mContext);
347         }
348     }
349 }
350