Hold only notification info in VoipCallMonitor.
Currently we're holding the instances of NotificationStatusBar in
VoipCallMonitor to track the notification of ongoing voip call. This
cause leaks of notification. Fix this by only track the necessary
information of the notification.
Bug: 280787082
Test: atest VoipCallMonitorTest
Change-Id: I151ececc22466bc07e083cd83d895dbdd88b8e12
diff --git a/src/com/android/server/telecom/voip/VoipCallMonitor.java b/src/com/android/server/telecom/voip/VoipCallMonitor.java
index 58650f5..9254395 100644
--- a/src/com/android/server/telecom/voip/VoipCallMonitor.java
+++ b/src/com/android/server/telecom/voip/VoipCallMonitor.java
@@ -31,7 +31,6 @@
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.telecom.Log;
-import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import com.android.internal.annotations.VisibleForTesting;
@@ -52,11 +51,11 @@
public class VoipCallMonitor extends CallsManagerListenerBase {
- private final List<Call> mPendingCalls;
+ private final List<Call> mNotificationPendingCalls;
// 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 final Map<NotificationInfo, Call> mNotificationInfoToCallMap;
+ private final Map<PhoneAccountHandle, Set<Call>> mAccountHandleToCallMap;
private ActivityManagerInternal mActivityManagerInternal;
private final Map<PhoneAccountHandle, ServiceConnection> mServices;
private NotificationListenerService mNotificationListener;
@@ -64,7 +63,7 @@
private final HandlerThread mHandlerThread;
private final Handler mHandler;
private final Context mContext;
- private List<StatusBarNotification> mPendingSBN;
+ private List<NotificationInfo> mCachedNotifications;
private TelecomSystem.SyncRoot mSyncRoot;
public VoipCallMonitor(Context context, TelecomSystem.SyncRoot lock) {
@@ -73,11 +72,11 @@
mHandlerThread = new HandlerThread(this.getClass().getSimpleName());
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper());
- mPendingCalls = new ArrayList<>();
- mPendingSBN = new ArrayList<>();
- mNotifications = new HashMap<>();
+ mNotificationPendingCalls = new ArrayList<>();
+ mCachedNotifications = new ArrayList<>();
+ mNotificationInfoToCallMap = new HashMap<>();
mServices = new HashMap<>();
- mPhoneAccountHandleListMap = new HashMap<>();
+ mAccountHandleToCallMap = new HashMap<>();
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
mNotificationListener = new NotificationListenerService() {
@@ -85,19 +84,21 @@
public void onNotificationPosted(StatusBarNotification sbn) {
synchronized (mLock) {
if (sbn.getNotification().isStyle(Notification.CallStyle.class)) {
+ NotificationInfo info = new NotificationInfo(sbn.getPackageName(),
+ sbn.getUser());
boolean sbnMatched = false;
- for (Call call : mPendingCalls) {
- if (notificationMatchedCall(sbn, call)) {
- mPendingCalls.remove(call);
- mNotifications.put(sbn.toString(), call);
+ for (Call call : mNotificationPendingCalls) {
+ if (info.matchesCall(call)) {
+ mNotificationPendingCalls.remove(call);
+ mNotificationInfoToCallMap.put(info, call);
sbnMatched = true;
break;
}
}
if (!sbnMatched) {
- // notification may posted before we started to monitor the call, cache
+ // notification may post before we started to monitor the call, cache
// this notification and try to match it later with new added call.
- mPendingSBN.add(sbn);
+ mCachedNotifications.add(info);
}
}
}
@@ -106,13 +107,16 @@
@Override
public void onNotificationRemoved(StatusBarNotification sbn) {
synchronized (mLock) {
- mPendingSBN.remove(sbn);
- if (mNotifications.isEmpty()) {
+ NotificationInfo info = new NotificationInfo(sbn.getPackageName(),
+ sbn.getUser());
+ mCachedNotifications.remove(info);
+ if (mNotificationInfoToCallMap.isEmpty()) {
return;
}
- Call call = mNotifications.getOrDefault(sbn.toString(), null);
+ Call call = mNotificationInfoToCallMap.getOrDefault(info, null);
if (call != null) {
- mNotifications.remove(sbn.toString(), call);
+ // TODO: fix potential bug for multiple calls of same voip app.
+ mNotificationInfoToCallMap.remove(info, call);
stopFGSDelegation(call);
}
}
@@ -147,7 +151,7 @@
synchronized (mLock) {
PhoneAccountHandle phoneAccountHandle = call.getTargetPhoneAccount();
- Set<Call> callList = mPhoneAccountHandleListMap.computeIfAbsent(phoneAccountHandle,
+ Set<Call> callList = mAccountHandleToCallMap.computeIfAbsent(phoneAccountHandle,
k -> new HashSet<>());
callList.add(call);
@@ -169,7 +173,7 @@
synchronized (mLock) {
stopMonitorWorks(call);
PhoneAccountHandle phoneAccountHandle = call.getTargetPhoneAccount();
- Set<Call> callList = mPhoneAccountHandleListMap.computeIfAbsent(phoneAccountHandle,
+ Set<Call> callList = mAccountHandleToCallMap.computeIfAbsent(phoneAccountHandle,
k -> new HashSet<>());
callList.remove(call);
@@ -218,13 +222,13 @@
synchronized (mLock) {
Log.i(this, "stopFGSDelegation of call %s", call);
PhoneAccountHandle handle = call.getTargetPhoneAccount();
- Set<Call> calls = mPhoneAccountHandleListMap.get(handle);
+ Set<Call> calls = mAccountHandleToCallMap.get(handle);
if (calls != null) {
for (Call c : calls) {
stopMonitorWorks(c);
}
}
- mPhoneAccountHandleListMap.remove(handle);
+ mAccountHandleToCallMap.remove(handle);
if (mActivityManagerInternal != null) {
ServiceConnection fgsConnection = mServices.get(handle);
@@ -247,26 +251,26 @@
private void startMonitorNotification(Call call) {
synchronized (mLock) {
boolean sbnMatched = false;
- for (StatusBarNotification sbn : mPendingSBN) {
- if (notificationMatchedCall(sbn, call)) {
- mPendingSBN.remove(sbn);
- mNotifications.put(sbn.toString(), call);
+ for (NotificationInfo info : mCachedNotifications) {
+ if (info.matchesCall(call)) {
+ mCachedNotifications.remove(info);
+ mNotificationInfoToCallMap.put(info, call);
sbnMatched = true;
break;
}
}
if (!sbnMatched) {
// Only continue to
- mPendingCalls.add(call);
+ mNotificationPendingCalls.add(call);
CompletableFuture<Void> future = new CompletableFuture<>();
mHandler.postDelayed(() -> future.complete(null), 5000L);
future.thenComposeAsync(
(x) -> {
- if (mPendingCalls.contains(call)) {
+ if (mNotificationPendingCalls.contains(call)) {
Log.i(this, "Notification for voip-call %s haven't "
+ "posted in time, stop delegation.", call.getId());
stopFGSDelegation(call);
- mPendingCalls.remove(call);
+ mNotificationPendingCalls.remove(call);
return null;
}
return null;
@@ -276,7 +280,7 @@
}
private void stopMonitorNotification(Call call) {
- mPendingCalls.remove(call);
+ mNotificationPendingCalls.remove(call);
}
@VisibleForTesting
@@ -289,15 +293,20 @@
mNotificationListener = listener;
}
- private boolean notificationMatchedCall(StatusBarNotification sbn, Call call) {
- String packageName = sbn.getPackageName();
- UserHandle userHandle = sbn.getUser();
- PhoneAccountHandle accountHandle = call.getTargetPhoneAccount();
+ private class NotificationInfo {
+ private String mPackageName;
+ private UserHandle mUserHandle;
- return packageName != null &&
- packageName.equals(call.getTargetPhoneAccount()
- .getComponentName().getPackageName())
- && userHandle != null
- && userHandle.equals(accountHandle.getUserHandle());
+ NotificationInfo(String packageName, UserHandle userHandle) {
+ mPackageName = packageName;
+ mUserHandle = userHandle;
+ }
+
+ boolean matchesCall(Call call) {
+ PhoneAccountHandle accountHandle = call.getTargetPhoneAccount();
+ return mPackageName != null && mPackageName.equals(
+ accountHandle.getComponentName().getPackageName())
+ && mUserHandle != null && mUserHandle.equals(accountHandle.getUserHandle());
+ }
}
}