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