blob: 5965d592cf4f86c94343b23425ca4c0f89e12bba [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
Alex Saloc7b9c082019-01-29 13:10:55 -080019import static android.provider.DeviceConfig.AttentionManagerService.COMPONENT_NAME;
Alex Salo440fe3d2019-01-25 11:50:38 -080020import static android.provider.DeviceConfig.AttentionManagerService.NAMESPACE;
Alex Saloc7b9c082019-01-29 13:10:55 -080021import static android.provider.DeviceConfig.AttentionManagerService.SERVICE_ENABLED;
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;
45import android.os.SystemClock;
46import android.os.UserHandle;
Alex Salo440fe3d2019-01-25 11:50:38 -080047import android.provider.DeviceConfig;
Alex Salob81472f2018-12-12 14:44:28 -080048import android.service.attention.AttentionService;
49import android.service.attention.AttentionService.AttentionFailureCodes;
50import android.service.attention.IAttentionCallback;
51import android.service.attention.IAttentionService;
52import android.text.TextUtils;
53import android.util.Slog;
54import android.util.SparseArray;
Alex Saloa060aee2019-01-21 14:36:41 -080055import android.util.StatsLog;
Alex Salob81472f2018-12-12 14:44:28 -080056
57import com.android.internal.R;
58import com.android.internal.annotations.GuardedBy;
59import com.android.internal.util.DumpUtils;
60import com.android.internal.util.IndentingPrintWriter;
61import com.android.internal.util.Preconditions;
62import com.android.server.SystemService;
63
64import java.io.PrintWriter;
65
66/**
67 * An attention service implementation that runs in System Server process.
68 * This service publishes a LocalService and reroutes calls to a {@link AttentionService} that it
69 * manages.
70 */
71public class AttentionManagerService extends SystemService {
72 private static final String LOG_TAG = "AttentionManagerService";
73
Alex Salo440fe3d2019-01-25 11:50:38 -080074 /** Default value in absence of {@link DeviceConfig} override. */
75 private static final boolean DEFAULT_SERVICE_ENABLED = true;
76
Alex Salob81472f2018-12-12 14:44:28 -080077 /** Service will unbind if connection is not used for that amount of time. */
78 private static final long CONNECTION_TTL_MILLIS = 60_000;
79
80 /** If the check attention called within that period - cached value will be returned. */
81 private static final long STALE_AFTER_MILLIS = 5_000;
82
83 private final Context mContext;
84 private final PowerManager mPowerManager;
Alex Salob81472f2018-12-12 14:44:28 -080085 private final Object mLock;
86 @GuardedBy("mLock")
87 private final SparseArray<UserState> mUserStates = new SparseArray<>();
88 private final AttentionHandler mAttentionHandler;
89
90 private ComponentName mComponentName;
91
92 public AttentionManagerService(Context context) {
93 super(context);
94 mContext = Preconditions.checkNotNull(context);
95 mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
Alex Salob81472f2018-12-12 14:44:28 -080096 mLock = new Object();
97 mAttentionHandler = new AttentionHandler();
98 }
99
100 @Override
101 public void onStart() {
102 publishLocalService(AttentionManagerInternal.class, new LocalService());
103 }
104
105 @Override
Alex Salocde84302019-01-21 15:31:07 -0800106 public void onSwitchUser(int userId) {
Alex Salo1e32c072019-02-13 16:58:01 -0800107 cancelAndUnbindLocked(peekUserStateLocked(userId));
Alex Salob81472f2018-12-12 14:44:28 -0800108 }
109
Alex Salo50b701e2019-02-01 13:34:24 -0800110 /** Resolves and sets up the attention service if it had not been done yet. */
111 private boolean isServiceAvailable() {
112 if (mComponentName == null) {
Alex Salob81472f2018-12-12 14:44:28 -0800113 mComponentName = resolveAttentionService(mContext);
Alex Salo50b701e2019-02-01 13:34:24 -0800114 if (mComponentName != null) {
Alex Salob81472f2018-12-12 14:44:28 -0800115 mContext.registerReceiver(new ScreenStateReceiver(),
116 new IntentFilter(Intent.ACTION_SCREEN_OFF));
117 }
118 }
Alex Salo50b701e2019-02-01 13:34:24 -0800119 return mComponentName != null;
Alex Salob81472f2018-12-12 14:44:28 -0800120 }
121
122 /**
123 * Returns {@code true} if attention service is supported on this device.
124 */
125 public boolean isAttentionServiceSupported() {
Alex Salo50b701e2019-02-01 13:34:24 -0800126 return isServiceEnabled() && isServiceAvailable();
Alex Salo440fe3d2019-01-25 11:50:38 -0800127 }
128
129 private boolean isServiceEnabled() {
Stanislav Zholninbb7daa52019-03-07 17:40:14 +0000130 return DeviceConfig.getBoolean(NAMESPACE, SERVICE_ENABLED, DEFAULT_SERVICE_ENABLED);
Alex Salob81472f2018-12-12 14:44:28 -0800131 }
132
133 /**
134 * Checks whether user attention is at the screen and calls in the provided callback.
135 *
136 * @return {@code true} if the framework was able to send the provided callback to the service
137 */
138 public boolean checkAttention(int requestCode, long timeout,
139 AttentionCallbackInternal callback) {
140 Preconditions.checkNotNull(callback);
141
142 if (!isAttentionServiceSupported()) {
143 Slog.w(LOG_TAG, "Trying to call checkAttention() on an unsupported device.");
144 return false;
145 }
146
147 // don't allow attention check in screen off state
148 if (!mPowerManager.isInteractive()) {
149 return false;
150 }
151
152 synchronized (mLock) {
Alex Salo1e32c072019-02-13 16:58:01 -0800153 final long now = SystemClock.uptimeMillis();
154 freeIfInactiveLocked();
Alex Salob81472f2018-12-12 14:44:28 -0800155
156 final UserState userState = getOrCreateCurrentUserStateLocked();
157 // lazily start the service, which should be very lightweight to start
158 if (!userState.bindLocked()) {
159 return false;
160 }
161
162 if (userState.mService == null) {
163 // make sure every callback is called back
164 if (userState.mPendingAttentionCheck != null) {
165 userState.mPendingAttentionCheck.cancel(
166 AttentionService.ATTENTION_FAILURE_UNKNOWN);
167 }
168 userState.mPendingAttentionCheck = new PendingAttentionCheck(requestCode,
169 callback, () -> checkAttention(requestCode, timeout, callback));
170 } else {
171 try {
172 // throttle frequent requests
173 final AttentionCheckCache attentionCheckCache = userState.mAttentionCheckCache;
Alex Salo1e32c072019-02-13 16:58:01 -0800174 if (attentionCheckCache != null && now
Alex Salob81472f2018-12-12 14:44:28 -0800175 < attentionCheckCache.mLastComputed + STALE_AFTER_MILLIS) {
176 callback.onSuccess(requestCode, attentionCheckCache.mResult,
177 attentionCheckCache.mTimestamp);
178 return true;
179 }
180
181 cancelAfterTimeoutLocked(timeout);
182
183 userState.mCurrentAttentionCheckRequestCode = requestCode;
184 userState.mService.checkAttention(requestCode, new IAttentionCallback.Stub() {
185 @Override
186 public void onSuccess(int requestCode, int result, long timestamp) {
187 callback.onSuccess(requestCode, result, timestamp);
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800188 synchronized (mLock) {
189 userState.mAttentionCheckCache = new AttentionCheckCache(
190 SystemClock.uptimeMillis(), result,
191 timestamp);
Alex Salo1e32c072019-02-13 16:58:01 -0800192 userState.mCurrentAttentionCheckIsFulfilled = true;
Lucas Dupin0a5d7972019-01-16 18:52:30 -0800193 }
Alex Saloa060aee2019-01-21 14:36:41 -0800194 StatsLog.write(StatsLog.ATTENTION_MANAGER_SERVICE_RESULT_REPORTED,
195 result);
Alex Salob81472f2018-12-12 14:44:28 -0800196 }
197
198 @Override
199 public void onFailure(int requestCode, int error) {
200 callback.onFailure(requestCode, error);
Alex Salo1e32c072019-02-13 16:58:01 -0800201 userState.mCurrentAttentionCheckIsFulfilled = true;
Alex Saloa060aee2019-01-21 14:36:41 -0800202 StatsLog.write(StatsLog.ATTENTION_MANAGER_SERVICE_RESULT_REPORTED,
203 error);
Alex Salob81472f2018-12-12 14:44:28 -0800204 }
Alex Salob81472f2018-12-12 14:44:28 -0800205 });
206 } catch (RemoteException e) {
207 Slog.e(LOG_TAG, "Cannot call into the AttentionService");
208 return false;
209 }
210 }
211 return true;
212 }
213 }
214
215 /** Cancels the specified attention check. */
216 public void cancelAttentionCheck(int requestCode) {
Alex Salocde84302019-01-21 15:31:07 -0800217 synchronized (mLock) {
Alex Salo1e32c072019-02-13 16:58:01 -0800218 final UserState userState = peekCurrentUserStateLocked();
219 if (userState == null) {
220 return;
221 }
Alex Salocde84302019-01-21 15:31:07 -0800222 if (userState.mService == null) {
223 if (userState.mPendingAttentionCheck != null
224 && userState.mPendingAttentionCheck.mRequestCode == requestCode) {
225 userState.mPendingAttentionCheck = null;
226 }
227 return;
228 }
229 try {
230 userState.mService.cancelAttentionCheck(requestCode);
231 } catch (RemoteException e) {
232 Slog.e(LOG_TAG, "Cannot call into the AttentionService");
233 }
Alex Salob81472f2018-12-12 14:44:28 -0800234 }
235 }
236
237 @GuardedBy("mLock")
Alex Salo1e32c072019-02-13 16:58:01 -0800238 private void freeIfInactiveLocked() {
239 // If we are called here, it means someone used the API again - reset the timer then.
240 mAttentionHandler.removeMessages(AttentionHandler.CHECK_CONNECTION_EXPIRATION);
241
242 // Schedule resources cleanup if no one calls the API again.
243 mAttentionHandler.sendEmptyMessageDelayed(AttentionHandler.CHECK_CONNECTION_EXPIRATION,
Alex Salob81472f2018-12-12 14:44:28 -0800244 CONNECTION_TTL_MILLIS);
245 }
246
247 @GuardedBy("mLock")
248 private void cancelAfterTimeoutLocked(long timeout) {
249 mAttentionHandler.sendEmptyMessageDelayed(AttentionHandler.ATTENTION_CHECK_TIMEOUT,
250 timeout);
251 }
252
253
254 @GuardedBy("mLock")
255 private UserState getOrCreateCurrentUserStateLocked() {
Alex Salocde84302019-01-21 15:31:07 -0800256 return getOrCreateUserStateLocked(ActivityManager.getCurrentUser());
Alex Salob81472f2018-12-12 14:44:28 -0800257 }
258
259 @GuardedBy("mLock")
260 private UserState getOrCreateUserStateLocked(int userId) {
261 UserState result = mUserStates.get(userId);
262 if (result == null) {
Alex Salo50b701e2019-02-01 13:34:24 -0800263 result = new UserState(userId, mContext, mLock, mComponentName);
Alex Salob81472f2018-12-12 14:44:28 -0800264 mUserStates.put(userId, result);
265 }
266 return result;
267 }
268
269 @GuardedBy("mLock")
Alex Salo1e32c072019-02-13 16:58:01 -0800270 @Nullable
271 private UserState peekCurrentUserStateLocked() {
Alex Salocde84302019-01-21 15:31:07 -0800272 return peekUserStateLocked(ActivityManager.getCurrentUser());
Alex Salob81472f2018-12-12 14:44:28 -0800273 }
274
275 @GuardedBy("mLock")
Alex Salo1e32c072019-02-13 16:58:01 -0800276 @Nullable
277 private UserState peekUserStateLocked(int userId) {
Alex Salob81472f2018-12-12 14:44:28 -0800278 return mUserStates.get(userId);
279 }
280
281 /**
282 * Provides attention service component name at runtime, making sure it's provided by the
283 * system.
284 */
285 private static ComponentName resolveAttentionService(Context context) {
Alex Saloc7b9c082019-01-29 13:10:55 -0800286 final String flag = DeviceConfig.getProperty(NAMESPACE, COMPONENT_NAME);
Alex Salo440fe3d2019-01-25 11:50:38 -0800287
288 final String componentNameString = flag != null ? flag : context.getString(
Alex Salob81472f2018-12-12 14:44:28 -0800289 R.string.config_defaultAttentionService);
290
291 if (TextUtils.isEmpty(componentNameString)) {
292 return null;
293 }
294
295 final ComponentName componentName = ComponentName.unflattenFromString(componentNameString);
296 if (componentName == null) {
297 return null;
298 }
299
300 final Intent intent = new Intent(AttentionService.SERVICE_INTERFACE).setPackage(
301 componentName.getPackageName());
302
303 // Make sure that only system apps can declare the AttentionService.
304 final ResolveInfo resolveInfo = context.getPackageManager().resolveService(intent,
305 PackageManager.MATCH_SYSTEM_ONLY);
306 if (resolveInfo == null || resolveInfo.serviceInfo == null) {
307 Slog.wtf(LOG_TAG, String.format("Service %s not found in package %s",
308 AttentionService.SERVICE_INTERFACE, componentName
309 ));
310 return null;
311 }
312
313 final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
314 final String permission = serviceInfo.permission;
315 if (Manifest.permission.BIND_ATTENTION_SERVICE.equals(permission)) {
316 return serviceInfo.getComponentName();
317 }
318 Slog.e(LOG_TAG, String.format(
319 "Service %s should require %s permission. Found %s permission",
320 serviceInfo.getComponentName(),
321 Manifest.permission.BIND_ATTENTION_SERVICE,
322 serviceInfo.permission));
323 return null;
324 }
325
326 private void dumpInternal(PrintWriter pw) {
327 if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return;
328 IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
329 ipw.println("Attention Manager Service (dumpsys attention)\n");
330
331 ipw.printPair("context", mContext);
332 pw.println();
333 synchronized (mLock) {
334 int size = mUserStates.size();
335 ipw.print("Number user states: ");
336 pw.println(size);
337 if (size > 0) {
338 ipw.increaseIndent();
339 for (int i = 0; i < size; i++) {
340 UserState userState = mUserStates.valueAt(i);
341 ipw.print(i);
342 ipw.print(":");
343 userState.dump(ipw);
344 ipw.println();
345 }
346 ipw.decreaseIndent();
347 }
348 }
349 }
350
351 private final class LocalService extends AttentionManagerInternal {
352 @Override
353 public boolean isAttentionServiceSupported() {
354 return AttentionManagerService.this.isAttentionServiceSupported();
355 }
356
357 @Override
358 public boolean checkAttention(int requestCode, long timeout,
359 AttentionCallbackInternal callback) {
360 return AttentionManagerService.this.checkAttention(requestCode, timeout, callback);
361 }
362
363 @Override
364 public void cancelAttentionCheck(int requestCode) {
365 AttentionManagerService.this.cancelAttentionCheck(requestCode);
366 }
367 }
368
369 private static final class AttentionCheckCache {
370 private final long mLastComputed;
371 private final int mResult;
372 private final long mTimestamp;
373
374 AttentionCheckCache(long lastComputed, @AttentionService.AttentionSuccessCodes int result,
375 long timestamp) {
376 mLastComputed = lastComputed;
377 mResult = result;
378 mTimestamp = timestamp;
379 }
380 }
381
382 private static final class PendingAttentionCheck {
383 private final int mRequestCode;
384 private final AttentionCallbackInternal mCallback;
385 private final Runnable mRunnable;
386
387 PendingAttentionCheck(int requestCode, AttentionCallbackInternal callback,
388 Runnable runnable) {
389 mRequestCode = requestCode;
390 mCallback = callback;
391 mRunnable = runnable;
392 }
393
394 void cancel(@AttentionFailureCodes int failureCode) {
395 mCallback.onFailure(mRequestCode, failureCode);
396 }
397
398 void run() {
399 mRunnable.run();
400 }
401 }
402
403 private static final class UserState {
Alex Salo50b701e2019-02-01 13:34:24 -0800404 final ComponentName mComponentName;
Alex Salob81472f2018-12-12 14:44:28 -0800405 final AttentionServiceConnection mConnection = new AttentionServiceConnection();
406
407 @GuardedBy("mLock")
408 IAttentionService mService;
409 @GuardedBy("mLock")
410 boolean mBinding;
411 @GuardedBy("mLock")
412 int mCurrentAttentionCheckRequestCode;
413 @GuardedBy("mLock")
Alex Salo1e32c072019-02-13 16:58:01 -0800414 boolean mCurrentAttentionCheckIsFulfilled;
415 @GuardedBy("mLock")
Alex Salob81472f2018-12-12 14:44:28 -0800416 PendingAttentionCheck mPendingAttentionCheck;
417
418 @GuardedBy("mLock")
419 AttentionCheckCache mAttentionCheckCache;
420
421 @UserIdInt
422 final int mUserId;
423 final Context mContext;
424 final Object mLock;
425
Alex Salo50b701e2019-02-01 13:34:24 -0800426 private UserState(int userId, Context context, Object lock, ComponentName componentName) {
Alex Salob81472f2018-12-12 14:44:28 -0800427 mUserId = userId;
428 mContext = Preconditions.checkNotNull(context);
429 mLock = Preconditions.checkNotNull(lock);
Alex Salo50b701e2019-02-01 13:34:24 -0800430 mComponentName = Preconditions.checkNotNull(componentName);
Alex Salob81472f2018-12-12 14:44:28 -0800431 }
432
433
434 @GuardedBy("mLock")
435 private void handlePendingCallbackLocked() {
436 if (mService != null && mPendingAttentionCheck != null) {
437 mPendingAttentionCheck.run();
438 mPendingAttentionCheck = null;
439 }
440 }
441
442 /** Binds to the system's AttentionService which provides an actual implementation. */
443 @GuardedBy("mLock")
444 private boolean bindLocked() {
445 // No need to bind if service is binding or has already been bound.
446 if (mBinding || mService != null) {
447 return true;
448 }
449
450 final boolean willBind;
451 final long identity = Binder.clearCallingIdentity();
452
453 try {
Alex Salob81472f2018-12-12 14:44:28 -0800454 final Intent mServiceIntent = new Intent(
455 AttentionService.SERVICE_INTERFACE).setComponent(
Alex Salo50b701e2019-02-01 13:34:24 -0800456 mComponentName);
Alex Salob81472f2018-12-12 14:44:28 -0800457 willBind = mContext.bindServiceAsUser(mServiceIntent, mConnection,
458 Context.BIND_AUTO_CREATE, UserHandle.CURRENT);
459 mBinding = willBind;
460 } finally {
461 Binder.restoreCallingIdentity(identity);
462 }
463 return willBind;
464 }
465
466 private void dump(IndentingPrintWriter pw) {
467 pw.printPair("context", mContext);
468 pw.printPair("userId", mUserId);
469 synchronized (mLock) {
470 pw.printPair("binding", mBinding);
471 pw.printPair("isAttentionCheckPending", mPendingAttentionCheck != null);
472 }
473 }
474
475 private final class AttentionServiceConnection implements ServiceConnection {
476 @Override
477 public void onServiceConnected(ComponentName name, IBinder service) {
478 init(IAttentionService.Stub.asInterface(service));
479 }
480
481 @Override
482 public void onServiceDisconnected(ComponentName name) {
483 cleanupService();
484 }
485
486 @Override
487 public void onBindingDied(ComponentName name) {
488 cleanupService();
489 }
490
491 @Override
492 public void onNullBinding(ComponentName name) {
493 cleanupService();
494 }
495
496 void cleanupService() {
497 init(null);
498 }
499
500 private void init(@Nullable IAttentionService service) {
501 synchronized (mLock) {
502 mService = service;
503 mBinding = false;
504 handlePendingCallbackLocked();
505 }
506 }
507 }
508 }
509
510 private class AttentionHandler extends Handler {
Alex Salo1e32c072019-02-13 16:58:01 -0800511 private static final int CHECK_CONNECTION_EXPIRATION = 1;
Alex Salob81472f2018-12-12 14:44:28 -0800512 private static final int ATTENTION_CHECK_TIMEOUT = 2;
513
514 AttentionHandler() {
515 super(Looper.myLooper());
516 }
517
518 public void handleMessage(Message msg) {
519 switch (msg.what) {
520 // Do not occupy resources when not in use - unbind proactively.
Alex Salo1e32c072019-02-13 16:58:01 -0800521 case CHECK_CONNECTION_EXPIRATION: {
Alex Salob81472f2018-12-12 14:44:28 -0800522 for (int i = 0; i < mUserStates.size(); i++) {
Alex Salo1e32c072019-02-13 16:58:01 -0800523 cancelAndUnbindLocked(mUserStates.valueAt(i));
Alex Salob81472f2018-12-12 14:44:28 -0800524 }
Alex Salob81472f2018-12-12 14:44:28 -0800525 }
526 break;
527
528 // Callee is no longer interested in the attention check result - cancel.
529 case ATTENTION_CHECK_TIMEOUT: {
Alex Salo1e32c072019-02-13 16:58:01 -0800530 synchronized (mLock) {
531 final UserState userState = peekCurrentUserStateLocked();
532 if (userState != null) {
533 // If not called back already.
534 if (!userState.mCurrentAttentionCheckIsFulfilled) {
535 cancel(userState,
536 AttentionService.ATTENTION_FAILURE_TIMED_OUT);
537 }
538
539 }
540 }
Alex Salob81472f2018-12-12 14:44:28 -0800541 }
542 break;
543
544 default:
545 break;
546 }
547 }
548 }
549
Alex Salo1e32c072019-02-13 16:58:01 -0800550 private void cancel(UserState userState, @AttentionFailureCodes int failureCode) {
551 if (userState != null && userState.mService != null) {
552 try {
553 userState.mService.cancelAttentionCheck(
554 userState.mCurrentAttentionCheckRequestCode);
555 } catch (RemoteException e) {
556 Slog.e(LOG_TAG, "Unable to cancel attention check");
Alex Salob81472f2018-12-12 14:44:28 -0800557 }
Alex Salo1e32c072019-02-13 16:58:01 -0800558
559 if (userState.mPendingAttentionCheck != null) {
560 userState.mPendingAttentionCheck.cancel(failureCode);
561 }
562 }
563 }
564
565 @GuardedBy("mLock")
566 private void cancelAndUnbindLocked(UserState userState) {
567 synchronized (mLock) {
568 cancel(userState, AttentionService.ATTENTION_FAILURE_UNKNOWN);
569
570 mContext.unbindService(userState.mConnection);
571 userState.mConnection.cleanupService();
572 mUserStates.remove(userState.mUserId);
Alex Salob81472f2018-12-12 14:44:28 -0800573 }
574 }
575
576 /**
577 * Unbinds and stops the service when the screen off intent is received.
578 * Attention service only makes sense when screen is ON; disconnect and stop service otherwise.
579 */
580 private final class ScreenStateReceiver extends BroadcastReceiver {
581 @Override
582 public void onReceive(Context context, Intent intent) {
583 if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
Alex Salo1e32c072019-02-13 16:58:01 -0800584 cancelAndUnbindLocked(peekCurrentUserStateLocked());
Alex Salob81472f2018-12-12 14:44:28 -0800585 }
586 }
587 }
588}