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: 9c7fdd7efa

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

Change-Id: Ic9012f206173647169844800d67e99cdfa41e75d
This commit is contained in:
TreeHugger Robot
2022-02-28 18:41:00 +00:00
committed by Automerger Merge Worker
2 changed files with 87 additions and 75 deletions

View File

@@ -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) {}
} }
} }

View File

@@ -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. */