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