Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e496b54c authored by Jordan Demeulenaere's avatar Jordan Demeulenaere
Browse files

Add some fakes to SystemUI-test-utils

This CL adds fake implementations for:
 - FgsManagerController
 - UserTracker
 - SecurityController
 - UserInfoController

It also adds a wrapper around a mocked UserSwitcherController to make it
easier to mock in tests while still handling callbacks.

Those fakes are going to be used to test the new implementation of the
QSFooterActions, which are going to be refactored using the modern
Android architecture. See ag/19678215 to see how they are used.

Bug: 242040009
Test: atest FgsManagerControllerTest
Test: atest FooterActionsViewModelTest
Test: atest FooterActionsInteractorTest
Change-Id: I0a3ef36de81c1bd48f33d4d234a4ee903bf40fc6
parent dcd089d3
Loading
Loading
Loading
Loading
+109 −45
Original line number Diff line number Diff line
@@ -68,9 +68,73 @@ import java.util.Objects
import java.util.concurrent.Executor
import javax.inject.Inject
import kotlin.math.max
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow

/** A controller for the dealing with services running in the foreground. */
interface FgsManagerController {
    /** Whether the TaskManager (and therefore this controller) is actually available. */
    val isAvailable: StateFlow<Boolean>

    /** The number of packages with a service running in the foreground. */
    val numRunningPackages: Int

    /**
     * Whether there were new changes to the foreground services since the last [shown][showDialog]
     * dialog was dismissed.
     */
    val newChangesSinceDialogWasDismissed: Boolean

    /**
     * Whether we should show a dot to indicate when [newChangesSinceDialogWasDismissed] is true.
     */
    val showFooterDot: StateFlow<Boolean>

    /**
     * Initialize this controller. This should be called once, before this controller is used for
     * the first time.
     */
    fun init()

    /**
     * Show the foreground services dialog. The dialog will be expanded from [viewLaunchedFrom] if
     * it's not `null`.
     */
    fun showDialog(viewLaunchedFrom: View?)

    /** Add a [OnNumberOfPackagesChangedListener]. */
    fun addOnNumberOfPackagesChangedListener(listener: OnNumberOfPackagesChangedListener)

    /** Remove a [OnNumberOfPackagesChangedListener]. */
    fun removeOnNumberOfPackagesChangedListener(listener: OnNumberOfPackagesChangedListener)

    /** Add a [OnDialogDismissedListener]. */
    fun addOnDialogDismissedListener(listener: OnDialogDismissedListener)

    /** Remove a [OnDialogDismissedListener]. */
    fun removeOnDialogDismissedListener(listener: OnDialogDismissedListener)

    /** Whether we should update the footer visibility. */
    // TODO(b/242040009): Remove this.
    fun shouldUpdateFooterVisibility(): Boolean

    @VisibleForTesting
    fun visibleButtonsCount(): Int

    interface OnNumberOfPackagesChangedListener {
        /** Called when [numRunningPackages] changed. */
        fun onNumberOfPackagesChanged(numPackages: Int)
    }

    interface OnDialogDismissedListener {
        /** Called when a dialog shown using [showDialog] was dismissed. */
        fun onDialogDismissed()
    }
}

