blob: 032af258dbd9161727a1035dd90e0a412c07ec07 [file] [log] [blame]
Alex Salob81472f2018-12-12 14:44:28 -08001/*
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
17package com.android.server.attention;
18
Matt Papeaea28312019-03-07 13:51:38 -080019import static android.provider.DeviceConfig.NAMESPACE_ATTENTION_MANAGER_SERVICE;
Alex Saloeeba0232019-03-14 18:56:54 -070020import static android.service.attention.AttentionService.ATTENTION_FAILURE_CANCELLED;
Alex Salo10800342019-04-02 18:45:20 -070021import static android.service.attention.AttentionService.ATTENTION_FAILURE_UNKNOWN;
Alex Salo440fe3d2019-01-25 11:50:38 -080022
Alex Salob81472f2018-12-12 14:44:28 -080023import android.Manifest;
24import android.annotation.Nullable;
25import android.annotation.UserIdInt;
26import android.app.ActivityManager;
27import android.attention.AttentionManagerInternal;
28import android.attention.AttentionManagerInternal.AttentionCallbackInternal;
29import android.content.BroadcastReceiver;
30import android.content.ComponentName;
31import android.content.Context;
32import android.content.Intent;
33import android.content.IntentFilter;
34import android.content.ServiceConnection;
35import android.content.pm.PackageManager;
36import android.content.pm.ResolveInfo;
37import android.content.pm.ServiceInfo;
38import android.os.Binder;
39import android.os.Handler;
40import android.os.IBinder;
41import android.os.Looper;
42import android.os.Message;
43import android.os.PowerManager;
44import android.os.RemoteException;
Aaron Li7565f8b2019-04-22 18:24:39 -070045import android.os.ResultReceiver;
46import android.os.ShellCallback;
47import android.os.ShellCommand;
Alex Salob81472f2018-12-12 14:44:28 -080048import android.os.SystemClock;
49import android.os.UserHandle;
Alex Salo440fe3d2019-01-25 11:50:38 -080050import android.provider.DeviceConfig;
Alex Salob81472f2018-12-12 14:44:28 -080051import android.service.attention.AttentionService;
52import android.service.attention.AttentionService.AttentionFailureCodes;
Alex Salo63829302019-04-06 14:57:30 -070053import android.service.attention.AttentionService.AttentionSuccessCodes;
Alex Salob81472f2018-12-12 14:44:28 -080054import android.service.attention.IAttentionCallback;
55import android.service.attention.IAttentionService;
56import android.text.TextUtils;
57import android.util.Slog;
58import android.util.SparseArray;
Alex Saloa060aee2019-01-21 14:36:41 -080059import android.util.StatsLog;
Alex Salob81472f2018-12-12 14:44:28 -080060
Alex Salob81472f2018-12-12 14:44:28 -080061import com.android.internal.annotations.GuardedBy;
Yi Jiangf9c9c162019-04-10 13:36:27 -070062import com.android.internal.annotations.VisibleForTesting;
Alex Salob81472f2018-12-12 14:44:28 -080063import com.android.internal.util.DumpUtils;
64import com.android.internal.util.IndentingPrintWriter;
65import com.android.internal.util.Preconditions;
66import com.android.server.SystemService;
67
Alex Salo5f5b5f42019-02-27 10:49:00 -080068import java.io.FileDescriptor;
Alex Salob81472f2018-12-12 14:44:28 -080069import java.io.PrintWriter;
70
71/**
72 * An attention service implementation that runs in System Server process.
73 * This service publishes a LocalService and reroutes calls to a {@link AttentionService} that it
74 * manages.
75 */
76public class AttentionManagerService extends SystemService {
77 private static final String LOG_TAG = "AttentionManagerService";
Alex Salo63829302019-04-06 14:57:30 -070078 private static final boolean DEBUG = false;
Alex Salob81472f2018-12-12 14:44:28 -080079
Alex Salo440fe3d2019-01-25 11:50:38 -080080 /** Default value in absence of {@link DeviceConfig} override. */
81 private static final boolean DEFAULT_SERVICE_ENABLED = true;
82
Alex Salob81472f2018-12-12 14:44:28 -080083 /** Service will unbind if connection is not used for that amount of time. */
84 private static final long CONNECTION_TTL_MILLIS = 60_000;
85
86 /** If the check attention called within that period - cached value will be returned. */
87 private static final long STALE_AFTER_MILLIS = 5_000;
88
Matt Papeaea28312019-03-07 13:51:38 -080089 /** DeviceConfig flag name, if {@code true}, enables AttentionManagerService features. */
90 private static final String SERVICE_ENABLED = "service_enabled";
Aaron Li7565f8b2019-04-22 18:24:39 -070091 private static String sTestAttentionServicePackage;
Alex Salob81472f2018-12-12 14:44:28 -080092 private final Context mContext;
93 private final PowerManager mPowerManager;
Alex Salob81472f2018-12-12 14:44:28 -080094 private final Object mLock;
95 @GuardedBy("mLock")
96 private final SparseArray<UserState> mUserStates = new SparseArray<>();
Yi Jiangf9c9c162019-04-10 13:36:27 -070097 private AttentionHandler mAttentionHandler;
Alex Salob81472f2018-12-12 14:44:28 -080098
Yi Jiangf9c9c162019-04-10 13:36:27 -070099 @VisibleForTesting
100 ComponentName mComponentName;
Alex Salob81472f2018-12-12 14:44:28 -0800101
102 public AttentionManagerService(Context context) {
Yi Jiangf9c9c162019-04-10 13:36:27 -0700103 this(context, (PowerManager) context.getSystemService(Context.POWER_SERVICE),
104 new Object(), null);
105 mAttentionHandler = new AttentionHandler();
106 }
107
108 @VisibleForTesting
109 AttentionManagerService(Context context, PowerManager powerManager, Object lock,
110 AttentionHandler handler) {
Alex Salob81472f2018-12-12 14:44:28 -0800111 super(context);
112 mContext = Preconditions.checkNotNull(context);
Yi Jiangf9c9c162019-04-10 13:36:27 -0700113 mPowerManager = powerManager;
114 mLock = lock;
115 mAttentionHandler = handler;
Alex Salob81472f2018-12-12 14:44:28 -0800116 }
117
118 @Override
119 public void onStart() {
Alex Salo5f5b5f42019-02-27 10:49:00 -0800120 publishBinderService(Context.ATTENTION_SERVICE, new BinderService());
Alex Salob81472f2018-12-12 14:44:28 -0800121 publishLocalService(AttentionManagerInternal.class, new LocalService());
122 }
123
124 @Override
Alex Salocde84302019-01-21 15:31:07 -0800125 public void onSwitchUser(int userId) {
Alex Salo1e32c072019-02-13 16:58:01 -0800126 cancelAndUnbindLocked(peekUserStateLocked(userId));
Alex Salob81472f2018-12-12 14:44:28 -0800127 }
128
Alex Salo7a6e3a62019-02-27 15:08:15 -0800129 /** Returns {@code true} if attention service is configured on this device. */
130 public static boolean isServiceConfigured(Context context) {
Aaron Li7565f8b2019-04-22 18:24:39 -0700131 return !TextUtils.isEmpty(getServiceConfigPackage(context));
Alex Salo7a6e3a62019-02-27 15:08:15 -0800132 }
133
Alex Salo50b701e2019-02-01 13:34:24 -0800134 /** Resolves and sets up the attention service if it had not been done yet. */
135 private boolean isServiceAvailable() {
136 if (mComponentName == null) {
Alex Salob81472f2018-12-12 14:44:28 -0800137 mComponentName = resolveAttentionService(mContext);
Alex Salo50b701e2019-02-01 13:34:24 -0800138 if (mComponentName != null) {
Alex Salob81472f2018-12-12 14:44:28 -0800139 mContext.registerReceiver(new ScreenStateReceiver(),
140 new IntentFilter(Intent.ACTION_SCREEN_OFF));
141 }
142 }
Alex Salo50b701e2019-02-01 13:34:24 -0800143 return mComponentName != null;
Alex Salob81472f2018-12-12 14:44:28 -0800144 }
145
146 /**
147 * Returns {@code true} if attention service is supported on this device.
148 */
Yi Jiang99a84792019-03-13 15:32:48 -0700149 private boolean isAttentionServiceSupported() {
Alex Salo50b701e2019-02-01 13:34:24 -0800150 return isServiceEnabled() && isServiceAvailable();
Alex Salo440fe3d2019-01-25 11:50:38 -0800151 }
152
Yi Jiangf9c9c162019-04-10 13:36:27 -0700153 @VisibleForTesting
154 protected boolean isServiceEnabled() {
Matt Papeaea28312019-03-07 13:51:38 -0800155 return DeviceConfig.getBoolean(NAMESPACE_ATTENTION_MANAGER_SERVICE, SERVICE_ENABLED,
156 DEFAULT_SERVICE_ENABLED);
Alex Salob81472f2018-12-12 14:44:28 -0800157 }
158
159 /**
160 * Checks whether user attention is at the screen and calls in the provided callback.
161 *
Alex Salo63829302019-04-06 14:57:30 -0700162 * Calling this multiple times quickly in a row will result in either a) returning a cached
163 * value, if present, or b) returning {@code false} because only one active request at a time is
164 * allowed.
165 *
166 * @return {@code true} if the framework was able to dispatch the request
Alex Salob81472f2018-12-12 14:44:28 -0800167 */
Yi Jiangf9c9c162019-04-10 13:36:27 -0700168 @VisibleForTesting
169 boolean checkAttention(long timeout, AttentionCallbackInternal callbackInternal) {
Alex Saloeeba0232019-03-14 18:56:54 -0700170 Preconditions.checkNotNull(callbackInternal);
Alex Salob81472f2018-12-12 14:44:28 -0800171
172 if (!isAttentionServiceSupported()) {
173 Slog.w(LOG_TAG, "Trying to call checkAttention() on an unsupported device.");
174 return false;
175 }
176
177 // don't allow attention check in screen off state
178 if (!mPowerManager.isInteractive()) {
179 return false;
180 }
181
182 synchronized (mLock) {
Alex Salo1e32c072019-02-13 16:58:01 -0800183 final long now = SystemClock.uptimeMillis();
Alex Saloeeba0232019-03-14 18:56:54 -0700184 // schedule shutting down the connection if no one resets this timer
Alex Salo1e32c072019-02-13 16:58:01 -0800185 freeIfInactiveLocked();
Alex Salob81472f2018-12-12 14:44:28 -0800186
187 final UserState userState = getOrCreateCurrentUserStateLocked();
188 // lazily start the service, which should be very lightweight to start
189 if (!userState.bindLocked()) {
190 return false;
191 }
192
Alex Salo63829302019-04-06 14:57:30 -0700193 // throttle frequent requests
194 final AttentionCheckCache cache = userState.mAttentionCheckCache;
195 if (cache != null && now < cache.mLastComputed + STALE_AFTER_MILLIS) {
196 callbackInternal.onSuccess(cache.mResult, cache.mTimestamp);
197 return true;
198 }
Alex Salob81472f2018-12-12 14:44:28 -0800199
Alex Salo63829302019-04-06 14:57:30 -0700200 // prevent spamming with multiple requests, only one at a time is allowed
201 if (userState.mCurrentAttentionCheck != null) {
202 if (!userState.mCurrentAttentionCheck.mIsDispatched
203 || !userState.mCurrentAttentionCheck.mIsFulfilled) {
204 return false;
205 }
206 }
207
208 userState.mCurrentAttentionCheck = createAttentionCheck(callbackInternal, userState);
209
210 if (userState.mService != null) {
211 try {
Alex Saloeeba0232019-03-14 18:56:54 -0700212 // schedule request cancellation if not returned by that point yet
Alex Salob81472f2018-12-12 14:44:28 -0800213 cancelAfterTimeoutLocked(timeout);
Alex Saloeeba0232019-03-14 18:56:54 -0700214 userState.mService.checkAttention(
215 userState.mCurrentAttentionCheck.mIAttentionCallback);
Alex Salo63829302019-04-06 14:57:30 -0700216 userState.mCurrentAttentionCheck.mIsDispatched = true;
Alex Salob81472f2018-12-12 14:44:28 -0800217 } catch (RemoteException e) {
218 Slog.e(LOG_TAG, "Cannot call into the AttentionService");
219 return false;
220 }
221 }
222 return true;
223 }
224 }
225
Alex Salo63829302019-04-06 14:57:30 -0700226 private AttentionCheck createAttentionCheck(AttentionCallbackInternal callbackInternal,
227 UserState userState) {
228 final IAttentionCallback iAttentionCallback = new IAttentionCallback.Stub() {
229 @Override
230 public void onSuccess(@AttentionSuccessCodes int result, long timestamp) {
231 // the callback might have been cancelled already
232 if (!userState.mCurrentAttentionCheck.mIsFulfilled) {
233 callbackInternal.onSuccess(result, timestamp);
234 userState.mCurrentAttentionCheck.mIsFulfilled = true;
235 }
236
237 synchronized (mLock) {
238 userState.mAttentionCheckCache = new AttentionCheckCache(
239 SystemClock.uptimeMillis(), result,
240 timestamp);
241 }
242 StatsLog.write(
243 StatsLog.ATTENTION_MANAGER_SERVICE_RESULT_REPORTED,
244 result);
245 }
246
247 @Override
248 public void onFailure(@AttentionFailureCodes int error) {
249 // the callback might have been cancelled already
250 if (!userState.mCurrentAttentionCheck.mIsFulfilled) {
251 callbackInternal.onFailure(error);
252 userState.mCurrentAttentionCheck.mIsFulfilled = true;
253 }
254
255 StatsLog.write(
256 StatsLog.ATTENTION_MANAGER_SERVICE_RESULT_REPORTED,
257 error);
258 }
259 };
260
261 return new AttentionCheck(callbackInternal, iAttentionCallback);
262 }
263
Alex Salob81472f2018-12-12 14:44:28 -0800264 /** Cancels the specified attention check. */
Yi Jiangf9c9c162019-04-10 13:36:27 -0700265 @VisibleForTesting
266 void cancelAttentionCheck(AttentionCallbackInternal callbackInternal) {
Alex Salocde84302019-01-21 15:31:07 -0800267 synchronized (mLock) {
Alex Salo1e32c072019-02-13 16:58:01 -0800268 final UserState userState = peekCurrentUserStateLocked();
269 if (userState == null) {
270 return;
271 }
Alex Salo63829302019-04-06 14:57:30 -0700272 if (!userState.mCurrentAttentionCheck.mCallbackInternal.equals(callbackInternal)) {
273 Slog.e(LOG_TAG, "Cannot cancel a non-current request");
Alex Salocde84302019-01-21 15:31:07 -0800274 return;
275 }
Alex Salo63829302019-04-06 14:57:30 -0700276 cancel(userState);
Alex Salob81472f2018-12-12 14:44:28 -0800277 }
278 }
279
280 @GuardedBy("mLock")
Yi Jiangf9c9c162019-04-10 13:36:27 -0700281 @VisibleForTesting
282 protected void freeIfInactiveLocked() {
Alex Salo1e32c072019-02-13 16:58:01 -0800283 // If we are called here, it means someone used the API again - reset the timer then.
284 mAttentionHandler.removeMessages(AttentionHandler.CHECK_CONNECTION_EXPIRATION);
285
286 // Schedule resources cleanup if no one calls the API again.
287 mAttentionHandler.sendEmptyMessageDelayed(AttentionHandler.CHECK_CONNECTION_EXPIRATION,
Alex Salob81472f2018-12-12 14:44:28 -0800288 CONNECTION_TTL_MILLIS);
289 }
290
291 @GuardedBy("mLock")
292 private void cancelAfterTimeoutLocked(long timeout) {
293 mAttentionHandler.sendEmptyMessageDelayed(AttentionHandler.ATTENTION_CHECK_TIMEOUT,
294 timeout);
295 }
296
297
298 @GuardedBy("mLock")
Yi Jiangf9c9c162019-04-10 13:36:27 -0700299 @VisibleForTesting
300 protected UserState getOrCreateCurrentUserStateLocked() {
Alex Salocde84302019-01-21 15:31:07 -0800301 return getOrCreateUserStateLocked(ActivityManager.getCurrentUser());
Alex Salob81472f2018-12-12 14:44:28 -0800302 }
303
304 @GuardedBy("mLock")
Yi Jiangfa323e72019-04-16 17:23:11 -0700305 @VisibleForTesting
306 protected UserState getOrCreateUserStateLocked(int userId) {
Alex Salob81472f2018-12-12 14:44:28 -0800307 UserState result = mUserStates.get(userId);
308 if (result == null) {
Alex Salo50b701e2019-02-01 13:34:24 -0800309 result = new UserState(userId, mContext, mLock, mComponentName);
Alex Salob81472f2018-12-12 14:44:28 -0800310 mUserStates.put(userId, result);
311 }
312 return result;
313 }
314
315 @GuardedBy("mLock")
Alex Salo1e32c072019-02-13 16:58:01 -0800316 @Nullable
Yi Jiangf9c9c162019-04-10 13:36:27 -0700317 @VisibleForTesting
318 protected UserState peekCurrentUserStateLocked() {
Alex Salocde84302019-01-21 15:31:07 -0800319 return peekUserStateLocked(ActivityManager.getCurrentUser());
Alex Salob81472f2018-12-12 14:44:28 -0800320 }
321
322 @GuardedBy("mLock")
Alex Salo1e32c072019-02-13 16:58:01 -0800323 @Nullable
324 private UserState peekUserStateLocked(int userId) {
Alex Salob81472f2018-12-12 14:44:28 -0800325 return mUserStates.get(userId);
326 }
327
Aaron Li7565f8b2019-04-22 18:24:39 -0700328 private static String getServiceConfigPackage(Context context) {
329 return context.getPackageManager().getAttentionServicePackageName();
Alex Salo7a6e3a62019-02-27 15:08:15 -0800330 }
331
Alex Salob81472f2018-12-12 14:44:28 -0800332 /**
333 * Provides attention service component name at runtime, making sure it's provided by the
334 * system.
335 */
336 private static ComponentName resolveAttentionService(Context context) {
Aaron Li7565f8b2019-04-22 18:24:39 -0700337 final String serviceConfigPackage = getServiceConfigPackage(context);
Alex Salo440fe3d2019-01-25 11:50:38 -0800338
Aaron Li7565f8b2019-04-22 18:24:39 -0700339 String resolvedPackage;
340 int flags = PackageManager.MATCH_SYSTEM_ONLY;
341 if (!TextUtils.isEmpty(sTestAttentionServicePackage)) {
342 resolvedPackage = sTestAttentionServicePackage;
343 flags = PackageManager.GET_META_DATA;
344 } else if (!TextUtils.isEmpty(serviceConfigPackage)) {
345 resolvedPackage = serviceConfigPackage;
346 } else {
Alex Salob81472f2018-12-12 14:44:28 -0800347 return null;
348 }
349
350 final Intent intent = new Intent(AttentionService.SERVICE_INTERFACE).setPackage(
Aaron Li7565f8b2019-04-22 18:24:39 -0700351 resolvedPackage);
Alex Salob81472f2018-12-12 14:44:28 -0800352
Aaron Li7565f8b2019-04-22 18:24:39 -0700353 final ResolveInfo resolveInfo = context.getPackageManager().resolveService(intent, flags);
Alex Salob81472f2018-12-12 14:44:28 -0800354 if (resolveInfo == null || resolveInfo.serviceInfo == null) {
355 Slog.wtf(LOG_TAG, String.format("Service %s not found in package %s",
Aaron Li7565f8b2019-04-22 18:24:39 -0700356 AttentionService.SERVICE_INTERFACE, serviceConfigPackage
Alex Salob81472f2018-12-12 14:44:28 -0800357 ));
358 return null;
359 }
360
361 final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
362 final String permission = serviceInfo.permission;
363 if (Manifest.permission.BIND_ATTENTION_SERVICE.equals(permission)) {
364 return serviceInfo.getComponentName();
365 }
366 Slog.e(LOG_TAG, String.format(
367 "Service %s should require %s permission. Found %s permission",
368 serviceInfo.getComponentName(),
369 Manifest.permission.BIND_ATTENTION_SERVICE,
370 serviceInfo.permission));
371 return null;
372 }
373
Alex Salo5f5b5f42019-02-27 10:49:00 -0800374 private void dumpInternal(IndentingPrintWriter ipw) {
375 ipw.println("Attention Manager Service (dumpsys attention) state:\n");
Alex Salob81472f2018-12-12 14:44:28 -0800376
377 ipw.printPair("context", mContext);
Alex Salo5f5b5f42019-02-27 10:49:00 -0800378 ipw.println();
Alex Salob81472f2018-12-12 14:44:28 -0800379 synchronized (mLock) {
380 int size = mUserStates.size();
381 ipw.print("Number user states: ");
Alex Salo5f5b5f42019-02-27 10:49:00 -0800382 ipw.println(size);
Alex Salob81472f2018-12-12 14:44:28 -0800383 if (size > 0) {
384 ipw.increaseIndent();
385 for (int i = 0; i < size; i++) {
386 UserState userState = mUserStates.valueAt(i);
387 ipw.print(i);
388 ipw.print(":");
389 userState.dump(ipw);
390 ipw.println();
391 }
392 ipw.decreaseIndent();
393 }
394 }
395 }
396
397 private final class LocalService extends AttentionManagerInternal {
398 @Override
399 public boolean isAttentionServiceSupported() {
400 return AttentionManagerService.this.isAttentionServiceSupported();
401 }
402
403 @Override
Alex Saloeeba0232019-03-14 18:56:54 -0700404 public boolean checkAttention(long timeout, AttentionCallbackInternal callbackInternal) {
405 return AttentionManagerService.this.checkAttention(timeout, callbackInternal);
Alex Salob81472f2018-12-12 14:44:28 -0800406 }
407
408 @Override
Alex Saloeeba0232019-03-14 18:56:54 -0700409 public void cancelAttentionCheck(AttentionCallbackInternal callbackInternal) {
410 AttentionManagerService.this.cancelAttentionCheck(callbackInternal);
Alex Salob81472f2018-12-12 14:44:28 -0800411 }
412 }
413
414 private static final class AttentionCheckCache {
415 private final long mLastComputed;
416 private final int mResult;
417 private final long mTimestamp;
418
419 AttentionCheckCache(long lastComputed, @AttentionService.AttentionSuccessCodes int result,
420 long timestamp) {
421 mLastComputed = lastComputed;
422 mResult = result;
423 mTimestamp = timestamp;
424 }
425 }
426
Yi Jiangf9c9c162019-04-10 13:36:27 -0700427 @VisibleForTesting
428 static final class AttentionCheck {
Alex Saloeeba0232019-03-14 18:56:54 -0700429 private final AttentionCallbackInternal mCallbackInternal;
430 private final IAttentionCallback mIAttentionCallback;
Alex Salo63829302019-04-06 14:57:30 -0700431 private boolean mIsDispatched;
432 private boolean mIsFulfilled;
Alex Saloeeba0232019-03-14 18:56:54 -0700433
434 AttentionCheck(AttentionCallbackInternal callbackInternal,
435 IAttentionCallback iAttentionCallback) {
436 mCallbackInternal = callbackInternal;
437 mIAttentionCallback = iAttentionCallback;
438 }
Alex Salo63829302019-04-06 14:57:30 -0700439
440 void cancelInternal() {
Aaron Li7565f8b2019-04-22 18:24:39 -0700441 mIsFulfilled = true;
Alex Salo63829302019-04-06 14:57:30 -0700442 mCallbackInternal.onFailure(ATTENTION_FAILURE_CANCELLED);
443 }
Alex Saloeeba0232019-03-14 18:56:54 -0700444 }
445
Yi Jiangf9c9c162019-04-10 13:36:27 -0700446 @VisibleForTesting
447 protected static class UserState {
Alex Salo50b701e2019-02-01 13:34:24 -0800448 final ComponentName mComponentName;
Alex Salob81472f2018-12-12 14:44:28 -0800449 final AttentionServiceConnection mConnection = new AttentionServiceConnection();
450
451 @GuardedBy("mLock")
452 IAttentionService mService;
453 @GuardedBy("mLock")
454 boolean mBinding;
455 @GuardedBy("mLock")
Alex Saloeeba0232019-03-14 18:56:54 -0700456 AttentionCheck mCurrentAttentionCheck;
Alex Salob81472f2018-12-12 14:44:28 -0800457 @GuardedBy("mLock")
Alex Salob81472f2018-12-12 14:44:28 -0800458 AttentionCheckCache mAttentionCheckCache;
459
460 @UserIdInt
461 final int mUserId;
462 final Context mContext;
463 final Object mLock;
464
Yi Jiangf9c9c162019-04-10 13:36:27 -0700465 UserState(int userId, Context context, Object lock, ComponentName componentName) {
Alex Salob81472f2018-12-12 14:44:28 -0800466 mUserId = userId;
467 mContext = Preconditions.checkNotNull(context);
468 mLock = Preconditions.checkNotNull(lock);
Alex Salo50b701e2019-02-01 13:34:24 -0800469 mComponentName = Preconditions.checkNotNull(componentName);
Alex Salob81472f2018-12-12 14:44:28 -0800470 }
471
472
473 @GuardedBy("mLock")
474 private void handlePendingCallbackLocked() {
Alex Salo63829302019-04-06 14:57:30 -0700475 if (!mCurrentAttentionCheck.mIsDispatched) {
476 if (mService != null) {
477 try {
478 mService.checkAttention(mCurrentAttentionCheck.mIAttentionCallback);
479 mCurrentAttentionCheck.mIsDispatched = true;
480 } catch (RemoteException e) {
481 Slog.e(LOG_TAG, "Cannot call into the AttentionService");
482 }
483 } else {
484 mCurrentAttentionCheck.mCallbackInternal.onFailure(ATTENTION_FAILURE_UNKNOWN);
485 }
Alex Salob81472f2018-12-12 14:44:28 -0800486 }
487 }
488
489 /** Binds to the system's AttentionService which provides an actual implementation. */
490 @GuardedBy("mLock")
491 private boolean bindLocked() {
492 // No need to bind if service is binding or has already been bound.
493 if (mBinding || mService != null) {
494 return true;
495 }
496
497 final boolean willBind;
498 final long identity = Binder.clearCallingIdentity();
499
500 try {
Alex Salob81472f2018-12-12 14:44:28 -0800501 final Intent mServiceIntent = new Intent(
502 AttentionService.SERVICE_INTERFACE).setComponent(
Alex Salo50b701e2019-02-01 13:34:24 -0800503 mComponentName);
Alex Salob81472f2018-12-12 14:44:28 -0800504 willBind = mContext.bindServiceAsUser(mServiceIntent, mConnection,
505 Context.BIND_AUTO_CREATE, UserHandle.CURRENT);
506 mBinding = willBind;
507 } finally {
508 Binder.restoreCallingIdentity(identity);
509 }
510 return willBind;
511 }
512
513 private void dump(IndentingPrintWriter pw) {
514 pw.printPair("context", mContext);
515 pw.printPair("userId", mUserId);
516 synchronized (mLock) {
517 pw.printPair("binding", mBinding);
Alex Salob81472f2018-12-12 14:44:28 -0800518 }
519 }
520
Yi Jiangf9c9c162019-04-10 13:36:27 -0700521 private class AttentionServiceConnection implements ServiceConnection {
Alex Salob81472f2018-12-12 14:44:28 -0800522 @Override
523 public void onServiceConnected(ComponentName name, IBinder service) {
524 init(IAttentionService.Stub.asInterface(service));
525 }
526
527 @Override
528 public void onServiceDisconnected(ComponentName name) {
529 cleanupService();
530 }
531
532 @Override
533 public void onBindingDied(ComponentName name) {
534 cleanupService();
535 }
536
537 @Override
538 public void onNullBinding(ComponentName name) {
539 cleanupService();
540 }
541
542 void cleanupService() {
543 init(null);
544 }
545
546 private void init(@Nullable IAttentionService service) {
547 synchronized (mLock) {
548 mService = service;
549 mBinding = false;
550 handlePendingCallbackLocked();
551 }
552 }
553 }
554 }
555
Yi Jiangf9c9c162019-04-10 13:36:27 -0700556 @VisibleForTesting
557 protected class AttentionHandler extends Handler {
Alex Salo1e32c072019-02-13 16:58:01 -0800558 private static final int CHECK_CONNECTION_EXPIRATION = 1;
Alex Salob81472f2018-12-12 14:44:28 -0800559 private static final int ATTENTION_CHECK_TIMEOUT = 2;
560
561 AttentionHandler() {
562 super(Looper.myLooper());
563 }
564
565 public void handleMessage(Message msg) {
566 switch (msg.what) {
567 // Do not occupy resources when not in use - unbind proactively.
Alex Salo1e32c072019-02-13 16:58:01 -0800568 case CHECK_CONNECTION_EXPIRATION: {
Alex Salob81472f2018-12-12 14:44:28 -0800569 for (int i = 0; i < mUserStates.size(); i++) {
Alex Salo1e32c072019-02-13 16:58:01 -0800570 cancelAndUnbindLocked(mUserStates.valueAt(i));
Alex Salob81472f2018-12-12 14:44:28 -0800571 }
Alex Salob81472f2018-12-12 14:44:28 -0800572 }
573 break;
574
575 // Callee is no longer interested in the attention check result - cancel.
576 case ATTENTION_CHECK_TIMEOUT: {
Alex Salo1e32c072019-02-13 16:58:01 -0800577 synchronized (mLock) {
Alex Salo63829302019-04-06 14:57:30 -0700578 cancel(peekCurrentUserStateLocked());
Alex Salo1e32c072019-02-13 16:58:01 -0800579 }
Alex Salob81472f2018-12-12 14:44:28 -0800580 }
581 break;
582
583 default:
584 break;
585 }
586 }
587 }
588
Yi Jiangf9c9c162019-04-10 13:36:27 -0700589 @VisibleForTesting
590 void cancel(UserState userState) {
Alex Salo63829302019-04-06 14:57:30 -0700591 if (userState == null || userState.mCurrentAttentionCheck == null) {
592 return;
593 }
Alex Salo1e32c072019-02-13 16:58:01 -0800594
Alex Salo63829302019-04-06 14:57:30 -0700595 if (userState.mCurrentAttentionCheck.mIsFulfilled) {
596 if (DEBUG) {
597 Slog.d(LOG_TAG, "Trying to cancel the check that has been already fulfilled.");
Alex Salo1e32c072019-02-13 16:58:01 -0800598 }
Alex Salo63829302019-04-06 14:57:30 -0700599 return;
600 }
Alex Salo63829302019-04-06 14:57:30 -0700601
602 if (userState.mService == null) {
603 userState.mCurrentAttentionCheck.cancelInternal();
604 return;
605 }
606
607 try {
608 userState.mService.cancelAttentionCheck(
609 userState.mCurrentAttentionCheck.mIAttentionCallback);
610 } catch (RemoteException e) {
611 Slog.e(LOG_TAG, "Unable to cancel attention check");
612 userState.mCurrentAttentionCheck.cancelInternal();
Alex Salo1e32c072019-02-13 16:58:01 -0800613 }
614 }
615
616 @GuardedBy("mLock")
617 private void cancelAndUnbindLocked(UserState userState) {
618 synchronized (mLock) {
Yi Jiang4dfc3682019-03-20 16:50:40 -0700619 if (userState == null) {
620 return;
621 }
Alex Salo63829302019-04-06 14:57:30 -0700622
623 cancel(userState);
624
625 if (userState.mService == null) {
626 return;
627 }
Alex Salo1e32c072019-02-13 16:58:01 -0800628
629 mContext.unbindService(userState.mConnection);
630 userState.mConnection.cleanupService();
631 mUserStates.remove(userState.mUserId);
Alex Salob81472f2018-12-12 14:44:28 -0800632 }
633 }
634
635 /**
636 * Unbinds and stops the service when the screen off intent is received.
637 * Attention service only makes sense when screen is ON; disconnect and stop service otherwise.
638 */
639 private final class ScreenStateReceiver extends BroadcastReceiver {
640 @Override
641 public void onReceive(Context context, Intent intent) {
642 if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
Alex Salo1e32c072019-02-13 16:58:01 -0800643 cancelAndUnbindLocked(peekCurrentUserStateLocked());
Alex Salob81472f2018-12-12 14:44:28 -0800644 }
645 }
646 }
Alex Salo5f5b5f42019-02-27 10:49:00 -0800647
Aaron Li7565f8b2019-04-22 18:24:39 -0700648 private final class AttentionManagerServiceShellCommand extends ShellCommand {
649 class TestableAttentionCallbackInternal extends AttentionCallbackInternal {
650 private int mLastCallbackCode = -1;
651
652 @Override
653 public void onSuccess(int result, long timestamp) {
654 mLastCallbackCode = result;
655 }
656
657 @Override
658 public void onFailure(int error) {
659 mLastCallbackCode = error;
660 }
661
662 public void reset() {
663 mLastCallbackCode = -1;
664 }
665
666 public int getLastCallbackCode() {
667 return mLastCallbackCode;
668 }
669 }
670
671 final TestableAttentionCallbackInternal mTestableAttentionCallback =
672 new TestableAttentionCallbackInternal();
673
674 @Override
675 public int onCommand(@Nullable final String cmd) {
676 if (cmd == null) {
677 return handleDefaultCommands(cmd);
678 }
679 final PrintWriter err = getErrPrintWriter();
680 try {
681 switch (cmd) {
682 case "getAttentionServiceComponent":
683 return cmdResolveAttentionServiceComponent();
684 case "call":
685 switch (getNextArgRequired()) {
686 case "checkAttention":
687 return cmdCallCheckAttention();
688 case "cancelCheckAttention":
689 return cmdCallCancelAttention();
690 default:
691 throw new IllegalArgumentException("Invalid argument");
692 }
693 case "setTestableAttentionService":
694 return cmdSetTestableAttentionService(getNextArgRequired());
695 case "clearTestableAttentionService":
696 return cmdClearTestableAttentionService();
697 case "getLastTestCallbackCode":
698 return cmdGetLastTestCallbackCode();
699 default:
700 return handleDefaultCommands(cmd);
701 }
702 } catch (IllegalArgumentException e) {
703 err.println("Error: " + e.getMessage());
704 }
705 return -1;
706 }
707
708 private int cmdSetTestableAttentionService(String testingServicePackage) {
709 final PrintWriter out = getOutPrintWriter();
710 if (TextUtils.isEmpty(testingServicePackage)) {
711 out.println("false");
712 } else {
713 sTestAttentionServicePackage = testingServicePackage;
714 resetStates();
715 out.println(mComponentName != null ? "true" : "false");
716 }
717 return 0;
718 }
719
720 private int cmdClearTestableAttentionService() {
721 sTestAttentionServicePackage = "";
722 mTestableAttentionCallback.reset();
723 resetStates();
724 return 0;
725 }
726
727 private int cmdCallCheckAttention() {
728 final PrintWriter out = getOutPrintWriter();
729 boolean calledSuccessfully = checkAttention(2000, mTestableAttentionCallback);
730 out.println(calledSuccessfully ? "true" : "false");
731 return 0;
732 }
733
734 private int cmdCallCancelAttention() {
735 final PrintWriter out = getOutPrintWriter();
736 cancelAttentionCheck(mTestableAttentionCallback);
737 out.println("true");
738 return 0;
739 }
740
741 private int cmdResolveAttentionServiceComponent() {
742 final PrintWriter out = getOutPrintWriter();
743 ComponentName resolvedComponent = resolveAttentionService(mContext);
744 out.println(resolvedComponent != null ? resolvedComponent.flattenToShortString() : "");
745 return 0;
746 }
747
748 private int cmdGetLastTestCallbackCode() {
749 final PrintWriter out = getOutPrintWriter();
750 out.println(mTestableAttentionCallback.getLastCallbackCode());
751 return 0;
752 }
753
754 private void resetStates() {
755 mComponentName = resolveAttentionService(mContext);
756 mUserStates.clear();
757 }
758
759 @Override
760 public void onHelp() {
761 final PrintWriter out = getOutPrintWriter();
762 out.println("Attention commands: ");
763 out.println(" setTestableAttentionService <service_package>: Bind to a custom"
764 + " implementation of attention service");
765 out.println(" ---<service_package>:");
766 out.println(
767 " := Package containing the Attention Service implementation to bind to");
768 out.println(" ---returns:");
769 out.println(" := true, if was bound successfully");
770 out.println(" := false, if was not bound successfully");
771 out.println(" clearTestableAttentionService: Undo custom bindings. Revert to previous"
772 + " behavior");
773 out.println(" getAttentionServiceComponent: Get the current service component string");
774 out.println(" ---returns:");
775 out.println(" := If valid, the component string (in shorten form) for the"
776 + " currently bound service.");
777 out.println(" := else, empty string");
778 out.println(" call checkAttention: Calls check attention");
779 out.println(" ---returns:");
780 out.println(
781 " := true, if the call was successfully dispatched to the service "
782 + "implementation."
783 + " (to see the result, call getLastTestCallbackCode)");
784 out.println(" := false, otherwise");
785 out.println(" call cancelCheckAttention: Cancels check attention");
786 out.println(" getLastTestCallbackCode");
787 out.println(" ---returns:");
788 out.println(
789 " := An integer, representing the last callback code received from the "
790 + "bounded implementation. If none, it will return -1");
791 }
792 }
793
Alex Salo5f5b5f42019-02-27 10:49:00 -0800794 private final class BinderService extends Binder {
Aaron Li7565f8b2019-04-22 18:24:39 -0700795 AttentionManagerServiceShellCommand mAttentionManagerServiceShellCommand =
796 new AttentionManagerServiceShellCommand();
797
798 @Override
799 public void onShellCommand(FileDescriptor in, FileDescriptor out,
800 FileDescriptor err,
801 String[] args, ShellCallback callback,
802 ResultReceiver resultReceiver) {
803 mAttentionManagerServiceShellCommand.exec(this, in, out, err, args, callback,
804 resultReceiver);
805 }
806
Alex Salo5f5b5f42019-02-27 10:49:00 -0800807 @Override
808 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
809 if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) {
810 return;
811 }
812
813 dumpInternal(new IndentingPrintWriter(pw, " "));
814 }
815 }
Alex Salob81472f2018-12-12 14:44:28 -0800816}