Loading services/core/java/com/android/server/attention/AttentionManagerService.java +32 −27 Original line number Original line Diff line number Diff line Loading @@ -190,9 +190,7 @@ public class AttentionManagerService extends SystemService { final UserState userState = getOrCreateCurrentUserStateLocked(); final UserState userState = getOrCreateCurrentUserStateLocked(); // lazily start the service, which should be very lightweight to start // lazily start the service, which should be very lightweight to start if (!userState.bindLocked()) { userState.bindLocked(); return false; } // throttle frequent requests // throttle frequent requests final AttentionCheckCache cache = userState.mAttentionCheckCache; final AttentionCheckCache cache = userState.mAttentionCheckCache; Loading Loading @@ -310,7 +308,7 @@ public class AttentionManagerService extends SystemService { protected UserState getOrCreateUserStateLocked(int userId) { protected UserState getOrCreateUserStateLocked(int userId) { UserState result = mUserStates.get(userId); UserState result = mUserStates.get(userId); if (result == null) { if (result == null) { result = new UserState(userId, mContext, mLock, mComponentName); result = new UserState(userId, mContext, mLock, mAttentionHandler, mComponentName); mUserStates.put(userId, result); mUserStates.put(userId, result); } } return result; return result; Loading Loading @@ -456,31 +454,33 @@ public class AttentionManagerService extends SystemService { @VisibleForTesting @VisibleForTesting protected static class UserState { protected static class UserState { final ComponentName mComponentName; private final ComponentName mComponentName; final AttentionServiceConnection mConnection = new AttentionServiceConnection(); private final AttentionServiceConnection mConnection = new AttentionServiceConnection(); @GuardedBy("mLock") @GuardedBy("mLock") IAttentionService mService; IAttentionService mService; @GuardedBy("mLock") @GuardedBy("mLock") boolean mBinding; @GuardedBy("mLock") AttentionCheck mCurrentAttentionCheck; AttentionCheck mCurrentAttentionCheck; @GuardedBy("mLock") @GuardedBy("mLock") AttentionCheckCache mAttentionCheckCache; AttentionCheckCache mAttentionCheckCache; @GuardedBy("mLock") private boolean mBinding; @UserIdInt @UserIdInt final int mUserId; private final int mUserId; final Context mContext; private final Context mContext; final Object mLock; private final Object mLock; private final Handler mAttentionHandler; UserState(int userId, Context context, Object lock, ComponentName componentName) { UserState(int userId, Context context, Object lock, Handler handler, ComponentName componentName) { mUserId = userId; mUserId = userId; mContext = Preconditions.checkNotNull(context); mContext = Preconditions.checkNotNull(context); mLock = Preconditions.checkNotNull(lock); mLock = Preconditions.checkNotNull(lock); mComponentName = Preconditions.checkNotNull(componentName); mComponentName = Preconditions.checkNotNull(componentName); mAttentionHandler = handler; } } @GuardedBy("mLock") @GuardedBy("mLock") private void handlePendingCallbackLocked() { private void handlePendingCallbackLocked() { if (!mCurrentAttentionCheck.mIsDispatched) { if (!mCurrentAttentionCheck.mIsDispatched) { Loading @@ -499,26 +499,25 @@ public class AttentionManagerService extends SystemService { /** Binds to the system's AttentionService which provides an actual implementation. */ /** Binds to the system's AttentionService which provides an actual implementation. */ @GuardedBy("mLock") @GuardedBy("mLock") private boolean bindLocked() { private void bindLocked() { // No need to bind if service is binding or has already been bound. // No need to bind if service is binding or has already been bound. if (mBinding || mService != null) { if (mBinding || mService != null) { return true; return; } } final boolean willBind; mBinding = true; final long identity = Binder.clearCallingIdentity(); // mContext.bindServiceAsUser() calls into ActivityManagerService which it may already // hold the lock and had called into PowerManagerService, which holds a lock. try { // That would create a deadlock. To solve that, putting it on a handler. final Intent mServiceIntent = new Intent( mAttentionHandler.post(() -> { final Intent serviceIntent = new Intent( AttentionService.SERVICE_INTERFACE).setComponent( AttentionService.SERVICE_INTERFACE).setComponent( mComponentName); mComponentName); willBind = mContext.bindServiceAsUser(mServiceIntent, mConnection, // Note: no reason to clear the calling identity, we won't have one in a handler. mContext.bindServiceAsUser(serviceIntent, mConnection, Context.BIND_AUTO_CREATE, UserHandle.CURRENT); Context.BIND_AUTO_CREATE, UserHandle.CURRENT); mBinding = willBind; } finally { }); Binder.restoreCallingIdentity(identity); } return willBind; } } private void dump(IndentingPrintWriter pw) { private void dump(IndentingPrintWriter pw) { Loading Loading @@ -587,6 +586,7 @@ public class AttentionManagerService extends SystemService { super(Looper.myLooper()); super(Looper.myLooper()); } } @Override public void handleMessage(Message msg) { public void handleMessage(Message msg) { switch (msg.what) { switch (msg.what) { // Do not occupy resources when not in use - unbind proactively. // Do not occupy resources when not in use - unbind proactively. Loading Loading @@ -651,7 +651,12 @@ public class AttentionManagerService extends SystemService { return; return; } } mContext.unbindService(userState.mConnection); mAttentionHandler.post(() -> mContext.unbindService(userState.mConnection)); // Note: this will set mBinding to false even though it could still be trying to bind // (i.e. the runnable was posted in bindLocked but then cancelAndUnbindLocked was // called before it's run yet). This is a safe state at the moment, // since it will eventually, but feels like a source for confusion down the road and // may cause some expensive and unnecessary work to be done. userState.mConnection.cleanupService(); userState.mConnection.cleanupService(); mUserStates.remove(userState.mUserId); mUserStates.remove(userState.mUserId); } } Loading services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java +1 −0 Original line number Original line Diff line number Diff line Loading @@ -86,6 +86,7 @@ public class AttentionManagerServiceTest { UserState mUserState = new UserState(0, UserState mUserState = new UserState(0, mContext, mContext, mLock, mLock, mMockHandler, componentName); componentName); mUserState.mService = new MockIAttentionService(); mUserState.mService = new MockIAttentionService(); mSpyUserState = spy(mUserState); mSpyUserState = spy(mUserState); Loading Loading
services/core/java/com/android/server/attention/AttentionManagerService.java +32 −27 Original line number Original line Diff line number Diff line Loading @@ -190,9 +190,7 @@ public class AttentionManagerService extends SystemService { final UserState userState = getOrCreateCurrentUserStateLocked(); final UserState userState = getOrCreateCurrentUserStateLocked(); // lazily start the service, which should be very lightweight to start // lazily start the service, which should be very lightweight to start if (!userState.bindLocked()) { userState.bindLocked(); return false; } // throttle frequent requests // throttle frequent requests final AttentionCheckCache cache = userState.mAttentionCheckCache; final AttentionCheckCache cache = userState.mAttentionCheckCache; Loading Loading @@ -310,7 +308,7 @@ public class AttentionManagerService extends SystemService { protected UserState getOrCreateUserStateLocked(int userId) { protected UserState getOrCreateUserStateLocked(int userId) { UserState result = mUserStates.get(userId); UserState result = mUserStates.get(userId); if (result == null) { if (result == null) { result = new UserState(userId, mContext, mLock, mComponentName); result = new UserState(userId, mContext, mLock, mAttentionHandler, mComponentName); mUserStates.put(userId, result); mUserStates.put(userId, result); } } return result; return result; Loading Loading @@ -456,31 +454,33 @@ public class AttentionManagerService extends SystemService { @VisibleForTesting @VisibleForTesting protected static class UserState { protected static class UserState { final ComponentName mComponentName; private final ComponentName mComponentName; final AttentionServiceConnection mConnection = new AttentionServiceConnection(); private final AttentionServiceConnection mConnection = new AttentionServiceConnection(); @GuardedBy("mLock") @GuardedBy("mLock") IAttentionService mService; IAttentionService mService; @GuardedBy("mLock") @GuardedBy("mLock") boolean mBinding; @GuardedBy("mLock") AttentionCheck mCurrentAttentionCheck; AttentionCheck mCurrentAttentionCheck; @GuardedBy("mLock") @GuardedBy("mLock") AttentionCheckCache mAttentionCheckCache; AttentionCheckCache mAttentionCheckCache; @GuardedBy("mLock") private boolean mBinding; @UserIdInt @UserIdInt final int mUserId; private final int mUserId; final Context mContext; private final Context mContext; final Object mLock; private final Object mLock; private final Handler mAttentionHandler; UserState(int userId, Context context, Object lock, ComponentName componentName) { UserState(int userId, Context context, Object lock, Handler handler, ComponentName componentName) { mUserId = userId; mUserId = userId; mContext = Preconditions.checkNotNull(context); mContext = Preconditions.checkNotNull(context); mLock = Preconditions.checkNotNull(lock); mLock = Preconditions.checkNotNull(lock); mComponentName = Preconditions.checkNotNull(componentName); mComponentName = Preconditions.checkNotNull(componentName); mAttentionHandler = handler; } } @GuardedBy("mLock") @GuardedBy("mLock") private void handlePendingCallbackLocked() { private void handlePendingCallbackLocked() { if (!mCurrentAttentionCheck.mIsDispatched) { if (!mCurrentAttentionCheck.mIsDispatched) { Loading @@ -499,26 +499,25 @@ public class AttentionManagerService extends SystemService { /** Binds to the system's AttentionService which provides an actual implementation. */ /** Binds to the system's AttentionService which provides an actual implementation. */ @GuardedBy("mLock") @GuardedBy("mLock") private boolean bindLocked() { private void bindLocked() { // No need to bind if service is binding or has already been bound. // No need to bind if service is binding or has already been bound. if (mBinding || mService != null) { if (mBinding || mService != null) { return true; return; } } final boolean willBind; mBinding = true; final long identity = Binder.clearCallingIdentity(); // mContext.bindServiceAsUser() calls into ActivityManagerService which it may already // hold the lock and had called into PowerManagerService, which holds a lock. try { // That would create a deadlock. To solve that, putting it on a handler. final Intent mServiceIntent = new Intent( mAttentionHandler.post(() -> { final Intent serviceIntent = new Intent( AttentionService.SERVICE_INTERFACE).setComponent( AttentionService.SERVICE_INTERFACE).setComponent( mComponentName); mComponentName); willBind = mContext.bindServiceAsUser(mServiceIntent, mConnection, // Note: no reason to clear the calling identity, we won't have one in a handler. mContext.bindServiceAsUser(serviceIntent, mConnection, Context.BIND_AUTO_CREATE, UserHandle.CURRENT); Context.BIND_AUTO_CREATE, UserHandle.CURRENT); mBinding = willBind; } finally { }); Binder.restoreCallingIdentity(identity); } return willBind; } } private void dump(IndentingPrintWriter pw) { private void dump(IndentingPrintWriter pw) { Loading Loading @@ -587,6 +586,7 @@ public class AttentionManagerService extends SystemService { super(Looper.myLooper()); super(Looper.myLooper()); } } @Override public void handleMessage(Message msg) { public void handleMessage(Message msg) { switch (msg.what) { switch (msg.what) { // Do not occupy resources when not in use - unbind proactively. // Do not occupy resources when not in use - unbind proactively. Loading Loading @@ -651,7 +651,12 @@ public class AttentionManagerService extends SystemService { return; return; } } mContext.unbindService(userState.mConnection); mAttentionHandler.post(() -> mContext.unbindService(userState.mConnection)); // Note: this will set mBinding to false even though it could still be trying to bind // (i.e. the runnable was posted in bindLocked but then cancelAndUnbindLocked was // called before it's run yet). This is a safe state at the moment, // since it will eventually, but feels like a source for confusion down the road and // may cause some expensive and unnecessary work to be done. userState.mConnection.cleanupService(); userState.mConnection.cleanupService(); mUserStates.remove(userState.mUserId); mUserStates.remove(userState.mUserId); } } Loading
services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java +1 −0 Original line number Original line Diff line number Diff line Loading @@ -86,6 +86,7 @@ public class AttentionManagerServiceTest { UserState mUserState = new UserState(0, UserState mUserState = new UserState(0, mContext, mContext, mLock, mLock, mMockHandler, componentName); componentName); mUserState.mService = new MockIAttentionService(); mUserState.mService = new MockIAttentionService(); mSpyUserState = spy(mUserState); mSpyUserState = spy(mUserState); Loading