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