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