@SysUISingleton
class FgsManagerController @Inject constructor(
class FgsManagerControllerImpl @Inject constructor(
    private val context: Context,
    @Main private val mainExecutor: Executor,
    @Background private val backgroundExecutor: Executor,
@@ -82,25 +146,32 @@ class FgsManagerController @Inject constructor(
    private val dialogLaunchAnimator: DialogLaunchAnimator,
    private val broadcastDispatcher: BroadcastDispatcher,
    private val dumpManager: DumpManager
) : IForegroundServiceObserver.Stub(), Dumpable {
) : IForegroundServiceObserver.Stub(), Dumpable, FgsManagerController {

    companion object {
        private const val INTERACTION_JANK_TAG = "active_background_apps"
        private val LOG_TAG = FgsManagerController::class.java.simpleName
        private const val DEFAULT_TASK_MANAGER_ENABLED = true
        private const val DEFAULT_TASK_MANAGER_SHOW_FOOTER_DOT = false
        private const val DEFAULT_TASK_MANAGER_SHOW_STOP_BUTTON_FOR_USER_ALLOWLISTED_APPS = true
    }

    var changesSinceDialog = false
    override var newChangesSinceDialogWasDismissed = false
        private set

    var isAvailable = false
        private set
    var showFooterDot = false
        private set
    var showStopBtnForUserAllowlistedApps = false
        private set
    val _isAvailable = MutableStateFlow(false)
    override val isAvailable: StateFlow<Boolean> = _isAvailable.asStateFlow()

    val _showFooterDot = MutableStateFlow(false)
    override val showFooterDot: StateFlow<Boolean> = _showFooterDot.asStateFlow()

    private var showStopBtnForUserAllowlistedApps = false

    override val numRunningPackages: Int
        get() {
            synchronized(lock) {
                return getNumVisiblePackagesLocked()
            }
        }

    private val lock = Any()

@@ -138,15 +209,7 @@ class FgsManagerController @Inject constructor(
        }
    }

    interface OnNumberOfPackagesChangedListener {
        fun onNumberOfPackagesChanged(numPackages: Int)
    }

    interface OnDialogDismissedListener {
        fun onDialogDismissed()
    }

    fun init() {
    override fun init() {
        synchronized(lock) {
            if (initialized) {
                return
@@ -165,19 +228,19 @@ class FgsManagerController @Inject constructor(
                NAMESPACE_SYSTEMUI,
                backgroundExecutor
            ) {
                isAvailable = it.getBoolean(TASK_MANAGER_ENABLED, isAvailable)
                showFooterDot =
                    it.getBoolean(TASK_MANAGER_SHOW_FOOTER_DOT, showFooterDot)
                _isAvailable.value = it.getBoolean(TASK_MANAGER_ENABLED, _isAvailable.value)
                _showFooterDot.value =
                    it.getBoolean(TASK_MANAGER_SHOW_FOOTER_DOT, _showFooterDot.value)
                showStopBtnForUserAllowlistedApps = it.getBoolean(
                    TASK_MANAGER_SHOW_STOP_BUTTON_FOR_USER_ALLOWLISTED_APPS,
                    showStopBtnForUserAllowlistedApps)
            }

            isAvailable = deviceConfigProxy.getBoolean(
            _isAvailable.value = deviceConfigProxy.getBoolean(
                NAMESPACE_SYSTEMUI,
                TASK_MANAGER_ENABLED, DEFAULT_TASK_MANAGER_ENABLED
            )
            showFooterDot = deviceConfigProxy.getBoolean(
            _showFooterDot.value = deviceConfigProxy.getBoolean(
                NAMESPACE_SYSTEMUI,
                TASK_MANAGER_SHOW_FOOTER_DOT, DEFAULT_TASK_MANAGER_SHOW_FOOTER_DOT
            )
@@ -232,42 +295,45 @@ class FgsManagerController @Inject constructor(
    }

    @GuardedBy("lock")
    val onNumberOfPackagesChangedListeners: MutableSet<OnNumberOfPackagesChangedListener> =
        mutableSetOf()
    private val onNumberOfPackagesChangedListeners =
        mutableSetOf<FgsManagerController.OnNumberOfPackagesChangedListener>()

    @GuardedBy("lock")
    val onDialogDismissedListeners: MutableSet<OnDialogDismissedListener> = mutableSetOf()
    private val onDialogDismissedListeners =
        mutableSetOf<FgsManagerController.OnDialogDismissedListener>()

    fun addOnNumberOfPackagesChangedListener(listener: OnNumberOfPackagesChangedListener) {
    override fun addOnNumberOfPackagesChangedListener(
        listener: FgsManagerController.OnNumberOfPackagesChangedListener
    ) {
        synchronized(lock) {
            onNumberOfPackagesChangedListeners.add(listener)
        }
    }

    fun removeOnNumberOfPackagesChangedListener(listener: OnNumberOfPackagesChangedListener) {
    override fun removeOnNumberOfPackagesChangedListener(
        listener: FgsManagerController.OnNumberOfPackagesChangedListener
    ) {
        synchronized(lock) {
            onNumberOfPackagesChangedListeners.remove(listener)
        }
    }

    fun addOnDialogDismissedListener(listener: OnDialogDismissedListener) {
    override fun addOnDialogDismissedListener(
        listener: FgsManagerController.OnDialogDismissedListener
    ) {
        synchronized(lock) {
            onDialogDismissedListeners.add(listener)
        }
    }

    fun removeOnDialogDismissedListener(listener: OnDialogDismissedListener) {
    override fun removeOnDialogDismissedListener(
        listener: FgsManagerController.OnDialogDismissedListener
    ) {
        synchronized(lock) {
            onDialogDismissedListeners.remove(listener)
        }
    }

    fun getNumRunningPackages(): Int {
        synchronized(lock) {
            return getNumVisiblePackagesLocked()
        }
    }

    private fun getNumVisiblePackagesLocked(): Int {
        return runningServiceTokens.keys.count {
            it.uiControl != UIControl.HIDE_ENTRY && currentProfileIds.contains(it.userId)
@@ -278,7 +344,7 @@ class FgsManagerController @Inject constructor(
        val num = getNumVisiblePackagesLocked()
        if (num != lastNumberOfVisiblePackages) {
            lastNumberOfVisiblePackages = num
            changesSinceDialog = true
            newChangesSinceDialogWasDismissed = true
            onNumberOfPackagesChangedListeners.forEach {
                backgroundExecutor.execute {
                    it.onNumberOfPackagesChanged(num)
@@ -287,9 +353,7 @@ class FgsManagerController @Inject constructor(
        }
    }

    @VisibleForTesting
    @JvmName("getNumVisibleButtons")
    internal fun getNumVisibleButtons(): Int {
    override fun visibleButtonsCount(): Int {
        synchronized(lock) {
            return getNumVisibleButtonsLocked()
        }
@@ -301,9 +365,9 @@ class FgsManagerController @Inject constructor(
        }
    }

    fun shouldUpdateFooterVisibility() = dialog == null
    override fun shouldUpdateFooterVisibility() = dialog == null

    fun showDialog(viewLaunchedFrom: View?) {
    override fun showDialog(viewLaunchedFrom: View?) {
        synchronized(lock) {
            if (dialog == null) {

@@ -328,7 +392,7 @@ class FgsManagerController @Inject constructor(
                this.dialog = dialog

                dialog.setOnDismissListener {
                    changesSinceDialog = false
                    newChangesSinceDialogWasDismissed = false
                    synchronized(lock) {
                        this.dialog = null
                        updateAppItemsLocked()
@@ -656,7 +720,7 @@ class FgsManagerController @Inject constructor(
        val pw = IndentingPrintWriter(printwriter)
        synchronized(lock) {
            pw.println("current user profiles = $currentProfileIds")
            pw.println("changesSinceDialog=$changesSinceDialog")
            pw.println("newChangesSinceDialogWasShown=$newChangesSinceDialogWasDismissed")
            pw.println("Running service tokens: [")
            pw.indentIfPossible {
                runningServiceTokens.forEach { (userPackage, startTimeAndTokens) ->
+5 −3
Original line number Diff line number Diff line
@@ -149,9 +149,11 @@ public class QSFgsManagerFooter implements View.OnClickListener,
            mNumberView.setContentDescription(text);
            if (mFgsManagerController.shouldUpdateFooterVisibility()) {
                mRootView.setVisibility(mNumPackages > 0
                        && mFgsManagerController.isAvailable() ? View.VISIBLE : View.GONE);
                int dotVis = mFgsManagerController.getShowFooterDot()
                        && mFgsManagerController.getChangesSinceDialog() ? View.VISIBLE : View.GONE;
                        && mFgsManagerController.isAvailable().getValue() ? View.VISIBLE
                        : View.GONE);
                int dotVis = mFgsManagerController.getShowFooterDot().getValue()
                        && mFgsManagerController.getNewChangesSinceDialogWasDismissed()
                        ? View.VISIBLE : View.GONE;
                mDotView.setVisibility(dotVis);
                mCollapsedDotView.setVisibility(dotVis);
                if (mVisibilityChangedListener != null) {
+6 −0
Original line number Diff line number Diff line
@@ -28,6 +28,8 @@ import com.android.systemui.battery.BatteryMeterView;
import com.android.systemui.dagger.qualifiers.RootView;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.privacy.OngoingPrivacyChip;
import com.android.systemui.qs.FgsManagerController;
import com.android.systemui.qs.FgsManagerControllerImpl;
import com.android.systemui.qs.FooterActionsView;
import com.android.systemui.qs.QSContainerImpl;
import com.android.systemui.qs.QSFooter;
@@ -194,4 +196,8 @@ public interface QSFragmentModule {
    ) {
        return layoutInflater.inflate(R.layout.fgs_footer, footerActionsView, false);
    }

    /** */
    @Binds
    FgsManagerController bindFgsManagerController(FgsManagerControllerImpl impl);
}
 No newline at end of file
+6 −6
Original line number Diff line number Diff line
@@ -188,9 +188,9 @@ public class FgsManagerControllerTest extends SysuiTestCase {
    public void testChangesSinceLastDialog() throws RemoteException {
        setUserProfiles(0);

        Assert.assertFalse(mFmc.getChangesSinceDialog());
        Assert.assertFalse(mFmc.getNewChangesSinceDialogWasDismissed());
        mIForegroundServiceObserver.onForegroundStateChanged(new Binder(), "pkg", 0, true);
        Assert.assertTrue(mFmc.getChangesSinceDialog());
        Assert.assertTrue(mFmc.getNewChangesSinceDialogWasDismissed());
    }

    @Test
@@ -233,14 +233,14 @@ public class FgsManagerControllerTest extends SysuiTestCase {
        final Binder binder = new Binder();
        setShowStopButtonForUserAllowlistedApps(true);
        mIForegroundServiceObserver.onForegroundStateChanged(binder, "pkg", 0, true);
        Assert.assertEquals(1, mFmc.getNumVisibleButtons());
        Assert.assertEquals(1, mFmc.visibleButtonsCount());

        mIForegroundServiceObserver.onForegroundStateChanged(binder, "pkg", 0, false);
        Assert.assertEquals(0, mFmc.getNumVisibleButtons());
        Assert.assertEquals(0, mFmc.visibleButtonsCount());

        setShowStopButtonForUserAllowlistedApps(false);
        mIForegroundServiceObserver.onForegroundStateChanged(binder, "pkg", 0, true);
        Assert.assertEquals(0, mFmc.getNumVisibleButtons());
        Assert.assertEquals(0, mFmc.visibleButtonsCount());
    }

    private void setShowStopButtonForUserAllowlistedApps(boolean enable) {
@@ -269,7 +269,7 @@ public class FgsManagerControllerTest extends SysuiTestCase {
        ArgumentCaptor<BroadcastReceiver> showFgsManagerReceiverArgumentCaptor =
                ArgumentCaptor.forClass(BroadcastReceiver.class);

        FgsManagerController result = new FgsManagerController(
        FgsManagerController result = new FgsManagerControllerImpl(
                mContext,
                mMainExecutor,
                mBackgroundExecutor,
+0 −1
Original line number Diff line number Diff line
@@ -56,7 +56,6 @@ class FakeFeatureFlags : FeatureFlags {
        stringFlags.put(flag.id, value)
    }


    override fun isEnabled(flag: UnreleasedFlag): Boolean = requireBooleanValue(flag.id)

    override fun isEnabled(flag: ReleasedFlag): Boolean = requireBooleanValue(flag.id)
Loading