Remove orphaned Calls as part of doUpdate().
When a connection is no longer valid in telephony (for whatever reason),
we do not clear it from our list of Calls. This results in the UI
becoming non-dismissable.
We do not expect this condition but have seen cases where connection
objects become orphaned (like when device switches technologies
GSM->CDMA and back). See bug for more information.
Could not repro this case. Did testing by simulating orphaned
connection objects and making sure the UI goes away.
bug:11580121
Change-Id: Iaa4264713bbab29545dff4b85d74d0879366b751
diff --git a/src/com/android/phone/CallModeler.java b/src/com/android/phone/CallModeler.java
index 9f4bb76..e4ed147 100644
--- a/src/com/android/phone/CallModeler.java
+++ b/src/com/android/phone/CallModeler.java
@@ -36,6 +36,7 @@
import com.android.services.telephony.common.Call.State;
import com.google.android.collect.Maps;
+import com.google.android.collect.Sets;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSortedSet;
@@ -46,6 +47,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;
+import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -346,10 +348,14 @@
PhoneGlobals.getInstance().updateWakeState();
}
-
/**
* Go through the Calls from CallManager and return the list of calls that were updated.
- * Or, the full list if requested.
+ * Method also finds any orphaned Calls (Connection objects no longer returned by telephony as
+ * either ringing, foreground, or background). For each orphaned call, it sets the call state
+ * to IDLE and adds it to the list of calls to update.
+ *
+ * @param fullUpdate Add all calls to out parameter including those that have no updates.
+ * @param out List to populate with Calls that have been updated.
*/
private void doUpdate(boolean fullUpdate, List<Call> out) {
final List<com.android.internal.telephony.Call> telephonyCalls = Lists.newArrayList();
@@ -357,6 +363,14 @@
telephonyCalls.addAll(mCallManager.getForegroundCalls());
telephonyCalls.addAll(mCallManager.getBackgroundCalls());
+ // orphanedConnections starts out including all connections we know about.
+ // As we iterate through the connections we get from the telephony layer we
+ // prune this Set down to only the connections we have but telephony no longer
+ // recognizes.
+ final Set<Connection> orphanedConnections = Sets.newHashSet();
+ orphanedConnections.addAll(mCallMap.keySet());
+ orphanedConnections.addAll(mConfCallMap.keySet());
+
// Cycle through all the Connections on all the Calls. Update our Call objects
// to reflect any new state and send the updated Call objects to the handler service.
for (com.android.internal.telephony.Call telephonyCall : telephonyCalls) {
@@ -364,6 +378,10 @@
for (Connection connection : telephonyCall.getConnections()) {
if (DBG) Log.d(TAG, "connection: " + connection + connection.getState());
+ if (orphanedConnections.contains(connection)) {
+ orphanedConnections.remove(connection);
+ }
+
// We only send updates for live calls which are not incoming (ringing).
// Disconnected and incoming calls are handled by onDisconnect and
// onNewRingingConnection.
@@ -404,7 +422,26 @@
for (Connection connection : telephonyCall.getConnections()) {
updateForConferenceCalls(connection, out);
}
+ }
+ // Iterate through orphaned connections, set them to idle, and remove
+ // them from our internal structures.
+ for (Connection orphanedConnection : orphanedConnections) {
+ if (mCallMap.containsKey(orphanedConnection)) {
+ final Call call = mCallMap.get(orphanedConnection);
+ call.setState(Call.State.IDLE);
+ out.add(call);
+
+ mCallMap.remove(orphanedConnection);
+ }
+
+ if (mConfCallMap.containsKey(orphanedConnection)) {
+ final Call call = mCallMap.get(orphanedConnection);
+ call.setState(Call.State.IDLE);
+ out.add(call);
+
+ mConfCallMap.remove(orphanedConnection);
+ }
}
}