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.attention; 18 19 import static android.content.Context.BIND_AUTO_CREATE; 20 import static android.content.Context.BIND_FOREGROUND_SERVICE; 21 import static android.content.Context.BIND_INCLUDE_CAPABILITIES; 22 import static android.provider.DeviceConfig.NAMESPACE_ATTENTION_MANAGER_SERVICE; 23 import static android.service.attention.AttentionService.ATTENTION_FAILURE_CANCELLED; 24 import static android.service.attention.AttentionService.ATTENTION_FAILURE_UNKNOWN; 25 26 import android.Manifest; 27 import android.annotation.NonNull; 28 import android.annotation.Nullable; 29 import android.annotation.UserIdInt; 30 import android.attention.AttentionManagerInternal; 31 import android.attention.AttentionManagerInternal.AttentionCallbackInternal; 32 import android.content.BroadcastReceiver; 33 import android.content.ComponentName; 34 import android.content.Context; 35 import android.content.Intent; 36 import android.content.IntentFilter; 37 import android.content.ServiceConnection; 38 import android.content.pm.PackageManager; 39 import android.content.pm.ResolveInfo; 40 import android.content.pm.ServiceInfo; 41 import android.os.Binder; 42 import android.os.Handler; 43 import android.os.IBinder; 44 import android.os.Looper; 45 import android.os.Message; 46 import android.os.PowerManager; 47 import android.os.RemoteException; 48 import android.os.ResultReceiver; 49 import android.os.ShellCallback; 50 import android.os.ShellCommand; 51 import android.os.SystemClock; 52 import android.os.UserHandle; 53 import android.provider.DeviceConfig; 54 import android.service.attention.AttentionService; 55 import android.service.attention.AttentionService.AttentionFailureCodes; 56 import android.service.attention.AttentionService.AttentionSuccessCodes; 57 import android.service.attention.IAttentionCallback; 58 import android.service.attention.IAttentionService; 59 import android.text.TextUtils; 60 import android.util.Slog; 61 import android.util.SparseArray; 62 63 import com.android.internal.annotations.GuardedBy; 64 import com.android.internal.annotations.VisibleForTesting; 65 import com.android.internal.util.DumpUtils; 66 import com.android.internal.util.FrameworkStatsLog; 67 import com.android.internal.util.IndentingPrintWriter; 68 import com.android.server.SystemService; 69 70 import java.io.FileDescriptor; 71 import java.io.PrintWriter; 72 import java.util.Objects; 73 74 /** 75 * An attention service implementation that runs in System Server process. 76 * This service publishes a LocalService and reroutes calls to a {@link AttentionService} that it 77 * manages. 78 */ 79 public class AttentionManagerService extends SystemService { 80 private static final String LOG_TAG = "AttentionManagerService"; 81 private static final boolean DEBUG = false; 82 83 /** Service will unbind if connection is not used for that amount of time. */ 84 private static final long CONNECTION_TTL_MILLIS = 60_000; 85 86 /** DeviceConfig flag name, if {@code true}, enables AttentionManagerService features. */ 87 private static final String KEY_SERVICE_ENABLED = "service_enabled"; 88 89 /** Default value in absence of {@link DeviceConfig} override. */ 90 private static final boolean DEFAULT_SERVICE_ENABLED = true; 91 92 /** 93 * DeviceConfig flag name, describes how much time we consider a result fresh; if the check 94 * attention called within that period - cached value will be returned. 95 */ 96 @VisibleForTesting static final String KEY_STALE_AFTER_MILLIS = "stale_after_millis"; 97 98 /** Default value in absence of {@link DeviceConfig} override. */ 99 @VisibleForTesting static final long DEFAULT_STALE_AFTER_MILLIS = 1_000; 100 101 /** The size of the buffer that stores recent attention check results. */ 102 @VisibleForTesting 103 protected static final int ATTENTION_CACHE_BUFFER_SIZE = 5; 104 105 private static String sTestAttentionServicePackage; 106 private final Context mContext; 107 private final PowerManager mPowerManager; 108 private final Object mLock; 109 @GuardedBy("mLock") 110 private final SparseArray<UserState> mUserStates = new SparseArray<>(); 111 private AttentionHandler mAttentionHandler; 112 113 @VisibleForTesting 114 ComponentName mComponentName; 115 AttentionManagerService(Context context)116 public AttentionManagerService(Context context) { 117 this(context, (PowerManager) context.getSystemService(Context.POWER_SERVICE), 118 new Object(), null); 119 mAttentionHandler = new AttentionHandler(); 120 } 121 122 @VisibleForTesting AttentionManagerService(Context context, PowerManager powerManager, Object lock, AttentionHandler handler)123 AttentionManagerService(Context context, PowerManager powerManager, Object lock, 124 AttentionHandler handler) { 125 super(context); 126 mContext = Objects.requireNonNull(context); 127 mPowerManager = powerManager; 128 mLock = lock; 129 mAttentionHandler = handler; 130 } 131 132 @Override onBootPhase(int phase)133 public void onBootPhase(int phase) { 134 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { 135 mContext.registerReceiver(new ScreenStateReceiver(), 136 new IntentFilter(Intent.ACTION_SCREEN_OFF)); 137 } 138 } 139 140 @Override onStart()141 public void onStart() { 142 publishBinderService(Context.ATTENTION_SERVICE, new BinderService()); 143 publishLocalService(AttentionManagerInternal.class, new LocalService()); 144 } 145 146 @Override onSwitchUser(int userId)147 public void onSwitchUser(int userId) { 148 cancelAndUnbindLocked(peekUserStateLocked(userId)); 149 } 150 151 /** Returns {@code true} if attention service is configured on this device. */ isServiceConfigured(Context context)152 public static boolean isServiceConfigured(Context context) { 153 return !TextUtils.isEmpty(getServiceConfigPackage(context)); 154 } 155 156 /** Resolves and sets up the attention service if it had not been done yet. */ isServiceAvailable()157 private boolean isServiceAvailable() { 158 if (mComponentName == null) { 159 mComponentName = resolveAttentionService(mContext); 160 } 161 return mComponentName != null; 162 } 163 164 /** 165 * Returns {@code true} if attention service is supported on this device. 166 */ isAttentionServiceSupported()167 private boolean isAttentionServiceSupported() { 168 return isServiceEnabled() && isServiceConfigured(mContext); 169 } 170 171 @VisibleForTesting isServiceEnabled()172 protected boolean isServiceEnabled() { 173 return DeviceConfig.getBoolean(NAMESPACE_ATTENTION_MANAGER_SERVICE, KEY_SERVICE_ENABLED, 174 DEFAULT_SERVICE_ENABLED); 175 } 176 177 /** 178 * How much time we consider a result fresh; if the check attention called within that period - 179 * cached value will be returned. 180 */ 181 @VisibleForTesting getStaleAfterMillis()182 protected long getStaleAfterMillis() { 183 final long millis = DeviceConfig.getLong(NAMESPACE_ATTENTION_MANAGER_SERVICE, 184 KEY_STALE_AFTER_MILLIS, 185 DEFAULT_STALE_AFTER_MILLIS); 186 187 if (millis < 0 || millis > 10_000) { 188 Slog.w(LOG_TAG, "Bad flag value supplied for: " + KEY_STALE_AFTER_MILLIS); 189 return DEFAULT_STALE_AFTER_MILLIS; 190 } 191 192 return millis; 193 } 194 195 /** 196 * Checks whether user attention is at the screen and calls in the provided callback. 197 * 198 * Calling this multiple times quickly in a row will result in either a) returning a cached 199 * value, if present, or b) returning {@code false} because only one active request at a time is 200 * allowed. 201 * 202 * @return {@code true} if the framework was able to dispatch the request 203 */ 204 @VisibleForTesting checkAttention(long timeout, AttentionCallbackInternal callbackInternal)205 boolean checkAttention(long timeout, AttentionCallbackInternal callbackInternal) { 206 Objects.requireNonNull(callbackInternal); 207 208 if (!isAttentionServiceSupported()) { 209 Slog.w(LOG_TAG, "Trying to call checkAttention() on an unsupported device."); 210 return false; 211 } 212 213 if (!isServiceAvailable()) { 214 Slog.w(LOG_TAG, "Service is not available at this moment."); 215 return false; 216 } 217 218 // don't allow attention check in screen off state 219 if (!mPowerManager.isInteractive()) { 220 return false; 221 } 222 223 synchronized (mLock) { 224 final long now = SystemClock.uptimeMillis(); 225 // schedule shutting down the connection if no one resets this timer 226 freeIfInactiveLocked(); 227 228 final UserState userState = getOrCreateCurrentUserStateLocked(); 229 // lazily start the service, which should be very lightweight to start 230 userState.bindLocked(); 231 232 // throttle frequent requests 233 final AttentionCheckCache cache = userState.mAttentionCheckCacheBuffer == null ? null 234 : userState.mAttentionCheckCacheBuffer.getLast(); 235 if (cache != null && now < cache.mLastComputed + getStaleAfterMillis()) { 236 callbackInternal.onSuccess(cache.mResult, cache.mTimestamp); 237 return true; 238 } 239 240 // prevent spamming with multiple requests, only one at a time is allowed 241 if (userState.mCurrentAttentionCheck != null) { 242 if (!userState.mCurrentAttentionCheck.mIsDispatched 243 || !userState.mCurrentAttentionCheck.mIsFulfilled) { 244 return false; 245 } 246 } 247 248 userState.mCurrentAttentionCheck = createAttentionCheck(callbackInternal, userState); 249 250 if (userState.mService != null) { 251 try { 252 // schedule request cancellation if not returned by that point yet 253 cancelAfterTimeoutLocked(timeout); 254 userState.mService.checkAttention( 255 userState.mCurrentAttentionCheck.mIAttentionCallback); 256 userState.mCurrentAttentionCheck.mIsDispatched = true; 257 } catch (RemoteException e) { 258 Slog.e(LOG_TAG, "Cannot call into the AttentionService"); 259 return false; 260 } 261 } 262 return true; 263 } 264 } 265 createAttentionCheck(AttentionCallbackInternal callbackInternal, UserState userState)266 private AttentionCheck createAttentionCheck(AttentionCallbackInternal callbackInternal, 267 UserState userState) { 268 final IAttentionCallback iAttentionCallback = new IAttentionCallback.Stub() { 269 @Override 270 public void onSuccess(@AttentionSuccessCodes int result, long timestamp) { 271 if (userState.mCurrentAttentionCheck.mIsFulfilled) { 272 return; 273 } 274 userState.mCurrentAttentionCheck.mIsFulfilled = true; 275 callbackInternal.onSuccess(result, timestamp); 276 logStats(result); 277 synchronized (mLock) { 278 if (userState.mAttentionCheckCacheBuffer == null) { 279 userState.mAttentionCheckCacheBuffer = new AttentionCheckCacheBuffer(); 280 } 281 userState.mAttentionCheckCacheBuffer.add( 282 new AttentionCheckCache(SystemClock.uptimeMillis(), result, timestamp)); 283 } 284 } 285 286 @Override 287 public void onFailure(@AttentionFailureCodes int error) { 288 if (userState.mCurrentAttentionCheck.mIsFulfilled) { 289 return; 290 } 291 userState.mCurrentAttentionCheck.mIsFulfilled = true; 292 callbackInternal.onFailure(error); 293 logStats(error); 294 } 295 296 private void logStats(int result) { 297 FrameworkStatsLog.write( 298 FrameworkStatsLog.ATTENTION_MANAGER_SERVICE_RESULT_REPORTED, 299 result); 300 } 301 }; 302 303 return new AttentionCheck(callbackInternal, iAttentionCallback); 304 } 305 306 /** Cancels the specified attention check. */ 307 @VisibleForTesting cancelAttentionCheck(AttentionCallbackInternal callbackInternal)308 void cancelAttentionCheck(AttentionCallbackInternal callbackInternal) { 309 synchronized (mLock) { 310 final UserState userState = peekCurrentUserStateLocked(); 311 if (userState == null) { 312 return; 313 } 314 if (!userState.mCurrentAttentionCheck.mCallbackInternal.equals(callbackInternal)) { 315 Slog.w(LOG_TAG, "Cannot cancel a non-current request"); 316 return; 317 } 318 cancel(userState); 319 } 320 } 321 322 @GuardedBy("mLock") 323 @VisibleForTesting freeIfInactiveLocked()324 protected void freeIfInactiveLocked() { 325 // If we are called here, it means someone used the API again - reset the timer then. 326 mAttentionHandler.removeMessages(AttentionHandler.CHECK_CONNECTION_EXPIRATION); 327 328 // Schedule resources cleanup if no one calls the API again. 329 mAttentionHandler.sendEmptyMessageDelayed(AttentionHandler.CHECK_CONNECTION_EXPIRATION, 330 CONNECTION_TTL_MILLIS); 331 } 332 333 @GuardedBy("mLock") cancelAfterTimeoutLocked(long timeout)334 private void cancelAfterTimeoutLocked(long timeout) { 335 mAttentionHandler.sendEmptyMessageDelayed(AttentionHandler.ATTENTION_CHECK_TIMEOUT, 336 timeout); 337 } 338 339 340 @GuardedBy("mLock") 341 @VisibleForTesting getOrCreateCurrentUserStateLocked()342 protected UserState getOrCreateCurrentUserStateLocked() { 343 // Doesn't need to cache the states of different users. 344 return getOrCreateUserStateLocked(0); 345 } 346 347 @GuardedBy("mLock") 348 @VisibleForTesting getOrCreateUserStateLocked(int userId)349 protected UserState getOrCreateUserStateLocked(int userId) { 350 UserState result = mUserStates.get(userId); 351 if (result == null) { 352 result = new UserState(userId, mContext, mLock, mAttentionHandler, mComponentName); 353 mUserStates.put(userId, result); 354 } 355 return result; 356 } 357 358 @GuardedBy("mLock") 359 @Nullable 360 @VisibleForTesting peekCurrentUserStateLocked()361 protected UserState peekCurrentUserStateLocked() { 362 // Doesn't need to cache the states of different users. 363 return peekUserStateLocked(0); 364 } 365 366 @GuardedBy("mLock") 367 @Nullable peekUserStateLocked(int userId)368 private UserState peekUserStateLocked(int userId) { 369 return mUserStates.get(userId); 370 } 371 getServiceConfigPackage(Context context)372 private static String getServiceConfigPackage(Context context) { 373 return context.getPackageManager().getAttentionServicePackageName(); 374 } 375 376 /** 377 * Provides attention service component name at runtime, making sure it's provided by the 378 * system. 379 */ resolveAttentionService(Context context)380 private static ComponentName resolveAttentionService(Context context) { 381 final String serviceConfigPackage = getServiceConfigPackage(context); 382 383 String resolvedPackage; 384 int flags = PackageManager.MATCH_SYSTEM_ONLY; 385 if (!TextUtils.isEmpty(sTestAttentionServicePackage)) { 386 resolvedPackage = sTestAttentionServicePackage; 387 flags = PackageManager.GET_META_DATA; 388 } else if (!TextUtils.isEmpty(serviceConfigPackage)) { 389 resolvedPackage = serviceConfigPackage; 390 } else { 391 return null; 392 } 393 394 final Intent intent = new Intent(AttentionService.SERVICE_INTERFACE).setPackage( 395 resolvedPackage); 396 397 final ResolveInfo resolveInfo = context.getPackageManager().resolveService(intent, flags); 398 if (resolveInfo == null || resolveInfo.serviceInfo == null) { 399 Slog.wtf(LOG_TAG, String.format("Service %s not found in package %s", 400 AttentionService.SERVICE_INTERFACE, serviceConfigPackage 401 )); 402 return null; 403 } 404 405 final ServiceInfo serviceInfo = resolveInfo.serviceInfo; 406 final String permission = serviceInfo.permission; 407 if (Manifest.permission.BIND_ATTENTION_SERVICE.equals(permission)) { 408 return serviceInfo.getComponentName(); 409 } 410 Slog.e(LOG_TAG, String.format( 411 "Service %s should require %s permission. Found %s permission", 412 serviceInfo.getComponentName(), 413 Manifest.permission.BIND_ATTENTION_SERVICE, 414 serviceInfo.permission)); 415 return null; 416 } 417 dumpInternal(IndentingPrintWriter ipw)418 private void dumpInternal(IndentingPrintWriter ipw) { 419 ipw.println("Attention Manager Service (dumpsys attention) state:\n"); 420 ipw.println("isServiceEnabled=" + isServiceEnabled()); 421 ipw.println("AttentionServicePackageName=" + getServiceConfigPackage(mContext)); 422 ipw.println("Resolved component:"); 423 if (mComponentName != null) { 424 ipw.increaseIndent(); 425 ipw.println("Component=" + mComponentName.getPackageName()); 426 ipw.println("Class=" + mComponentName.getClassName()); 427 ipw.decreaseIndent(); 428 } 429 430 synchronized (mLock) { 431 int size = mUserStates.size(); 432 ipw.print("Number user states: "); 433 ipw.println(size); 434 if (size > 0) { 435 ipw.increaseIndent(); 436 for (int i = 0; i < size; i++) { 437 UserState userState = mUserStates.valueAt(i); 438 ipw.print(i); 439 ipw.print(":"); 440 userState.dump(ipw); 441 ipw.println(); 442 } 443 ipw.decreaseIndent(); 444 } 445 } 446 } 447 448 private final class LocalService extends AttentionManagerInternal { 449 @Override isAttentionServiceSupported()450 public boolean isAttentionServiceSupported() { 451 return AttentionManagerService.this.isAttentionServiceSupported(); 452 } 453 454 @Override checkAttention(long timeout, AttentionCallbackInternal callbackInternal)455 public boolean checkAttention(long timeout, AttentionCallbackInternal callbackInternal) { 456 return AttentionManagerService.this.checkAttention(timeout, callbackInternal); 457 } 458 459 @Override cancelAttentionCheck(AttentionCallbackInternal callbackInternal)460 public void cancelAttentionCheck(AttentionCallbackInternal callbackInternal) { 461 AttentionManagerService.this.cancelAttentionCheck(callbackInternal); 462 } 463 } 464 465 @VisibleForTesting 466 protected static final class AttentionCheckCacheBuffer { 467 private final AttentionCheckCache[] mQueue; 468 private int mStartIndex; 469 private int mSize; 470 AttentionCheckCacheBuffer()471 AttentionCheckCacheBuffer() { 472 mQueue = new AttentionCheckCache[ATTENTION_CACHE_BUFFER_SIZE]; 473 mStartIndex = 0; 474 mSize = 0; 475 } 476 getLast()477 public AttentionCheckCache getLast() { 478 int lastIdx = (mStartIndex + mSize - 1) % ATTENTION_CACHE_BUFFER_SIZE; 479 return mSize == 0 ? null : mQueue[lastIdx]; 480 } 481 add(@onNull AttentionCheckCache cache)482 public void add(@NonNull AttentionCheckCache cache) { 483 int nextIndex = (mStartIndex + mSize) % ATTENTION_CACHE_BUFFER_SIZE; 484 mQueue[nextIndex] = cache; 485 if (mSize == ATTENTION_CACHE_BUFFER_SIZE) { 486 mStartIndex++; 487 } else { 488 mSize++; 489 } 490 } 491 get(int offset)492 public AttentionCheckCache get(int offset) { 493 return offset >= mSize ? null 494 : mQueue[(mStartIndex + offset) % ATTENTION_CACHE_BUFFER_SIZE]; 495 } 496 } 497 498 @VisibleForTesting 499 protected static final class AttentionCheckCache { 500 private final long mLastComputed; 501 private final int mResult; 502 private final long mTimestamp; 503 AttentionCheckCache(long lastComputed, @AttentionService.AttentionSuccessCodes int result, long timestamp)504 AttentionCheckCache(long lastComputed, @AttentionService.AttentionSuccessCodes int result, 505 long timestamp) { 506 mLastComputed = lastComputed; 507 mResult = result; 508 mTimestamp = timestamp; 509 } 510 } 511 512 @VisibleForTesting 513 static final class AttentionCheck { 514 private final AttentionCallbackInternal mCallbackInternal; 515 private final IAttentionCallback mIAttentionCallback; 516 private boolean mIsDispatched; 517 private boolean mIsFulfilled; 518 AttentionCheck(AttentionCallbackInternal callbackInternal, IAttentionCallback iAttentionCallback)519 AttentionCheck(AttentionCallbackInternal callbackInternal, 520 IAttentionCallback iAttentionCallback) { 521 mCallbackInternal = callbackInternal; 522 mIAttentionCallback = iAttentionCallback; 523 } 524 cancelInternal()525 void cancelInternal() { 526 mIsFulfilled = true; 527 mCallbackInternal.onFailure(ATTENTION_FAILURE_CANCELLED); 528 } 529 } 530 531 @VisibleForTesting 532 protected static class UserState { 533 private final ComponentName mComponentName; 534 private final AttentionServiceConnection mConnection = new AttentionServiceConnection(); 535 536 @GuardedBy("mLock") 537 IAttentionService mService; 538 @GuardedBy("mLock") 539 AttentionCheck mCurrentAttentionCheck; 540 @GuardedBy("mLock") 541 AttentionCheckCacheBuffer mAttentionCheckCacheBuffer; 542 @GuardedBy("mLock") 543 private boolean mBinding; 544 545 @UserIdInt 546 private final int mUserId; 547 private final Context mContext; 548 private final Object mLock; 549 private final Handler mAttentionHandler; 550 UserState(int userId, Context context, Object lock, Handler handler, ComponentName componentName)551 UserState(int userId, Context context, Object lock, Handler handler, 552 ComponentName componentName) { 553 mUserId = userId; 554 mContext = Objects.requireNonNull(context); 555 mLock = Objects.requireNonNull(lock); 556 mComponentName = Objects.requireNonNull(componentName); 557 mAttentionHandler = handler; 558 } 559 560 @GuardedBy("mLock") handlePendingCallbackLocked()561 private void handlePendingCallbackLocked() { 562 if (!mCurrentAttentionCheck.mIsDispatched) { 563 if (mService != null) { 564 try { 565 mService.checkAttention(mCurrentAttentionCheck.mIAttentionCallback); 566 mCurrentAttentionCheck.mIsDispatched = true; 567 } catch (RemoteException e) { 568 Slog.e(LOG_TAG, "Cannot call into the AttentionService"); 569 } 570 } else { 571 mCurrentAttentionCheck.mCallbackInternal.onFailure(ATTENTION_FAILURE_UNKNOWN); 572 } 573 } 574 } 575 576 /** Binds to the system's AttentionService which provides an actual implementation. */ 577 @GuardedBy("mLock") bindLocked()578 private void bindLocked() { 579 // No need to bind if service is binding or has already been bound. 580 if (mBinding || mService != null) { 581 return; 582 } 583 584 mBinding = true; 585 // mContext.bindServiceAsUser() calls into ActivityManagerService which it may already 586 // hold the lock and had called into PowerManagerService, which holds a lock. 587 // That would create a deadlock. To solve that, putting it on a handler. 588 mAttentionHandler.post(() -> { 589 final Intent serviceIntent = new Intent( 590 AttentionService.SERVICE_INTERFACE).setComponent( 591 mComponentName); 592 // Note: no reason to clear the calling identity, we won't have one in a handler. 593 mContext.bindServiceAsUser(serviceIntent, mConnection, 594 BIND_AUTO_CREATE | BIND_FOREGROUND_SERVICE | BIND_INCLUDE_CAPABILITIES, 595 UserHandle.CURRENT); 596 }); 597 } 598 dump(IndentingPrintWriter pw)599 private void dump(IndentingPrintWriter pw) { 600 pw.println("userId=" + mUserId); 601 synchronized (mLock) { 602 pw.println("binding=" + mBinding); 603 pw.println("current attention check:"); 604 if (mCurrentAttentionCheck != null) { 605 pw.increaseIndent(); 606 pw.println("is dispatched=" + mCurrentAttentionCheck.mIsDispatched); 607 pw.println("is fulfilled:=" + mCurrentAttentionCheck.mIsFulfilled); 608 pw.decreaseIndent(); 609 } 610 if (mAttentionCheckCacheBuffer != null) { 611 pw.println("attention check cache:"); 612 for (int i = 0; i < mAttentionCheckCacheBuffer.mSize; i++) { 613 pw.increaseIndent(); 614 pw.println("timestamp=" + mAttentionCheckCacheBuffer.get(i).mTimestamp); 615 pw.println("result=" + mAttentionCheckCacheBuffer.get(i).mResult); 616 pw.decreaseIndent(); 617 } 618 } 619 } 620 } 621 622 private class AttentionServiceConnection implements ServiceConnection { 623 @Override onServiceConnected(ComponentName name, IBinder service)624 public void onServiceConnected(ComponentName name, IBinder service) { 625 init(IAttentionService.Stub.asInterface(service)); 626 } 627 628 @Override onServiceDisconnected(ComponentName name)629 public void onServiceDisconnected(ComponentName name) { 630 cleanupService(); 631 } 632 633 @Override onBindingDied(ComponentName name)634 public void onBindingDied(ComponentName name) { 635 cleanupService(); 636 } 637 638 @Override onNullBinding(ComponentName name)639 public void onNullBinding(ComponentName name) { 640 cleanupService(); 641 } 642 cleanupService()643 void cleanupService() { 644 init(null); 645 } 646 init(@ullable IAttentionService service)647 private void init(@Nullable IAttentionService service) { 648 synchronized (mLock) { 649 mService = service; 650 mBinding = false; 651 handlePendingCallbackLocked(); 652 } 653 } 654 } 655 } 656 657 @VisibleForTesting 658 protected class AttentionHandler extends Handler { 659 private static final int CHECK_CONNECTION_EXPIRATION = 1; 660 private static final int ATTENTION_CHECK_TIMEOUT = 2; 661 AttentionHandler()662 AttentionHandler() { 663 super(Looper.myLooper()); 664 } 665 666 @Override handleMessage(Message msg)667 public void handleMessage(Message msg) { 668 switch (msg.what) { 669 // Do not occupy resources when not in use - unbind proactively. 670 case CHECK_CONNECTION_EXPIRATION: { 671 for (int i = 0; i < mUserStates.size(); i++) { 672 cancelAndUnbindLocked(mUserStates.valueAt(i)); 673 } 674 } 675 break; 676 677 // Callee is no longer interested in the attention check result - cancel. 678 case ATTENTION_CHECK_TIMEOUT: { 679 synchronized (mLock) { 680 cancel(peekCurrentUserStateLocked()); 681 } 682 } 683 break; 684 685 default: 686 break; 687 } 688 } 689 } 690 691 @VisibleForTesting cancel(UserState userState)692 void cancel(UserState userState) { 693 if (userState == null || userState.mCurrentAttentionCheck == null) { 694 return; 695 } 696 697 if (userState.mCurrentAttentionCheck.mIsFulfilled) { 698 if (DEBUG) { 699 Slog.d(LOG_TAG, "Trying to cancel the check that has been already fulfilled."); 700 } 701 return; 702 } 703 704 if (userState.mService == null) { 705 userState.mCurrentAttentionCheck.cancelInternal(); 706 return; 707 } 708 709 try { 710 userState.mService.cancelAttentionCheck( 711 userState.mCurrentAttentionCheck.mIAttentionCallback); 712 } catch (RemoteException e) { 713 Slog.e(LOG_TAG, "Unable to cancel attention check"); 714 userState.mCurrentAttentionCheck.cancelInternal(); 715 } 716 } 717 718 @GuardedBy("mLock") cancelAndUnbindLocked(UserState userState)719 private void cancelAndUnbindLocked(UserState userState) { 720 synchronized (mLock) { 721 if (userState == null) { 722 return; 723 } 724 725 cancel(userState); 726 727 if (userState.mService == null) { 728 return; 729 } 730 731 mAttentionHandler.post(() -> mContext.unbindService(userState.mConnection)); 732 // Note: this will set mBinding to false even though it could still be trying to bind 733 // (i.e. the runnable was posted in bindLocked but then cancelAndUnbindLocked was 734 // called before it's run yet). This is a safe state at the moment, 735 // since it will eventually, but feels like a source for confusion down the road and 736 // may cause some expensive and unnecessary work to be done. 737 userState.mConnection.cleanupService(); 738 mUserStates.remove(userState.mUserId); 739 } 740 } 741 742 /** 743 * Unbinds and stops the service when the screen off intent is received. 744 * Attention service only makes sense when screen is ON; disconnect and stop service otherwise. 745 */ 746 private final class ScreenStateReceiver extends BroadcastReceiver { 747 @Override onReceive(Context context, Intent intent)748 public void onReceive(Context context, Intent intent) { 749 if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) { 750 cancelAndUnbindLocked(peekCurrentUserStateLocked()); 751 } 752 } 753 } 754 755 private final class AttentionManagerServiceShellCommand extends ShellCommand { 756 class TestableAttentionCallbackInternal extends AttentionCallbackInternal { 757 private int mLastCallbackCode = -1; 758 759 @Override onSuccess(int result, long timestamp)760 public void onSuccess(int result, long timestamp) { 761 mLastCallbackCode = result; 762 } 763 764 @Override onFailure(int error)765 public void onFailure(int error) { 766 mLastCallbackCode = error; 767 } 768 reset()769 public void reset() { 770 mLastCallbackCode = -1; 771 } 772 getLastCallbackCode()773 public int getLastCallbackCode() { 774 return mLastCallbackCode; 775 } 776 } 777 778 final TestableAttentionCallbackInternal mTestableAttentionCallback = 779 new TestableAttentionCallbackInternal(); 780 781 @Override onCommand(@ullable final String cmd)782 public int onCommand(@Nullable final String cmd) { 783 if (cmd == null) { 784 return handleDefaultCommands(cmd); 785 } 786 final PrintWriter err = getErrPrintWriter(); 787 try { 788 switch (cmd) { 789 case "getAttentionServiceComponent": 790 return cmdResolveAttentionServiceComponent(); 791 case "call": 792 switch (getNextArgRequired()) { 793 case "checkAttention": 794 return cmdCallCheckAttention(); 795 case "cancelCheckAttention": 796 return cmdCallCancelAttention(); 797 default: 798 throw new IllegalArgumentException("Invalid argument"); 799 } 800 case "setTestableAttentionService": 801 return cmdSetTestableAttentionService(getNextArgRequired()); 802 case "clearTestableAttentionService": 803 return cmdClearTestableAttentionService(); 804 case "getLastTestCallbackCode": 805 return cmdGetLastTestCallbackCode(); 806 default: 807 return handleDefaultCommands(cmd); 808 } 809 } catch (IllegalArgumentException e) { 810 err.println("Error: " + e.getMessage()); 811 } 812 return -1; 813 } 814 cmdSetTestableAttentionService(String testingServicePackage)815 private int cmdSetTestableAttentionService(String testingServicePackage) { 816 final PrintWriter out = getOutPrintWriter(); 817 if (TextUtils.isEmpty(testingServicePackage)) { 818 out.println("false"); 819 } else { 820 sTestAttentionServicePackage = testingServicePackage; 821 resetStates(); 822 out.println(mComponentName != null ? "true" : "false"); 823 } 824 return 0; 825 } 826 cmdClearTestableAttentionService()827 private int cmdClearTestableAttentionService() { 828 sTestAttentionServicePackage = ""; 829 mTestableAttentionCallback.reset(); 830 resetStates(); 831 return 0; 832 } 833 cmdCallCheckAttention()834 private int cmdCallCheckAttention() { 835 final PrintWriter out = getOutPrintWriter(); 836 boolean calledSuccessfully = checkAttention(2000, mTestableAttentionCallback); 837 out.println(calledSuccessfully ? "true" : "false"); 838 return 0; 839 } 840 cmdCallCancelAttention()841 private int cmdCallCancelAttention() { 842 final PrintWriter out = getOutPrintWriter(); 843 cancelAttentionCheck(mTestableAttentionCallback); 844 out.println("true"); 845 return 0; 846 } 847 cmdResolveAttentionServiceComponent()848 private int cmdResolveAttentionServiceComponent() { 849 final PrintWriter out = getOutPrintWriter(); 850 ComponentName resolvedComponent = resolveAttentionService(mContext); 851 out.println(resolvedComponent != null ? resolvedComponent.flattenToShortString() : ""); 852 return 0; 853 } 854 cmdGetLastTestCallbackCode()855 private int cmdGetLastTestCallbackCode() { 856 final PrintWriter out = getOutPrintWriter(); 857 out.println(mTestableAttentionCallback.getLastCallbackCode()); 858 return 0; 859 } 860 resetStates()861 private void resetStates() { 862 mComponentName = resolveAttentionService(mContext); 863 mUserStates.clear(); 864 } 865 866 @Override onHelp()867 public void onHelp() { 868 final PrintWriter out = getOutPrintWriter(); 869 out.println("Attention commands: "); 870 out.println(" setTestableAttentionService <service_package>: Bind to a custom" 871 + " implementation of attention service"); 872 out.println(" ---<service_package>:"); 873 out.println( 874 " := Package containing the Attention Service implementation to bind to"); 875 out.println(" ---returns:"); 876 out.println(" := true, if was bound successfully"); 877 out.println(" := false, if was not bound successfully"); 878 out.println(" clearTestableAttentionService: Undo custom bindings. Revert to previous" 879 + " behavior"); 880 out.println(" getAttentionServiceComponent: Get the current service component string"); 881 out.println(" ---returns:"); 882 out.println(" := If valid, the component string (in shorten form) for the" 883 + " currently bound service."); 884 out.println(" := else, empty string"); 885 out.println(" call checkAttention: Calls check attention"); 886 out.println(" ---returns:"); 887 out.println( 888 " := true, if the call was successfully dispatched to the service " 889 + "implementation." 890 + " (to see the result, call getLastTestCallbackCode)"); 891 out.println(" := false, otherwise"); 892 out.println(" call cancelCheckAttention: Cancels check attention"); 893 out.println(" getLastTestCallbackCode"); 894 out.println(" ---returns:"); 895 out.println( 896 " := An integer, representing the last callback code received from the " 897 + "bounded implementation. If none, it will return -1"); 898 } 899 } 900 901 private final class BinderService extends Binder { 902 AttentionManagerServiceShellCommand mAttentionManagerServiceShellCommand = 903 new AttentionManagerServiceShellCommand(); 904 905 @Override onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver resultReceiver)906 public void onShellCommand(FileDescriptor in, FileDescriptor out, 907 FileDescriptor err, 908 String[] args, ShellCallback callback, 909 ResultReceiver resultReceiver) { 910 mAttentionManagerServiceShellCommand.exec(this, in, out, err, args, callback, 911 resultReceiver); 912 } 913 914 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)915 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 916 if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) { 917 return; 918 } 919 920 dumpInternal(new IndentingPrintWriter(pw, " ")); 921 } 922 } 923 } 924