Merge "Add notification cache for VoipCallMonitor to avoid match failure between notifications and calls." into udc-dev
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());
+    }
 }