Loading packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +7 −6 Original line number Diff line number Diff line Loading @@ -203,7 +203,6 @@ import java.util.Map.Entry; import java.util.Optional; import java.util.Set; import java.util.TimeZone; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.stream.Collectors; Loading Loading @@ -2440,7 +2439,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab handleDevicePolicyManagerStateChanged(msg.arg1); break; case MSG_USER_SWITCHING: handleUserSwitching(msg.arg1, (CountDownLatch) msg.obj); handleUserSwitching(msg.arg1, (Runnable) msg.obj); break; case MSG_USER_SWITCH_COMPLETE: handleUserSwitchComplete(msg.arg1); Loading Loading @@ -2741,10 +2740,12 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } private final UserTracker.Callback mUserChangedCallback = new UserTracker.Callback() { @Override public void onUserChanging(int newUser, Context userContext, CountDownLatch latch) { public void onUserChanging(int newUser, @NonNull Context userContext, @NonNull Runnable resultCallback) { mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_SWITCHING, newUser, 0, latch)); newUser, 0, resultCallback)); } @Override Loading Loading @@ -3575,7 +3576,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab * Handle {@link #MSG_USER_SWITCHING} */ @VisibleForTesting void handleUserSwitching(int userId, CountDownLatch latch) { void handleUserSwitching(int userId, Runnable resultCallback) { mLogger.logUserSwitching(userId, "from UserTracker"); Assert.isMainThread(); clearBiometricRecognized(); Loading @@ -3589,7 +3590,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab cb.onUserSwitching(userId); } } latch.countDown(); resultCallback.run(); } /** Loading packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +6 −7 Original line number Diff line number Diff line Loading @@ -117,7 +117,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.function.Supplier; Loading Loading @@ -492,9 +491,9 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis notifySystemUiStateFlags(mSysUiState.getFlags()); notifyConnectionChanged(); if (mLatchForOnUserChanging != null) { mLatchForOnUserChanging.countDown(); mLatchForOnUserChanging = null; if (mDoneUserChanging != null) { mDoneUserChanging.run(); mDoneUserChanging = null; } } Loading Loading @@ -550,14 +549,14 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis } }; private CountDownLatch mLatchForOnUserChanging; private Runnable mDoneUserChanging; private final UserTracker.Callback mUserChangedCallback = new UserTracker.Callback() { @Override public void onUserChanging(int newUser, @NonNull Context userContext, CountDownLatch latch) { @NonNull Runnable resultCallback) { mConnectionBackoffAttempts = 0; mLatchForOnUserChanging = latch; mDoneUserChanging = resultCallback; internalConnectToCurrentUser("User changed"); } }; Loading packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt +6 −7 Original line number Diff line number Diff line Loading @@ -19,7 +19,6 @@ package com.android.systemui.settings import android.content.Context import android.content.pm.UserInfo import android.os.UserHandle import java.util.concurrent.CountDownLatch import java.util.concurrent.Executor /** Loading Loading @@ -68,8 +67,8 @@ interface UserTracker : UserContentResolverProvider, UserContextProvider { interface Callback { /** * Same as {@link onUserChanging(Int, Context, CountDownLatch)} but the latch will be * auto-decremented after the completion of this method. * Same as {@link onUserChanging(Int, Context, Runnable)} but the callback will be * called automatically after the completion of this method. */ fun onUserChanging(newUser: Int, userContext: Context) {} Loading @@ -78,12 +77,12 @@ interface UserTracker : UserContentResolverProvider, UserContextProvider { * Override this method to run things while the screen is frozen for the user switch. * Please use {@link #onUserChanged} if the task doesn't need to push the unfreezing of the * screen further. Please be aware that code executed in this callback will lengthen the * user switch duration. When overriding this method, countDown() MUST be called on the * latch once execution is complete. * user switch duration. When overriding this method, resultCallback#run() MUST be called * once the execution is complete. */ fun onUserChanging(newUser: Int, userContext: Context, latch: CountDownLatch) { fun onUserChanging(newUser: Int, userContext: Context, resultCallback: Runnable) { onUserChanging(newUser, userContext) latch.countDown() resultCallback.run() } /** Loading packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt +73 −7 Original line number Diff line number Diff line Loading @@ -33,11 +33,22 @@ import androidx.annotation.GuardedBy import androidx.annotation.WorkerThread import com.android.systemui.Dumpable import com.android.systemui.dump.DumpManager import com.android.systemui.flags.FeatureFlagsClassic import com.android.systemui.flags.Flags import com.android.systemui.util.Assert import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex import java.io.PrintWriter import java.lang.ref.WeakReference import java.util.concurrent.CountDownLatch import java.util.concurrent.Executor import javax.inject.Provider import kotlin.properties.ReadWriteProperty import kotlin.reflect.KProperty Loading @@ -58,20 +69,26 @@ import kotlin.reflect.KProperty */ open class UserTrackerImpl internal constructor( private val context: Context, private val featureFlagsProvider: Provider<FeatureFlagsClassic>, private val userManager: UserManager, private val iActivityManager: IActivityManager, private val dumpManager: DumpManager, private val backgroundHandler: Handler private val appScope: CoroutineScope, private val backgroundContext: CoroutineDispatcher, private val backgroundHandler: Handler, ) : UserTracker, Dumpable, BroadcastReceiver() { companion object { private const val TAG = "UserTrackerImpl" private const val USER_CHANGE_THRESHOLD = 5L * 1000 // 5 sec } var initialized = false private set private val mutex = Any() private val isBackgroundUserSwitchEnabled: Boolean get() = featureFlagsProvider.get().isEnabled(Flags.USER_TRACKER_BACKGROUND_CALLBACKS) override var userId: Int by SynchronizedDelegate(context.userId) protected set Loading Loading @@ -103,6 +120,10 @@ open class UserTrackerImpl internal constructor( @GuardedBy("callbacks") private val callbacks: MutableList<DataItem> = ArrayList() private var beforeUserSwitchingJob: Job? = null private var userSwitchingJob: Job? = null private var afterUserSwitchingJob: Job? = null open fun initialize(startingUser: Int) { if (initialized) { return Loading @@ -119,7 +140,7 @@ open class UserTrackerImpl internal constructor( addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED) addAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED) } context.registerReceiverForAllUsers(this, filter, null /* permission */, backgroundHandler) context.registerReceiverForAllUsers(this, filter, null, backgroundHandler) registerUserSwitchObserver() Loading Loading @@ -162,17 +183,40 @@ open class UserTrackerImpl internal constructor( private fun registerUserSwitchObserver() { iActivityManager.registerUserSwitchObserver(object : UserSwitchObserver() { override fun onBeforeUserSwitching(newUserId: Int) { if (isBackgroundUserSwitchEnabled) { beforeUserSwitchingJob?.cancel() beforeUserSwitchingJob = appScope.launch(backgroundContext) { handleBeforeUserSwitching(newUserId) } } else { handleBeforeUserSwitching(newUserId) } } override fun onUserSwitching(newUserId: Int, reply: IRemoteCallback?) { if (isBackgroundUserSwitchEnabled) { userSwitchingJob?.cancel() userSwitchingJob = appScope.launch(backgroundContext) { handleUserSwitchingCoroutines(newUserId) { reply?.sendResult(null) } } } else { handleUserSwitching(newUserId) reply?.sendResult(null) } } override fun onUserSwitchComplete(newUserId: Int) { if (isBackgroundUserSwitchEnabled) { afterUserSwitchingJob?.cancel() afterUserSwitchingJob = appScope.launch(backgroundContext) { handleUserSwitchComplete(newUserId) } } else { handleUserSwitchComplete(newUserId) } } }, TAG) } Loading @@ -195,7 +239,7 @@ open class UserTrackerImpl internal constructor( val callback = it.callback.get() if (callback != null) { it.executor.execute { callback.onUserChanging(userId, userContext, latch) callback.onUserChanging(userId, userContext) { latch.countDown() } } } else { latch.countDown() Loading @@ -204,6 +248,28 @@ open class UserTrackerImpl internal constructor( latch.await() } @WorkerThread protected open suspend fun handleUserSwitchingCoroutines(newUserId: Int, onDone: () -> Unit) = coroutineScope { Assert.isNotMainThread() Log.i(TAG, "Switching to user $newUserId") for (callbackDataItem in synchronized(callbacks) { callbacks.toList() }) { val callback: UserTracker.Callback = callbackDataItem.callback.get() ?: continue launch(callbackDataItem.executor.asCoroutineDispatcher()) { val mutex = Mutex(true) val thresholdLogJob = launch(backgroundContext) { delay(USER_CHANGE_THRESHOLD) Log.e(TAG, "Failed to finish $callback in time") } callback.onUserChanging(userId, userContext) { mutex.unlock() } mutex.lock() thresholdLogJob.cancel() }.join() } onDone() } @WorkerThread protected open fun handleUserSwitchComplete(newUserId: Int) { Assert.isNotMainThread() Loading packages/SystemUI/src/com/android/systemui/settings/dagger/MultiUserUtilsModule.java +12 −2 Original line number Diff line number Diff line Loading @@ -25,8 +25,10 @@ import android.os.UserManager; import com.android.systemui.CoreStartable; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Application; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlagsClassic; import com.android.systemui.settings.DisplayTracker; import com.android.systemui.settings.DisplayTrackerImpl; import com.android.systemui.settings.UserContentResolverProvider; Loading @@ -42,6 +44,11 @@ import dagger.Provides; import dagger.multibindings.ClassKey; import dagger.multibindings.IntoMap; import javax.inject.Provider; import kotlinx.coroutines.CoroutineDispatcher; import kotlinx.coroutines.CoroutineScope; /** * Dagger Module for classes found within the com.android.systemui.settings package. */ Loading @@ -60,14 +67,17 @@ public abstract class MultiUserUtilsModule { @Provides static UserTracker provideUserTracker( Context context, Provider<FeatureFlagsClassic> featureFlagsProvider, UserManager userManager, IActivityManager iActivityManager, DumpManager dumpManager, @Application CoroutineScope appScope, @Background CoroutineDispatcher backgroundDispatcher, @Background Handler handler ) { int startingUser = ActivityManager.getCurrentUser(); UserTrackerImpl tracker = new UserTrackerImpl(context, userManager, iActivityManager, dumpManager, handler); UserTrackerImpl tracker = new UserTrackerImpl(context, featureFlagsProvider, userManager, iActivityManager, dumpManager, appScope, backgroundDispatcher, handler); tracker.initialize(startingUser); return tracker; } Loading Loading
packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +7 −6 Original line number Diff line number Diff line Loading @@ -203,7 +203,6 @@ import java.util.Map.Entry; import java.util.Optional; import java.util.Set; import java.util.TimeZone; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.stream.Collectors; Loading Loading @@ -2440,7 +2439,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab handleDevicePolicyManagerStateChanged(msg.arg1); break; case MSG_USER_SWITCHING: handleUserSwitching(msg.arg1, (CountDownLatch) msg.obj); handleUserSwitching(msg.arg1, (Runnable) msg.obj); break; case MSG_USER_SWITCH_COMPLETE: handleUserSwitchComplete(msg.arg1); Loading Loading @@ -2741,10 +2740,12 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } private final UserTracker.Callback mUserChangedCallback = new UserTracker.Callback() { @Override public void onUserChanging(int newUser, Context userContext, CountDownLatch latch) { public void onUserChanging(int newUser, @NonNull Context userContext, @NonNull Runnable resultCallback) { mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_SWITCHING, newUser, 0, latch)); newUser, 0, resultCallback)); } @Override Loading Loading @@ -3575,7 +3576,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab * Handle {@link #MSG_USER_SWITCHING} */ @VisibleForTesting void handleUserSwitching(int userId, CountDownLatch latch) { void handleUserSwitching(int userId, Runnable resultCallback) { mLogger.logUserSwitching(userId, "from UserTracker"); Assert.isMainThread(); clearBiometricRecognized(); Loading @@ -3589,7 +3590,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab cb.onUserSwitching(userId); } } latch.countDown(); resultCallback.run(); } /** Loading
packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +6 −7 Original line number Diff line number Diff line Loading @@ -117,7 +117,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.function.Supplier; Loading Loading @@ -492,9 +491,9 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis notifySystemUiStateFlags(mSysUiState.getFlags()); notifyConnectionChanged(); if (mLatchForOnUserChanging != null) { mLatchForOnUserChanging.countDown(); mLatchForOnUserChanging = null; if (mDoneUserChanging != null) { mDoneUserChanging.run(); mDoneUserChanging = null; } } Loading Loading @@ -550,14 +549,14 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis } }; private CountDownLatch mLatchForOnUserChanging; private Runnable mDoneUserChanging; private final UserTracker.Callback mUserChangedCallback = new UserTracker.Callback() { @Override public void onUserChanging(int newUser, @NonNull Context userContext, CountDownLatch latch) { @NonNull Runnable resultCallback) { mConnectionBackoffAttempts = 0; mLatchForOnUserChanging = latch; mDoneUserChanging = resultCallback; internalConnectToCurrentUser("User changed"); } }; Loading
packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt +6 −7 Original line number Diff line number Diff line Loading @@ -19,7 +19,6 @@ package com.android.systemui.settings import android.content.Context import android.content.pm.UserInfo import android.os.UserHandle import java.util.concurrent.CountDownLatch import java.util.concurrent.Executor /** Loading Loading @@ -68,8 +67,8 @@ interface UserTracker : UserContentResolverProvider, UserContextProvider { interface Callback { /** * Same as {@link onUserChanging(Int, Context, CountDownLatch)} but the latch will be * auto-decremented after the completion of this method. * Same as {@link onUserChanging(Int, Context, Runnable)} but the callback will be * called automatically after the completion of this method. */ fun onUserChanging(newUser: Int, userContext: Context) {} Loading @@ -78,12 +77,12 @@ interface UserTracker : UserContentResolverProvider, UserContextProvider { * Override this method to run things while the screen is frozen for the user switch. * Please use {@link #onUserChanged} if the task doesn't need to push the unfreezing of the * screen further. Please be aware that code executed in this callback will lengthen the * user switch duration. When overriding this method, countDown() MUST be called on the * latch once execution is complete. * user switch duration. When overriding this method, resultCallback#run() MUST be called * once the execution is complete. */ fun onUserChanging(newUser: Int, userContext: Context, latch: CountDownLatch) { fun onUserChanging(newUser: Int, userContext: Context, resultCallback: Runnable) { onUserChanging(newUser, userContext) latch.countDown() resultCallback.run() } /** Loading
packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt +73 −7 Original line number Diff line number Diff line Loading @@ -33,11 +33,22 @@ import androidx.annotation.GuardedBy import androidx.annotation.WorkerThread import com.android.systemui.Dumpable import com.android.systemui.dump.DumpManager import com.android.systemui.flags.FeatureFlagsClassic import com.android.systemui.flags.Flags import com.android.systemui.util.Assert import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex import java.io.PrintWriter import java.lang.ref.WeakReference import java.util.concurrent.CountDownLatch import java.util.concurrent.Executor import javax.inject.Provider import kotlin.properties.ReadWriteProperty import kotlin.reflect.KProperty Loading @@ -58,20 +69,26 @@ import kotlin.reflect.KProperty */ open class UserTrackerImpl internal constructor( private val context: Context, private val featureFlagsProvider: Provider<FeatureFlagsClassic>, private val userManager: UserManager, private val iActivityManager: IActivityManager, private val dumpManager: DumpManager, private val backgroundHandler: Handler private val appScope: CoroutineScope, private val backgroundContext: CoroutineDispatcher, private val backgroundHandler: Handler, ) : UserTracker, Dumpable, BroadcastReceiver() { companion object { private const val TAG = "UserTrackerImpl" private const val USER_CHANGE_THRESHOLD = 5L * 1000 // 5 sec } var initialized = false private set private val mutex = Any() private val isBackgroundUserSwitchEnabled: Boolean get() = featureFlagsProvider.get().isEnabled(Flags.USER_TRACKER_BACKGROUND_CALLBACKS) override var userId: Int by SynchronizedDelegate(context.userId) protected set Loading Loading @@ -103,6 +120,10 @@ open class UserTrackerImpl internal constructor( @GuardedBy("callbacks") private val callbacks: MutableList<DataItem> = ArrayList() private var beforeUserSwitchingJob: Job? = null private var userSwitchingJob: Job? = null private var afterUserSwitchingJob: Job? = null open fun initialize(startingUser: Int) { if (initialized) { return Loading @@ -119,7 +140,7 @@ open class UserTrackerImpl internal constructor( addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED) addAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED) } context.registerReceiverForAllUsers(this, filter, null /* permission */, backgroundHandler) context.registerReceiverForAllUsers(this, filter, null, backgroundHandler) registerUserSwitchObserver() Loading Loading @@ -162,17 +183,40 @@ open class UserTrackerImpl internal constructor( private fun registerUserSwitchObserver() { iActivityManager.registerUserSwitchObserver(object : UserSwitchObserver() { override fun onBeforeUserSwitching(newUserId: Int) { if (isBackgroundUserSwitchEnabled) { beforeUserSwitchingJob?.cancel() beforeUserSwitchingJob = appScope.launch(backgroundContext) { handleBeforeUserSwitching(newUserId) } } else { handleBeforeUserSwitching(newUserId) } } override fun onUserSwitching(newUserId: Int, reply: IRemoteCallback?) { if (isBackgroundUserSwitchEnabled) { userSwitchingJob?.cancel() userSwitchingJob = appScope.launch(backgroundContext) { handleUserSwitchingCoroutines(newUserId) { reply?.sendResult(null) } } } else { handleUserSwitching(newUserId) reply?.sendResult(null) } } override fun onUserSwitchComplete(newUserId: Int) { if (isBackgroundUserSwitchEnabled) { afterUserSwitchingJob?.cancel() afterUserSwitchingJob = appScope.launch(backgroundContext) { handleUserSwitchComplete(newUserId) } } else { handleUserSwitchComplete(newUserId) } } }, TAG) } Loading @@ -195,7 +239,7 @@ open class UserTrackerImpl internal constructor( val callback = it.callback.get() if (callback != null) { it.executor.execute { callback.onUserChanging(userId, userContext, latch) callback.onUserChanging(userId, userContext) { latch.countDown() } } } else { latch.countDown() Loading @@ -204,6 +248,28 @@ open class UserTrackerImpl internal constructor( latch.await() } @WorkerThread protected open suspend fun handleUserSwitchingCoroutines(newUserId: Int, onDone: () -> Unit) = coroutineScope { Assert.isNotMainThread() Log.i(TAG, "Switching to user $newUserId") for (callbackDataItem in synchronized(callbacks) { callbacks.toList() }) { val callback: UserTracker.Callback = callbackDataItem.callback.get() ?: continue launch(callbackDataItem.executor.asCoroutineDispatcher()) { val mutex = Mutex(true) val thresholdLogJob = launch(backgroundContext) { delay(USER_CHANGE_THRESHOLD) Log.e(TAG, "Failed to finish $callback in time") } callback.onUserChanging(userId, userContext) { mutex.unlock() } mutex.lock() thresholdLogJob.cancel() }.join() } onDone() } @WorkerThread protected open fun handleUserSwitchComplete(newUserId: Int) { Assert.isNotMainThread() Loading
packages/SystemUI/src/com/android/systemui/settings/dagger/MultiUserUtilsModule.java +12 −2 Original line number Diff line number Diff line Loading @@ -25,8 +25,10 @@ import android.os.UserManager; import com.android.systemui.CoreStartable; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Application; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlagsClassic; import com.android.systemui.settings.DisplayTracker; import com.android.systemui.settings.DisplayTrackerImpl; import com.android.systemui.settings.UserContentResolverProvider; Loading @@ -42,6 +44,11 @@ import dagger.Provides; import dagger.multibindings.ClassKey; import dagger.multibindings.IntoMap; import javax.inject.Provider; import kotlinx.coroutines.CoroutineDispatcher; import kotlinx.coroutines.CoroutineScope; /** * Dagger Module for classes found within the com.android.systemui.settings package. */ Loading @@ -60,14 +67,17 @@ public abstract class MultiUserUtilsModule { @Provides static UserTracker provideUserTracker( Context context, Provider<FeatureFlagsClassic> featureFlagsProvider, UserManager userManager, IActivityManager iActivityManager, DumpManager dumpManager, @Application CoroutineScope appScope, @Background CoroutineDispatcher backgroundDispatcher, @Background Handler handler ) { int startingUser = ActivityManager.getCurrentUser(); UserTrackerImpl tracker = new UserTrackerImpl(context, userManager, iActivityManager, dumpManager, handler); UserTrackerImpl tracker = new UserTrackerImpl(context, featureFlagsProvider, userManager, iActivityManager, dumpManager, appScope, backgroundDispatcher, handler); tracker.initialize(startingUser); return tracker; } Loading