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