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