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