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

Commit 15bc821a authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Automerger Merge Worker
Browse files

Merge "[Ongoing Call] Make the uidObserver a val so that we're guaranteed to...

Merge "[Ongoing Call] Make the uidObserver a val so that we're guaranteed to not accidentally register it multiple times in a row." into tm-dev am: 9c7fdd7e

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/16988928

Change-Id: Ic9012f206173647169844800d67e99cdfa41e75d
parents b9f43c0c 9c7fdd7e
Loading
Loading
Loading
Loading
+82 −68
Original line number Original line Diff line number Diff line
@@ -69,13 +69,10 @@ class OngoingCallController @Inject constructor(
    private var isFullscreen: Boolean = false
    private var isFullscreen: Boolean = false
    /** Non-null if there's an active call notification. */
    /** Non-null if there's an active call notification. */
    private var callNotificationInfo: CallNotificationInfo? = null
    private var callNotificationInfo: CallNotificationInfo? = null
    /** True if the application managing the call is visible to the user. */
    private var isCallAppVisible: Boolean = false
    private var chipView: View? = null
    private var chipView: View? = null
    private var uidObserver: IUidObserver.Stub? = null


    private val mListeners: MutableList<OngoingCallListener> = mutableListOf()
    private val mListeners: MutableList<OngoingCallListener> = mutableListOf()

    private val uidObserver = CallAppUidObserver()
    private val notifListener = object : NotifCollectionListener {
    private val notifListener = object : NotifCollectionListener {
        // Temporary workaround for b/178406514 for testing purposes.
        // Temporary workaround for b/178406514 for testing purposes.
        //
        //
@@ -160,7 +157,7 @@ class OngoingCallController @Inject constructor(
    fun hasOngoingCall(): Boolean {
    fun hasOngoingCall(): Boolean {
        return callNotificationInfo?.isOngoing == true &&
        return callNotificationInfo?.isOngoing == true &&
                // When the user is in the phone app, don't show the chip.
                // When the user is in the phone app, don't show the chip.
                !isCallAppVisible
                !uidObserver.isCallAppVisible
    }
    }


    override fun addCallback(listener: OngoingCallListener) {
    override fun addCallback(listener: OngoingCallListener) {
@@ -196,7 +193,7 @@ class OngoingCallController @Inject constructor(
            }
            }
            updateChipClickListener()
            updateChipClickListener()


            setUpUidObserver(currentCallNotificationInfo)
            uidObserver.registerWithUid(currentCallNotificationInfo.uid)
            if (!currentCallNotificationInfo.statusBarSwipedAway) {
            if (!currentCallNotificationInfo.statusBarSwipedAway) {
                statusBarWindowController.ifPresent {
                statusBarWindowController.ifPresent {
                    it.setOngoingProcessRequiresStatusBarVisible(true)
                    it.setOngoingProcessRequiresStatusBarVisible(true)
@@ -240,64 +237,6 @@ class OngoingCallController @Inject constructor(
        }
        }
    }
    }


    /**
     * Sets up an [IUidObserver] to monitor the status of the application managing the ongoing call.
     */
    private fun setUpUidObserver(currentCallNotificationInfo: CallNotificationInfo) {
        try {
            isCallAppVisible = isProcessVisibleToUser(
                    iActivityManager.getUidProcessState(
                        currentCallNotificationInfo.uid, context.opPackageName
                    )
            )
        } catch (se: SecurityException) {
            Log.e(TAG, "Security exception when trying to get process state: $se")
            return
        }

        if (uidObserver != null) {
            iActivityManager.unregisterUidObserver(uidObserver)
        }

        uidObserver = object : IUidObserver.Stub() {
            override fun onUidStateChanged(
                uid: Int,
                procState: Int,
                procStateSeq: Long,
                capability: Int
            ) {
                if (uid == currentCallNotificationInfo.uid) {
                    val oldIsCallAppVisible = isCallAppVisible
                    isCallAppVisible = isProcessVisibleToUser(procState)
                    if (oldIsCallAppVisible != isCallAppVisible) {
                        // Animations may be run as a result of the call's state change, so ensure
                        // the listener is notified on the main thread.
                        mainExecutor.execute {
                            mListeners.forEach { l -> l.onOngoingCallStateChanged(animate = true) }
                        }
                    }
                }
            }

            override fun onUidGone(uid: Int, disabled: Boolean) {}
            override fun onUidActive(uid: Int) {}
            override fun onUidIdle(uid: Int, disabled: Boolean) {}
            override fun onUidCachedChanged(uid: Int, cached: Boolean) {}
        }

        try {
            iActivityManager.registerUidObserver(
                uidObserver,
                ActivityManager.UID_OBSERVER_PROCSTATE,
                ActivityManager.PROCESS_STATE_UNKNOWN,
                context.opPackageName
            )
        } catch (se: SecurityException) {
            Log.e(TAG, "Security exception when trying to register uid observer: $se")
            return
        }
    }

    /** Returns true if the given [procState] represents a process that's visible to the user. */
    /** Returns true if the given [procState] represents a process that's visible to the user. */
    private fun isProcessVisibleToUser(procState: Int): Boolean {
    private fun isProcessVisibleToUser(procState: Int): Boolean {
        return procState <= ActivityManager.PROCESS_STATE_TOP
        return procState <= ActivityManager.PROCESS_STATE_TOP
@@ -321,9 +260,7 @@ class OngoingCallController @Inject constructor(
        statusBarWindowController.ifPresent { it.setOngoingProcessRequiresStatusBarVisible(false) }
        statusBarWindowController.ifPresent { it.setOngoingProcessRequiresStatusBarVisible(false) }
        swipeStatusBarAwayGestureHandler.ifPresent { it.removeOnGestureDetectedCallback(TAG) }
        swipeStatusBarAwayGestureHandler.ifPresent { it.removeOnGestureDetectedCallback(TAG) }
        mListeners.forEach { l -> l.onOngoingCallStateChanged(animate = true) }
        mListeners.forEach { l -> l.onOngoingCallStateChanged(animate = true) }
        if (uidObserver != null) {
        uidObserver.unregister()
            iActivityManager.unregisterUidObserver(uidObserver)
        }
    }
    }


    /** Tear down anything related to the chip view to prevent leaks. */
    /** Tear down anything related to the chip view to prevent leaks. */
@@ -380,7 +317,84 @@ class OngoingCallController @Inject constructor(


    override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
    override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
        pw.println("Active call notification: $callNotificationInfo")
        pw.println("Active call notification: $callNotificationInfo")
        pw.println("Call app visible: $isCallAppVisible")
        pw.println("Call app visible: ${uidObserver.isCallAppVisible}")
    }

    /** Our implementation of a [IUidObserver]. */
    inner class CallAppUidObserver : IUidObserver.Stub() {
        /** True if the application managing the call is visible to the user. */
        var isCallAppVisible: Boolean = false
            private set

        /** The UID of the application managing the call. Null if there is no active call. */
        private var callAppUid: Int? = null

        /**
         * True if this observer is currently registered with the activity manager and false
         * otherwise.
         */
        private var isRegistered = false


        /** Register this observer with the activity manager and the given [uid]. */
        fun registerWithUid(uid: Int) {
            if (callAppUid == uid) {
                return
            }
            callAppUid = uid

            try {
                isCallAppVisible = isProcessVisibleToUser(
                    iActivityManager.getUidProcessState(uid, context.opPackageName)
                )
                if (isRegistered) {
                    return
                }
                iActivityManager.registerUidObserver(
                    uidObserver,
                    ActivityManager.UID_OBSERVER_PROCSTATE,
                    ActivityManager.PROCESS_STATE_UNKNOWN,
                    context.opPackageName
                )
                isRegistered = true
            } catch (se: SecurityException) {
                Log.e(TAG, "Security exception when trying to set up uid observer: $se")
            }
        }

        /** Unregister this observer with the activity manager. */
        fun unregister() {
            callAppUid = null
            isRegistered = false
            iActivityManager.unregisterUidObserver(uidObserver)
        }

        override fun onUidStateChanged(
            uid: Int,
            procState: Int,
            procStateSeq: Long,
            capability: Int
        ) {
            val currentCallAppUid = callAppUid ?: return
            if (uid != currentCallAppUid) {
                return
            }

            val oldIsCallAppVisible = isCallAppVisible
            isCallAppVisible = isProcessVisibleToUser(procState)
            if (oldIsCallAppVisible != isCallAppVisible) {
                // Animations may be run as a result of the call's state change, so ensure
                // the listener is notified on the main thread.
                mainExecutor.execute {
                    mListeners.forEach { l -> l.onOngoingCallStateChanged(animate = true) }
                }
            }
        }

        override fun onUidGone(uid: Int, disabled: Boolean) {}
        override fun onUidActive(uid: Int) {}
        override fun onUidIdle(uid: Int, disabled: Boolean) {}
        override fun onUidCachedChanged(uid: Int, cached: Boolean) {}
    }
    }
}
}


+5 −7
Original line number Original line Diff line number Diff line
@@ -205,17 +205,15 @@ class OngoingCallControllerTest : SysuiTestCase() {


    /** Regression test for b/194731244. */
    /** Regression test for b/194731244. */
    @Test
    @Test
    fun onEntryUpdated_calledManyTimes_uidObserverUnregisteredManyTimes() {
    fun onEntryUpdated_calledManyTimes_uidObserverOnlyRegisteredOnce() {
        val numCalls = 4
        for (i in 0 until 4) {

        for (i in 0 until numCalls) {
            // Re-create the notification each time so that it's considered a different object and
            // Re-create the notification each time so that it's considered a different object and
            // observers will get re-registered (and hopefully unregistered).
            // will re-trigger the whole flow.
            notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
            notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
        }
        }


        // There should be 1 observer still registered, so we should unregister n-1 times.
        verify(mockIActivityManager, times(1))
        verify(mockIActivityManager, times(numCalls - 1)).unregisterUidObserver(any())
            .registerUidObserver(any(), any(), any(), any())
    }
    }


    /** Regression test for b/216248574. */
    /** Regression test for b/216248574. */