Add notification cache for VoipCallMonitor to avoid match failure
between notifications and calls.
Sometimes voip app can post notification before the foreground service
delegation started. Cache the already posted notification and match it
with possible calls when we actually start the monitoring.
Bug: 277648806
Test: Manually test with test app transactionalVoipApp
Change-Id: Id4b26245443fa814eab3f7f26c98199772509b2d
diff --git a/src/com/android/server/telecom/voip/VoipCallMonitor.java b/src/com/android/server/telecom/voip/VoipCallMonitor.java
index 67af11d..84fdb5d 100644
--- a/src/com/android/server/telecom/voip/VoipCallMonitor.java
+++ b/src/com/android/server/telecom/voip/VoipCallMonitor.java
@@ -49,7 +49,9 @@
public class VoipCallMonitor extends CallsManagerListenerBase {
private final List<Call> mPendingCalls;
- private final Map<StatusBarNotification, Call> mNotifications;
+ // Same notification may be passed as different object in onNotificationPosted and
+ // onNotificationRemoved. Use its string as key to cache ongoing notifications.
+ private final Map<String, Call> mNotifications;
private final Map<PhoneAccountHandle, Set<Call>> mPhoneAccountHandleListMap;
private ActivityManagerInternal mActivityManagerInternal;
private final Map<PhoneAccountHandle, ServiceConnection> mServices;
@@ -58,6 +60,7 @@
private final HandlerThread mHandlerThread;
private final Handler mHandler;
private final Context mContext;
+ private List<StatusBarNotification> mPendingSBN;
public VoipCallMonitor(Context context, TelecomSystem.SyncRoot lock) {
mContext = context;
@@ -65,6 +68,7 @@
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper());
mPendingCalls = new ArrayList<>();
+ mPendingSBN = new ArrayList<>();
mNotifications = new HashMap<>();
mServices = new HashMap<>();
mPhoneAccountHandleListMap = new HashMap<>();
@@ -74,24 +78,21 @@
@Override
public void onNotificationPosted(StatusBarNotification sbn) {
synchronized (mLock) {
- if (mPendingCalls.isEmpty()) {
- return;
- }
if (sbn.getNotification().isStyle(Notification.CallStyle.class)) {
- String packageName = sbn.getPackageName();
- UserHandle userHandle = sbn.getUser();
-
+ boolean sbnMatched = false;
for (Call call : mPendingCalls) {
- if (packageName != null &&
- packageName.equals(call.getTargetPhoneAccount()
- .getComponentName().getPackageName())
- && userHandle != null
- && userHandle.equals(call.getInitiatingUser())) {
+ if (notificationMatchedCall(sbn, call)) {
mPendingCalls.remove(call);
- mNotifications.put(sbn, call);
+ mNotifications.put(sbn.toString(), call);
+ sbnMatched = true;
break;
}
}
+ if (!sbnMatched) {
+ // notification may posted before we started to monitor the call, cache
+ // this notification and try to match it later with new added call.
+ mPendingSBN.add(sbn);
+ }
}
}
}
@@ -99,12 +100,13 @@
@Override
public void onNotificationRemoved(StatusBarNotification sbn) {
synchronized (mLock) {
+ mPendingSBN.remove(sbn);
if (mNotifications.isEmpty()) {
return;
}
- Call call = mNotifications.getOrDefault(sbn, null);
+ Call call = mNotifications.getOrDefault(sbn.toString(), null);
if (call != null) {
- mNotifications.remove(sbn, call);
+ mNotifications.remove(sbn.toString(), call);
stopFGSDelegation(call.getTargetPhoneAccount());
}
}
@@ -225,15 +227,31 @@
}
private void startMonitorNotification(Call call) {
- mPendingCalls.add(call);
- mHandler.postDelayed(() -> {
- synchronized (mLock) {
- if (mPendingCalls.contains(call)) {
- stopFGSDelegation(call.getTargetPhoneAccount());
- mPendingCalls.remove(call);
+ synchronized (mLock) {
+ boolean sbnMatched = false;
+ for (StatusBarNotification sbn : mPendingSBN) {
+ if (notificationMatchedCall(sbn, call)) {
+ mPendingSBN.remove(sbn);
+ mNotifications.put(sbn.toString(), call);
+ sbnMatched = true;
+ break;
}
}
- }, 5000L);
+ if (!sbnMatched) {
+ // Only continue to
+ mPendingCalls.add(call);
+ mHandler.postDelayed(() -> {
+ synchronized (mLock) {
+ if (mPendingCalls.contains(call)) {
+ Log.i(this, "Notification for voip-call %s haven't "
+ + "posted in time, stop delegation.", call.getId());
+ stopFGSDelegation(call.getTargetPhoneAccount());
+ mPendingCalls.remove(call);
+ }
+ }
+ }, 5000L);
+ }
+ }
}
private void stopMonitorNotification(Call call) {
@@ -249,4 +267,16 @@
public void setNotificationListenerService(NotificationListenerService listener) {
mNotificationListener = listener;
}
+
+ private boolean notificationMatchedCall(StatusBarNotification sbn, Call call) {
+ String packageName = sbn.getPackageName();
+ UserHandle userHandle = sbn.getUser();
+ PhoneAccountHandle accountHandle = call.getTargetPhoneAccount();
+
+ return packageName != null &&
+ packageName.equals(call.getTargetPhoneAccount()
+ .getComponentName().getPackageName())
+ && userHandle != null
+ && userHandle.equals(accountHandle.getUserHandle());
+ }
}