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