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