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