[Ongoing Call] Make the uidObserver a val so that we're guaranteed to
not accidentally register it multiple times in a row. This is a follow-up fix from comments on ag/15503430. Fixes: 216520671 Test: Verify the call chip still disappears when you're in a call and you open the call app. Test: atest OngoingCallControllerTest Change-Id: I9454b27961d9277a6c85e16c196d6ed0ddc6273e Change-Id: I8073f178566754037e25af57d2b9949f0d6a396d Merged-In: I8073f178566754037e25af57d2b9949f0d6a396d
This commit is contained in:
@@ -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) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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. */
|
||||||
|
|||||||
Reference in New Issue
Block a user