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 import static android.service.attention.AttentionService.PROXIMITY_UNKNOWN; 26 27 import android.Manifest; 28 import android.annotation.NonNull; 29 import android.annotation.Nullable; 30 import android.app.ActivityThread; 31 import android.attention.AttentionManagerInternal; 32 import android.attention.AttentionManagerInternal.AttentionCallbackInternal; 33 import android.attention.AttentionManagerInternal.ProximityUpdateCallbackInternal; 34 import android.content.BroadcastReceiver; 35 import android.content.ComponentName; 36 import android.content.Context; 37 import android.content.Intent; 38 import android.content.IntentFilter; 39 import android.content.ServiceConnection; 40 import android.content.pm.PackageManager; 41 import android.content.pm.ResolveInfo; 42 import android.content.pm.ServiceInfo; 43 import android.hardware.SensorPrivacyManager; 44 import android.os.Binder; 45 import android.os.Handler; 46 import android.os.IBinder; 47 import android.os.Looper; 48 import android.os.Message; 49 import android.os.PowerManager; 50 import android.os.RemoteException; 51 import android.os.ResultReceiver; 52 import android.os.ShellCallback; 53 import android.os.ShellCommand; 54 import android.os.SystemClock; 55 import android.os.UserHandle; 56 import android.provider.DeviceConfig; 57 import android.service.attention.AttentionService; 58 import android.service.attention.AttentionService.AttentionFailureCodes; 59 import android.service.attention.AttentionService.AttentionSuccessCodes; 60 import android.service.attention.IAttentionCallback; 61 import android.service.attention.IAttentionService; 62 import android.service.attention.IProximityUpdateCallback; 63 import android.text.TextUtils; 64 import android.util.Slog; 65 66 import com.android.internal.annotations.GuardedBy; 67 import com.android.internal.annotations.VisibleForTesting; 68 import com.android.internal.util.DumpUtils; 69 import com.android.internal.util.FrameworkStatsLog; 70 import com.android.internal.util.IndentingPrintWriter; 71 import com.android.server.SystemService; 72 73 import java.io.FileDescriptor; 74 import java.io.PrintWriter; 75 import java.util.Objects; 76 import java.util.Set; 77 import java.util.concurrent.CountDownLatch; 78 import java.util.concurrent.TimeUnit; 79 80 /** 81 * An attention service implementation that runs in System Server process. 82 * This service publishes a LocalService and reroutes calls to a {@link AttentionService} that it 83 * manages. 84 */ 85 public class AttentionManagerService extends SystemService { 86 private static final String LOG_TAG = "AttentionManagerService"; 87 private static final boolean DEBUG = false; 88 89 /** Service will unbind if connection is not used for that amount of time. */ 90 private static final long CONNECTION_TTL_MILLIS = 60_000; 91 92 /** How long AttentionManagerService will wait for service binding after lazy binding. */ 93 private static final long SERVICE_BINDING_WAIT_MILLIS = 1000; 94 95 /** DeviceConfig flag name, if {@code true}, enables AttentionManagerService features. */ 96 @VisibleForTesting 97 static final String KEY_SERVICE_ENABLED = "service_enabled"; 98 99 /** Default value in absence of {@link DeviceConfig} override. */ 100 private static final boolean DEFAULT_SERVICE_ENABLED = true; 101 102 @VisibleForTesting 103 boolean mIsServiceEnabled; 104 105 /** 106 * DeviceConfig flag name, describes how much time we consider a result fresh; if the check 107 * attention called within that period - cached value will be returned. 108 */ 109 @VisibleForTesting 110 static final String KEY_STALE_AFTER_MILLIS = "stale_after_millis"; 111 112 /** Default value in absence of {@link DeviceConfig} override. */ 113 @VisibleForTesting 114 static final long DEFAULT_STALE_AFTER_MILLIS = 1_000; 115 116 @VisibleForTesting 117 long mStaleAfterMillis; 118 119 /** The size of the buffer that stores recent attention check results. */ 120 @VisibleForTesting 121 protected static final int ATTENTION_CACHE_BUFFER_SIZE = 5; 122 123 private final AttentionServiceConnection mConnection = new AttentionServiceConnection(); 124 private static String sTestAttentionServicePackage; 125 private final Context mContext; 126 private final PowerManager mPowerManager; 127 private final SensorPrivacyManager mPrivacyManager; 128 private final Object mLock; 129 @GuardedBy("mLock") 130 @VisibleForTesting 131 protected IAttentionService mService; 132 @GuardedBy("mLock") 133 private AttentionCheckCacheBuffer mAttentionCheckCacheBuffer; 134 @GuardedBy("mLock") 135 private boolean mBinding; 136 private AttentionHandler mAttentionHandler; 137 private CountDownLatch mServiceBindingLatch; 138 139 @VisibleForTesting 140 ComponentName mComponentName; 141 142 @VisibleForTesting 143 @GuardedBy("mLock") 144 AttentionCheck mCurrentAttentionCheck; 145 146 /** 147 * A proxy for relaying proximity information between the Attention Service and the client. 148 * The proxy will be initialized when the client calls onStartProximityUpdates and will be 149 * disabled only when the client calls onStopProximityUpdates. 150 */ 151 @VisibleForTesting 152 @GuardedBy("mLock") 153 ProximityUpdate mCurrentProximityUpdate; 154 AttentionManagerService(Context context)155 public AttentionManagerService(Context context) { 156 this(context, (PowerManager) context.getSystemService(Context.POWER_SERVICE), 157 new Object(), null); 158 mAttentionHandler = new AttentionHandler(); 159 } 160 161 @VisibleForTesting AttentionManagerService(Context context, PowerManager powerManager, Object lock, AttentionHandler handler)162 AttentionManagerService(Context context, PowerManager powerManager, Object lock, 163 AttentionHandler handler) { 164 super(context); 165 mContext = Objects.requireNonNull(context); 166 mPowerManager = powerManager; 167 mLock = lock; 168 mAttentionHandler = handler; 169 mPrivacyManager = SensorPrivacyManager.getInstance(context); 170 mServiceBindingLatch = new CountDownLatch(1); 171 } 172 173 @Override onBootPhase(int phase)174 public void onBootPhase(int phase) { 175 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { 176 mContext.registerReceiver(new ScreenStateReceiver(), 177 new IntentFilter(Intent.ACTION_SCREEN_OFF)); 178 179 readValuesFromDeviceConfig(); 180 DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_ATTENTION_MANAGER_SERVICE, 181 ActivityThread.currentApplication().getMainExecutor(), 182 (properties) -> onDeviceConfigChange(properties.getKeyset())); 183 } 184 } 185 186 @Override onStart()187 public void onStart() { 188 publishBinderService(Context.ATTENTION_SERVICE, new BinderService()); 189 publishLocalService(AttentionManagerInternal.class, new LocalService()); 190 } 191 192 /** Returns {@code true} if attention service is configured on this device. */ isServiceConfigured(Context context)193 public static boolean isServiceConfigured(Context context) { 194 return !TextUtils.isEmpty(getServiceConfigPackage(context)); 195 } 196 197 /** Resolves and sets up the attention service if it had not been done yet. */ 198 @VisibleForTesting isServiceAvailable()199 protected boolean isServiceAvailable() { 200 if (mComponentName == null) { 201 mComponentName = resolveAttentionService(mContext); 202 } 203 return mComponentName != null; 204 } 205 getIsServiceEnabled()206 private boolean getIsServiceEnabled() { 207 return DeviceConfig.getBoolean(NAMESPACE_ATTENTION_MANAGER_SERVICE, KEY_SERVICE_ENABLED, 208 DEFAULT_SERVICE_ENABLED); 209 } 210 211 /** 212 * How much time we consider a result fresh; if the check attention called within that period - 213 * cached value will be returned. 214 */ 215 @VisibleForTesting getStaleAfterMillis()216 protected long getStaleAfterMillis() { 217 final long millis = DeviceConfig.getLong(NAMESPACE_ATTENTION_MANAGER_SERVICE, 218 KEY_STALE_AFTER_MILLIS, 219 DEFAULT_STALE_AFTER_MILLIS); 220 221 if (millis < 0 || millis > 10_000) { 222 Slog.w(LOG_TAG, "Bad flag value supplied for: " + KEY_STALE_AFTER_MILLIS); 223 return DEFAULT_STALE_AFTER_MILLIS; 224 } 225 226 return millis; 227 } 228 onDeviceConfigChange(@onNull Set<String> keys)229 private void onDeviceConfigChange(@NonNull Set<String> keys) { 230 for (String key : keys) { 231 switch (key) { 232 case KEY_SERVICE_ENABLED: 233 case KEY_STALE_AFTER_MILLIS: 234 readValuesFromDeviceConfig(); 235 return; 236 default: 237 Slog.i(LOG_TAG, "Ignoring change on " + key); 238 } 239 } 240 } 241 readValuesFromDeviceConfig()242 private void readValuesFromDeviceConfig() { 243 mIsServiceEnabled = getIsServiceEnabled(); 244 mStaleAfterMillis = getStaleAfterMillis(); 245 246 Slog.i(LOG_TAG, "readValuesFromDeviceConfig():" 247 + "\nmIsServiceEnabled=" + mIsServiceEnabled 248 + "\nmStaleAfterMillis=" + mStaleAfterMillis); 249 } 250 251 /** 252 * Checks whether user attention is at the screen and calls in the provided callback. 253 * 254 * Calling this multiple times quickly in a row will result in either a) returning a cached 255 * value, if present, or b) returning {@code false} because only one active request at a time is 256 * allowed. 257 * 258 * @return {@code true} if the framework was able to dispatch the request 259 */ 260 @VisibleForTesting checkAttention(long timeout, AttentionCallbackInternal callbackInternal)261 boolean checkAttention(long timeout, AttentionCallbackInternal callbackInternal) { 262 Objects.requireNonNull(callbackInternal); 263 264 if (!mIsServiceEnabled) { 265 Slog.w(LOG_TAG, "Trying to call checkAttention() on an unsupported device."); 266 return false; 267 } 268 269 if (!isServiceAvailable()) { 270 Slog.w(LOG_TAG, "Service is not available at this moment."); 271 return false; 272 } 273 274 if (mPrivacyManager.isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA)) { 275 Slog.w(LOG_TAG, "Camera is locked by a toggle."); 276 return false; 277 } 278 279 // don't allow attention check in screen off state or power save mode 280 if (!mPowerManager.isInteractive() || mPowerManager.isPowerSaveMode()) { 281 return false; 282 } 283 284 synchronized (mLock) { 285 // schedule shutting down the connection if no one resets this timer 286 freeIfInactiveLocked(); 287 288 // lazily start the service, which should be very lightweight to start 289 bindLocked(); 290 } 291 final long now = SystemClock.uptimeMillis(); 292 // Proceed when the service binding is complete. 293 awaitServiceBinding(Math.min(SERVICE_BINDING_WAIT_MILLIS, timeout)); 294 synchronized (mLock) { 295 // throttle frequent requests 296 final AttentionCheckCache cache = mAttentionCheckCacheBuffer == null ? null 297 : mAttentionCheckCacheBuffer.getLast(); 298 if (cache != null && now < cache.mLastComputed + mStaleAfterMillis) { 299 callbackInternal.onSuccess(cache.mResult, cache.mTimestamp); 300 return true; 301 } 302 303 // prevent spamming with multiple requests, only one at a time is allowed 304 if (mCurrentAttentionCheck != null) { 305 if (!mCurrentAttentionCheck.mIsDispatched 306 || !mCurrentAttentionCheck.mIsFulfilled) { 307 return false; 308 } 309 } 310 311 mCurrentAttentionCheck = new AttentionCheck(callbackInternal, this); 312 313 if (mService != null) { 314 try { 315 // schedule request cancellation if not returned by that point yet 316 cancelAfterTimeoutLocked(timeout); 317 mService.checkAttention(mCurrentAttentionCheck.mIAttentionCallback); 318 mCurrentAttentionCheck.mIsDispatched = true; 319 } catch (RemoteException e) { 320 Slog.e(LOG_TAG, "Cannot call into the AttentionService"); 321 return false; 322 } 323 } 324 return true; 325 } 326 } 327 328 /** Cancels the specified attention check. */ 329 @VisibleForTesting cancelAttentionCheck(AttentionCallbackInternal callbackInternal)330 void cancelAttentionCheck(AttentionCallbackInternal callbackInternal) { 331 synchronized (mLock) { 332 if (!mCurrentAttentionCheck.mCallbackInternal.equals(callbackInternal)) { 333 Slog.w(LOG_TAG, "Cannot cancel a non-current request"); 334 return; 335 } 336 cancel(); 337 } 338 } 339 340 /** 341 * Requests the continuous updates of proximity signal via the provided callback, 342 * until the given callback is stopped. 343 * 344 * Calling this multiple times for duplicate requests will be no-ops, returning true. 345 * 346 * TODO(b/239130847): Maintain the proximity state in AttentionManagerService and change this 347 * to a polling API. 348 * 349 * @return {@code true} if the framework was able to dispatch the request 350 */ 351 @VisibleForTesting onStartProximityUpdates(ProximityUpdateCallbackInternal callbackInternal)352 boolean onStartProximityUpdates(ProximityUpdateCallbackInternal callbackInternal) { 353 Objects.requireNonNull(callbackInternal); 354 if (!mIsServiceEnabled) { 355 Slog.w(LOG_TAG, "Trying to call onProximityUpdate() on an unsupported device."); 356 return false; 357 } 358 359 if (!isServiceAvailable()) { 360 Slog.w(LOG_TAG, "Service is not available at this moment."); 361 return false; 362 } 363 364 // don't allow proximity request in screen off state. 365 // This behavior might change in the future. 366 if (!mPowerManager.isInteractive()) { 367 Slog.w(LOG_TAG, "Proximity Service is unavailable during screen off at this moment."); 368 return false; 369 } 370 371 synchronized (mLock) { 372 // schedule shutting down the connection if no one resets this timer 373 freeIfInactiveLocked(); 374 375 // lazily start the service, which should be very lightweight to start 376 bindLocked(); 377 } 378 // Proceed when the service binding is complete. 379 awaitServiceBinding(SERVICE_BINDING_WAIT_MILLIS); 380 synchronized (mLock) { 381 /* 382 Prevent spamming with multiple requests, only one at a time is allowed. 383 If there are use-cases for keeping track of multiple requests, we 384 can refactor ProximityUpdate object to keep track of multiple internal callbacks. 385 */ 386 if (mCurrentProximityUpdate != null && mCurrentProximityUpdate.mStartedUpdates) { 387 if (mCurrentProximityUpdate.mCallbackInternal == callbackInternal) { 388 Slog.w(LOG_TAG, "Provided callback is already registered. Skipping."); 389 return true; 390 } else { 391 // reject the new request since the old request is still alive. 392 Slog.w(LOG_TAG, "New proximity update cannot be processed because there is " 393 + "already an ongoing update"); 394 return false; 395 } 396 } 397 mCurrentProximityUpdate = new ProximityUpdate(callbackInternal); 398 return mCurrentProximityUpdate.startUpdates(); 399 } 400 } 401 402 /** Cancels the specified proximity registration. */ 403 @VisibleForTesting onStopProximityUpdates(ProximityUpdateCallbackInternal callbackInternal)404 void onStopProximityUpdates(ProximityUpdateCallbackInternal callbackInternal) { 405 synchronized (mLock) { 406 if (mCurrentProximityUpdate == null 407 || !mCurrentProximityUpdate.mCallbackInternal.equals(callbackInternal) 408 || !mCurrentProximityUpdate.mStartedUpdates) { 409 Slog.w(LOG_TAG, "Cannot stop a non-current callback"); 410 return; 411 } 412 mCurrentProximityUpdate.cancelUpdates(); 413 mCurrentProximityUpdate = null; 414 } 415 } 416 417 @GuardedBy("mLock") 418 @VisibleForTesting freeIfInactiveLocked()419 protected void freeIfInactiveLocked() { 420 // If we are called here, it means someone used the API again - reset the timer then. 421 mAttentionHandler.removeMessages(AttentionHandler.CHECK_CONNECTION_EXPIRATION); 422 423 // Schedule resources cleanup if no one calls the API again. 424 mAttentionHandler.sendEmptyMessageDelayed(AttentionHandler.CHECK_CONNECTION_EXPIRATION, 425 CONNECTION_TTL_MILLIS); 426 } 427 428 @GuardedBy("mLock") cancelAfterTimeoutLocked(long timeout)429 private void cancelAfterTimeoutLocked(long timeout) { 430 mAttentionHandler.sendEmptyMessageDelayed(AttentionHandler.ATTENTION_CHECK_TIMEOUT, 431 timeout); 432 } 433 getServiceConfigPackage(Context context)434 private static String getServiceConfigPackage(Context context) { 435 return context.getPackageManager().getAttentionServicePackageName(); 436 } 437 awaitServiceBinding(long millis)438 private void awaitServiceBinding(long millis) { 439 try { 440 mServiceBindingLatch.await(millis, TimeUnit.MILLISECONDS); 441 } catch (InterruptedException e) { 442 Slog.e(LOG_TAG, "Interrupted while waiting to bind Attention Service.", e); 443 } 444 } 445 446 /** 447 * Provides attention service component name at runtime, making sure it's provided by the 448 * system. 449 */ resolveAttentionService(Context context)450 private static ComponentName resolveAttentionService(Context context) { 451 final String serviceConfigPackage = getServiceConfigPackage(context); 452 453 String resolvedPackage; 454 int flags = PackageManager.MATCH_SYSTEM_ONLY; 455 if (!TextUtils.isEmpty(sTestAttentionServicePackage)) { 456 resolvedPackage = sTestAttentionServicePackage; 457 flags = PackageManager.GET_META_DATA; 458 } else if (!TextUtils.isEmpty(serviceConfigPackage)) { 459 resolvedPackage = serviceConfigPackage; 460 } else { 461 return null; 462 } 463 464 final Intent intent = new Intent(AttentionService.SERVICE_INTERFACE).setPackage( 465 resolvedPackage); 466 467 final ResolveInfo resolveInfo = context.getPackageManager().resolveService(intent, flags); 468 if (resolveInfo == null || resolveInfo.serviceInfo == null) { 469 Slog.wtf(LOG_TAG, String.format("Service %s not found in package %s", 470 AttentionService.SERVICE_INTERFACE, serviceConfigPackage 471 )); 472 return null; 473 } 474 475 final ServiceInfo serviceInfo = resolveInfo.serviceInfo; 476 final String permission = serviceInfo.permission; 477 if (Manifest.permission.BIND_ATTENTION_SERVICE.equals(permission)) { 478 return serviceInfo.getComponentName(); 479 } 480 Slog.e(LOG_TAG, String.format( 481 "Service %s should require %s permission. Found %s permission", 482 serviceInfo.getComponentName(), 483 Manifest.permission.BIND_ATTENTION_SERVICE, 484 serviceInfo.permission)); 485 return null; 486 } 487 dumpInternal(IndentingPrintWriter ipw)488 private void dumpInternal(IndentingPrintWriter ipw) { 489 ipw.println("Attention Manager Service (dumpsys attention) state:\n"); 490 ipw.println("isServiceEnabled=" + mIsServiceEnabled); 491 ipw.println("mStaleAfterMillis=" + mStaleAfterMillis); 492 ipw.println("AttentionServicePackageName=" + getServiceConfigPackage(mContext)); 493 ipw.println("Resolved component:"); 494 if (mComponentName != null) { 495 ipw.increaseIndent(); 496 ipw.println("Component=" + mComponentName.getPackageName()); 497 ipw.println("Class=" + mComponentName.getClassName()); 498 ipw.decreaseIndent(); 499 } 500 synchronized (mLock) { 501 ipw.println("binding=" + mBinding); 502 ipw.println("current attention check:"); 503 if (mCurrentAttentionCheck != null) { 504 mCurrentAttentionCheck.dump(ipw); 505 } 506 if (mAttentionCheckCacheBuffer != null) { 507 mAttentionCheckCacheBuffer.dump(ipw); 508 } 509 if (mCurrentProximityUpdate != null) { 510 mCurrentProximityUpdate.dump(ipw); 511 } 512 } 513 } 514 515 private final class LocalService extends AttentionManagerInternal { 516 @Override isAttentionServiceSupported()517 public boolean isAttentionServiceSupported() { 518 return AttentionManagerService.this.mIsServiceEnabled; 519 } 520 521 @Override checkAttention(long timeout, AttentionCallbackInternal callbackInternal)522 public boolean checkAttention(long timeout, AttentionCallbackInternal callbackInternal) { 523 return AttentionManagerService.this.checkAttention(timeout, callbackInternal); 524 } 525 526 @Override cancelAttentionCheck(AttentionCallbackInternal callbackInternal)527 public void cancelAttentionCheck(AttentionCallbackInternal callbackInternal) { 528 AttentionManagerService.this.cancelAttentionCheck(callbackInternal); 529 } 530 531 @Override onStartProximityUpdates( ProximityUpdateCallbackInternal callback)532 public boolean onStartProximityUpdates( 533 ProximityUpdateCallbackInternal callback) { 534 return AttentionManagerService.this.onStartProximityUpdates(callback); 535 } 536 537 @Override onStopProximityUpdates(ProximityUpdateCallbackInternal callback)538 public void onStopProximityUpdates(ProximityUpdateCallbackInternal callback) { 539 AttentionManagerService.this.onStopProximityUpdates(callback); 540 } 541 } 542 543 @VisibleForTesting 544 protected static final class AttentionCheckCacheBuffer { 545 private final AttentionCheckCache[] mQueue; 546 private int mStartIndex; 547 private int mSize; 548 AttentionCheckCacheBuffer()549 AttentionCheckCacheBuffer() { 550 mQueue = new AttentionCheckCache[ATTENTION_CACHE_BUFFER_SIZE]; 551 mStartIndex = 0; 552 mSize = 0; 553 } 554 getLast()555 public AttentionCheckCache getLast() { 556 int lastIdx = (mStartIndex + mSize - 1) % ATTENTION_CACHE_BUFFER_SIZE; 557 return mSize == 0 ? null : mQueue[lastIdx]; 558 } 559 add(@onNull AttentionCheckCache cache)560 public void add(@NonNull AttentionCheckCache cache) { 561 int nextIndex = (mStartIndex + mSize) % ATTENTION_CACHE_BUFFER_SIZE; 562 mQueue[nextIndex] = cache; 563 if (mSize == ATTENTION_CACHE_BUFFER_SIZE) { 564 mStartIndex++; 565 } else { 566 mSize++; 567 } 568 } 569 get(int offset)570 public AttentionCheckCache get(int offset) { 571 return offset >= mSize ? null 572 : mQueue[(mStartIndex + offset) % ATTENTION_CACHE_BUFFER_SIZE]; 573 } 574 dump(IndentingPrintWriter ipw)575 private void dump(IndentingPrintWriter ipw) { 576 ipw.println("attention check cache:"); 577 AttentionCheckCache cache; 578 for (int i = 0; i < mSize; i++) { 579 cache = get(i); 580 if (cache != null) { 581 ipw.increaseIndent(); 582 ipw.println("timestamp=" + cache.mTimestamp); 583 ipw.println("result=" + cache.mResult); 584 ipw.decreaseIndent(); 585 } 586 } 587 } 588 } 589 590 @VisibleForTesting 591 protected static final class AttentionCheckCache { 592 private final long mLastComputed; 593 private final int mResult; 594 private final long mTimestamp; 595 AttentionCheckCache(long lastComputed, @AttentionService.AttentionSuccessCodes int result, long timestamp)596 AttentionCheckCache(long lastComputed, @AttentionService.AttentionSuccessCodes int result, 597 long timestamp) { 598 mLastComputed = lastComputed; 599 mResult = result; 600 mTimestamp = timestamp; 601 } 602 } 603 604 @VisibleForTesting 605 static final class AttentionCheck { 606 private final AttentionCallbackInternal mCallbackInternal; 607 private final IAttentionCallback mIAttentionCallback; 608 609 private boolean mIsDispatched; 610 private boolean mIsFulfilled; 611 AttentionCheck(AttentionCallbackInternal callbackInternal, AttentionManagerService service)612 AttentionCheck(AttentionCallbackInternal callbackInternal, 613 AttentionManagerService service) { 614 mCallbackInternal = callbackInternal; 615 mIAttentionCallback = new IAttentionCallback.Stub() { 616 @Override 617 public void onSuccess(@AttentionSuccessCodes int result, long timestamp) { 618 if (mIsFulfilled) { 619 return; 620 } 621 mIsFulfilled = true; 622 callbackInternal.onSuccess(result, timestamp); 623 logStats(result); 624 service.appendResultToAttentionCacheBuffer( 625 new AttentionCheckCache(SystemClock.uptimeMillis(), result, 626 timestamp)); 627 } 628 629 @Override 630 public void onFailure(@AttentionFailureCodes int error) { 631 if (mIsFulfilled) { 632 return; 633 } 634 mIsFulfilled = true; 635 callbackInternal.onFailure(error); 636 logStats(error); 637 } 638 639 private void logStats(int result) { 640 FrameworkStatsLog.write( 641 FrameworkStatsLog.ATTENTION_MANAGER_SERVICE_RESULT_REPORTED, 642 result); 643 } 644 }; 645 } 646 cancelInternal()647 void cancelInternal() { 648 mIsFulfilled = true; 649 mCallbackInternal.onFailure(ATTENTION_FAILURE_CANCELLED); 650 } 651 dump(IndentingPrintWriter ipw)652 void dump(IndentingPrintWriter ipw) { 653 ipw.increaseIndent(); 654 ipw.println("is dispatched=" + mIsDispatched); 655 ipw.println("is fulfilled:=" + mIsFulfilled); 656 ipw.decreaseIndent(); 657 } 658 } 659 660 @VisibleForTesting 661 final class ProximityUpdate { 662 private final ProximityUpdateCallbackInternal mCallbackInternal; 663 private final IProximityUpdateCallback mIProximityUpdateCallback; 664 private boolean mStartedUpdates; 665 ProximityUpdate(ProximityUpdateCallbackInternal callbackInternal)666 ProximityUpdate(ProximityUpdateCallbackInternal callbackInternal) { 667 mCallbackInternal = callbackInternal; 668 mIProximityUpdateCallback = new IProximityUpdateCallback.Stub() { 669 @Override 670 public void onProximityUpdate(double distance) { 671 mCallbackInternal.onProximityUpdate(distance); 672 synchronized (mLock) { 673 freeIfInactiveLocked(); 674 } 675 } 676 }; 677 } 678 startUpdates()679 boolean startUpdates() { 680 synchronized (mLock) { 681 if (mStartedUpdates) { 682 Slog.w(LOG_TAG, "Already registered to a proximity service."); 683 return false; 684 } 685 if (mService == null) { 686 Slog.w(LOG_TAG, 687 "There is no service bound. Proximity update request rejected."); 688 return false; 689 } 690 try { 691 mService.onStartProximityUpdates(mIProximityUpdateCallback); 692 mStartedUpdates = true; 693 } catch (RemoteException e) { 694 Slog.e(LOG_TAG, "Cannot call into the AttentionService", e); 695 return false; 696 } 697 } 698 return true; 699 } 700 cancelUpdates()701 void cancelUpdates() { 702 synchronized (mLock) { 703 if (mStartedUpdates) { 704 if (mService == null) { 705 mStartedUpdates = false; 706 return; 707 } 708 try { 709 mService.onStopProximityUpdates(); 710 mStartedUpdates = false; 711 } catch (RemoteException e) { 712 Slog.e(LOG_TAG, "Cannot call into the AttentionService", e); 713 } 714 } 715 } 716 } 717 dump(IndentingPrintWriter ipw)718 void dump(IndentingPrintWriter ipw) { 719 ipw.increaseIndent(); 720 ipw.println("is StartedUpdates=" + mStartedUpdates); 721 ipw.decreaseIndent(); 722 } 723 } 724 appendResultToAttentionCacheBuffer(AttentionCheckCache cache)725 private void appendResultToAttentionCacheBuffer(AttentionCheckCache cache) { 726 synchronized (mLock) { 727 if (mAttentionCheckCacheBuffer == null) { 728 mAttentionCheckCacheBuffer = new AttentionCheckCacheBuffer(); 729 } 730 mAttentionCheckCacheBuffer.add(cache); 731 } 732 } 733 734 private class AttentionServiceConnection implements ServiceConnection { 735 @Override onServiceConnected(ComponentName name, IBinder service)736 public void onServiceConnected(ComponentName name, IBinder service) { 737 init(IAttentionService.Stub.asInterface(service)); 738 mServiceBindingLatch.countDown(); 739 } 740 741 @Override onServiceDisconnected(ComponentName name)742 public void onServiceDisconnected(ComponentName name) { 743 cleanupService(); 744 } 745 746 @Override onBindingDied(ComponentName name)747 public void onBindingDied(ComponentName name) { 748 cleanupService(); 749 } 750 751 @Override onNullBinding(ComponentName name)752 public void onNullBinding(ComponentName name) { 753 cleanupService(); 754 } 755 cleanupService()756 void cleanupService() { 757 init(null); 758 mServiceBindingLatch = new CountDownLatch(1); 759 } 760 init(@ullable IAttentionService service)761 private void init(@Nullable IAttentionService service) { 762 synchronized (mLock) { 763 mService = service; 764 mBinding = false; 765 handlePendingCallbackLocked(); 766 } 767 } 768 } 769 770 @GuardedBy("mLock") handlePendingCallbackLocked()771 private void handlePendingCallbackLocked() { 772 if (mCurrentAttentionCheck != null && !mCurrentAttentionCheck.mIsDispatched) { 773 if (mService != null) { 774 try { 775 mService.checkAttention(mCurrentAttentionCheck.mIAttentionCallback); 776 mCurrentAttentionCheck.mIsDispatched = true; 777 } catch (RemoteException e) { 778 Slog.e(LOG_TAG, "Cannot call into the AttentionService"); 779 } 780 } else { 781 mCurrentAttentionCheck.mCallbackInternal.onFailure(ATTENTION_FAILURE_UNKNOWN); 782 } 783 } 784 if (mCurrentProximityUpdate != null && mCurrentProximityUpdate.mStartedUpdates) { 785 if (mService != null) { 786 try { 787 mService.onStartProximityUpdates( 788 mCurrentProximityUpdate.mIProximityUpdateCallback); 789 } catch (RemoteException e) { 790 Slog.e(LOG_TAG, "Cannot call into the AttentionService", e); 791 } 792 } else { 793 mCurrentProximityUpdate.cancelUpdates(); 794 mCurrentProximityUpdate = null; 795 } 796 } 797 } 798 799 @VisibleForTesting 800 protected class AttentionHandler extends Handler { 801 private static final int CHECK_CONNECTION_EXPIRATION = 1; 802 private static final int ATTENTION_CHECK_TIMEOUT = 2; 803 AttentionHandler()804 AttentionHandler() { 805 super(Looper.myLooper()); 806 } 807 808 @Override handleMessage(Message msg)809 public void handleMessage(Message msg) { 810 switch (msg.what) { 811 // Do not occupy resources when not in use - unbind proactively. 812 case CHECK_CONNECTION_EXPIRATION: { 813 synchronized (mLock) { 814 cancelAndUnbindLocked(); 815 } 816 } 817 break; 818 819 // Callee is no longer interested in the attention check result - cancel. 820 case ATTENTION_CHECK_TIMEOUT: { 821 synchronized (mLock) { 822 cancel(); 823 } 824 } 825 break; 826 827 default: 828 break; 829 } 830 } 831 } 832 833 @VisibleForTesting 834 @GuardedBy("mLock") cancel()835 void cancel() { 836 if (mCurrentAttentionCheck.mIsFulfilled) { 837 if (DEBUG) { 838 Slog.d(LOG_TAG, "Trying to cancel the check that has been already fulfilled."); 839 } 840 return; 841 } 842 843 if (mService == null) { 844 mCurrentAttentionCheck.cancelInternal(); 845 return; 846 } 847 848 try { 849 mService.cancelAttentionCheck(mCurrentAttentionCheck.mIAttentionCallback); 850 } catch (RemoteException e) { 851 Slog.e(LOG_TAG, "Unable to cancel attention check"); 852 mCurrentAttentionCheck.cancelInternal(); 853 } 854 } 855 856 @GuardedBy("mLock") cancelAndUnbindLocked()857 private void cancelAndUnbindLocked() { 858 synchronized (mLock) { 859 if (mCurrentAttentionCheck != null) { 860 cancel(); 861 } 862 if (mCurrentProximityUpdate != null) { 863 mCurrentProximityUpdate.cancelUpdates(); 864 } 865 if (mService == null) { 866 return; 867 } 868 mAttentionHandler.post(() -> mContext.unbindService(mConnection)); 869 // Note: this will set mBinding to false even though it could still be trying to bind 870 // (i.e. the runnable was posted in bindLocked but then cancelAndUnbindLocked was 871 // called before it's run yet). This is a safe state at the moment, 872 // since it will eventually, but feels like a source for confusion down the road and 873 // may cause some expensive and unnecessary work to be done. 874 mConnection.cleanupService(); 875 } 876 } 877 878 /** Binds to the system's AttentionService which provides an actual implementation. */ 879 @GuardedBy("mLock") bindLocked()880 private void bindLocked() { 881 // No need to bind if service is binding or has already been bound. 882 if (mBinding || mService != null) { 883 return; 884 } 885 886 mBinding = true; 887 // mContext.bindServiceAsUser() calls into ActivityManagerService which it may already 888 // hold the lock and had called into PowerManagerService, which holds a lock. 889 // That would create a deadlock. To solve that, putting it on a handler. 890 mAttentionHandler.post(() -> { 891 final Intent serviceIntent = new Intent( 892 AttentionService.SERVICE_INTERFACE).setComponent( 893 mComponentName); 894 // Note: no reason to clear the calling identity, we won't have one in a handler. 895 mContext.bindServiceAsUser(serviceIntent, mConnection, 896 BIND_AUTO_CREATE | BIND_FOREGROUND_SERVICE | BIND_INCLUDE_CAPABILITIES, 897 UserHandle.CURRENT); 898 899 }); 900 } 901 902 /** 903 * Unbinds and stops the service when the screen off intent is received. 904 * Attention service only makes sense when screen is ON; disconnect and stop service otherwise. 905 */ 906 private final class ScreenStateReceiver extends BroadcastReceiver { 907 @Override onReceive(Context context, Intent intent)908 public void onReceive(Context context, Intent intent) { 909 if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) { 910 synchronized (mLock) { 911 cancelAndUnbindLocked(); 912 } 913 } 914 } 915 } 916 917 private final class AttentionManagerServiceShellCommand extends ShellCommand { 918 class TestableAttentionCallbackInternal extends AttentionCallbackInternal { 919 private int mLastCallbackCode = -1; 920 921 @Override onSuccess(int result, long timestamp)922 public void onSuccess(int result, long timestamp) { 923 mLastCallbackCode = result; 924 } 925 926 @Override onFailure(int error)927 public void onFailure(int error) { 928 mLastCallbackCode = error; 929 } 930 reset()931 public void reset() { 932 mLastCallbackCode = -1; 933 } 934 getLastCallbackCode()935 public int getLastCallbackCode() { 936 return mLastCallbackCode; 937 } 938 } 939 940 class TestableProximityUpdateCallbackInternal implements ProximityUpdateCallbackInternal { 941 private double mLastCallbackCode = PROXIMITY_UNKNOWN; 942 943 @Override onProximityUpdate(double distance)944 public void onProximityUpdate(double distance) { 945 mLastCallbackCode = distance; 946 } 947 reset()948 public void reset() { 949 mLastCallbackCode = PROXIMITY_UNKNOWN; 950 } 951 getLastCallbackCode()952 public double getLastCallbackCode() { 953 return mLastCallbackCode; 954 } 955 } 956 957 final TestableAttentionCallbackInternal mTestableAttentionCallback = 958 new TestableAttentionCallbackInternal(); 959 final TestableProximityUpdateCallbackInternal mTestableProximityUpdateCallback = 960 new TestableProximityUpdateCallbackInternal(); 961 962 @Override onCommand(@ullable final String cmd)963 public int onCommand(@Nullable final String cmd) { 964 if (cmd == null) { 965 return handleDefaultCommands(cmd); 966 } 967 final PrintWriter err = getErrPrintWriter(); 968 try { 969 switch (cmd) { 970 case "getAttentionServiceComponent": 971 return cmdResolveAttentionServiceComponent(); 972 case "call": 973 switch (getNextArgRequired()) { 974 case "checkAttention": 975 return cmdCallCheckAttention(); 976 case "cancelCheckAttention": 977 return cmdCallCancelAttention(); 978 case "onStartProximityUpdates": 979 return cmdCallOnStartProximityUpdates(); 980 case "onStopProximityUpdates": 981 return cmdCallOnStopProximityUpdates(); 982 default: 983 throw new IllegalArgumentException("Invalid argument"); 984 } 985 case "setTestableAttentionService": 986 return cmdSetTestableAttentionService(getNextArgRequired()); 987 case "clearTestableAttentionService": 988 return cmdClearTestableAttentionService(); 989 case "getLastTestCallbackCode": 990 return cmdGetLastTestCallbackCode(); 991 case "getLastTestProximityUpdateCallbackCode": 992 return cmdGetLastTestProximityUpdateCallbackCode(); 993 default: 994 return handleDefaultCommands(cmd); 995 } 996 } catch (IllegalArgumentException e) { 997 err.println("Error: " + e.getMessage()); 998 } 999 return -1; 1000 } 1001 cmdSetTestableAttentionService(String testingServicePackage)1002 private int cmdSetTestableAttentionService(String testingServicePackage) { 1003 final PrintWriter out = getOutPrintWriter(); 1004 if (TextUtils.isEmpty(testingServicePackage)) { 1005 out.println("false"); 1006 } else { 1007 sTestAttentionServicePackage = testingServicePackage; 1008 resetStates(); 1009 out.println(mComponentName != null ? "true" : "false"); 1010 } 1011 return 0; 1012 } 1013 cmdClearTestableAttentionService()1014 private int cmdClearTestableAttentionService() { 1015 sTestAttentionServicePackage = ""; 1016 mTestableAttentionCallback.reset(); 1017 mTestableProximityUpdateCallback.reset(); 1018 resetStates(); 1019 return 0; 1020 } 1021 cmdCallCheckAttention()1022 private int cmdCallCheckAttention() { 1023 final PrintWriter out = getOutPrintWriter(); 1024 boolean calledSuccessfully = checkAttention(2000, mTestableAttentionCallback); 1025 out.println(calledSuccessfully ? "true" : "false"); 1026 return 0; 1027 } 1028 cmdCallCancelAttention()1029 private int cmdCallCancelAttention() { 1030 final PrintWriter out = getOutPrintWriter(); 1031 cancelAttentionCheck(mTestableAttentionCallback); 1032 out.println("true"); 1033 return 0; 1034 } 1035 cmdCallOnStartProximityUpdates()1036 private int cmdCallOnStartProximityUpdates() { 1037 final PrintWriter out = getOutPrintWriter(); 1038 boolean calledSuccessfully = onStartProximityUpdates(mTestableProximityUpdateCallback); 1039 out.println(calledSuccessfully ? "true" : "false"); 1040 return 0; 1041 } 1042 cmdCallOnStopProximityUpdates()1043 private int cmdCallOnStopProximityUpdates() { 1044 final PrintWriter out = getOutPrintWriter(); 1045 onStopProximityUpdates(mTestableProximityUpdateCallback); 1046 out.println("true"); 1047 return 0; 1048 } 1049 cmdResolveAttentionServiceComponent()1050 private int cmdResolveAttentionServiceComponent() { 1051 final PrintWriter out = getOutPrintWriter(); 1052 ComponentName resolvedComponent = resolveAttentionService(mContext); 1053 out.println(resolvedComponent != null ? resolvedComponent.flattenToShortString() : ""); 1054 return 0; 1055 } 1056 cmdGetLastTestCallbackCode()1057 private int cmdGetLastTestCallbackCode() { 1058 final PrintWriter out = getOutPrintWriter(); 1059 out.println(mTestableAttentionCallback.getLastCallbackCode()); 1060 return 0; 1061 } 1062 cmdGetLastTestProximityUpdateCallbackCode()1063 private int cmdGetLastTestProximityUpdateCallbackCode() { 1064 final PrintWriter out = getOutPrintWriter(); 1065 out.println(mTestableProximityUpdateCallback.getLastCallbackCode()); 1066 return 0; 1067 } 1068 resetStates()1069 private void resetStates() { 1070 synchronized (mLock) { 1071 mCurrentProximityUpdate = null; 1072 cancelAndUnbindLocked(); 1073 } 1074 mComponentName = resolveAttentionService(mContext); 1075 } 1076 1077 @Override onHelp()1078 public void onHelp() { 1079 final PrintWriter out = getOutPrintWriter(); 1080 out.println("Attention commands: "); 1081 out.println(" setTestableAttentionService <service_package>: Bind to a custom" 1082 + " implementation of attention service"); 1083 out.println(" ---<service_package>:"); 1084 out.println( 1085 " := Package containing the Attention Service implementation to bind to"); 1086 out.println(" ---returns:"); 1087 out.println(" := true, if was bound successfully"); 1088 out.println(" := false, if was not bound successfully"); 1089 out.println(" clearTestableAttentionService: Undo custom bindings. Revert to previous" 1090 + " behavior"); 1091 out.println(" getAttentionServiceComponent: Get the current service component string"); 1092 out.println(" ---returns:"); 1093 out.println(" := If valid, the component string (in shorten form) for the" 1094 + " currently bound service."); 1095 out.println(" := else, empty string"); 1096 out.println(" call checkAttention: Calls check attention"); 1097 out.println(" ---returns:"); 1098 out.println( 1099 " := true, if the call was successfully dispatched to the service " 1100 + "implementation." 1101 + " (to see the result, call getLastTestCallbackCode)"); 1102 out.println(" := false, otherwise"); 1103 out.println(" call cancelCheckAttention: Cancels check attention"); 1104 out.println(" call onStartProximityUpdates: Calls onStartProximityUpdates"); 1105 out.println(" ---returns:"); 1106 out.println( 1107 " := true, if the request was successfully dispatched to the service " 1108 + "implementation." 1109 + " (to see the result, call getLastTestProximityUpdateCallbackCode)"); 1110 out.println(" := false, otherwise"); 1111 out.println(" call onStopProximityUpdates: Cancels proximity updates"); 1112 out.println(" getLastTestCallbackCode"); 1113 out.println(" ---returns:"); 1114 out.println( 1115 " := An integer, representing the last callback code received from the " 1116 + "bounded implementation. If none, it will return -1"); 1117 out.println(" getLastTestProximityUpdateCallbackCode"); 1118 out.println(" ---returns:"); 1119 out.println( 1120 " := A double, representing the last proximity value received from the " 1121 + "bounded implementation. If none, it will return -1.0"); 1122 } 1123 } 1124 1125 private final class BinderService extends Binder { 1126 AttentionManagerServiceShellCommand mAttentionManagerServiceShellCommand = 1127 new AttentionManagerServiceShellCommand(); 1128 1129 @Override onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver resultReceiver)1130 public void onShellCommand(FileDescriptor in, FileDescriptor out, 1131 FileDescriptor err, 1132 String[] args, ShellCallback callback, 1133 ResultReceiver resultReceiver) { 1134 mAttentionManagerServiceShellCommand.exec(this, in, out, err, args, callback, 1135 resultReceiver); 1136 } 1137 1138 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)1139 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1140 if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) { 1141 return; 1142 } 1143 1144 dumpInternal(new IndentingPrintWriter(pw, " ")); 1145 } 1146 } 1147 } 1148