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