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