/** * Sets the new call state onto the call and performs some additional logic associated with * setting the state. */ private void setNewState(Call call, int newState, Connection connection) { Preconditions.checkState(call.getState() != newState); // When starting an outgoing call, we need to grab gateway information // for the call, if available, and set it. final RawGatewayInfo info = mCallGatewayManager.getGatewayInfo(connection); if (Call.State.isDialing(newState)) { if (!info.isEmpty()) { call.setGatewayNumber(info.getFormattedGatewayNumber()); call.setGatewayPackage(info.packageName); } } else if (!Call.State.isConnected(newState)) { mCallGatewayManager.clearGatewayData(connection); } call.setState(newState); }
/** * Checks to see if the connection is the first connection in a conference call. If it is a * conference call, we will create a new Conference Call object or update the existing conference * call object for that connection. If it is not a conference call but a previous associated * conference call still exists, we mark it as idle and remove it from the map. In both cases * above, we add the Calls to be updated to the UI. * * @param connection The connection object to check. * @param updatedCalls List of 'updated' calls that will be sent to the UI. */ private boolean updateForConferenceCalls(Connection connection, List<Call> updatedCalls) { // We consider this connection a conference connection if the call it // belongs to is a multiparty call AND it is the first live connection. final boolean isConferenceCallConnection = isPartOfLiveConferenceCall(connection) && getEarliestLiveConnection(connection.getCall()) == connection; boolean changed = false; // If this connection is the main connection for the conference call, then create or update // a Call object for that conference call. if (isConferenceCallConnection) { final Call confCall = getCallFromMap(mConfCallMap, connection, true); changed = updateCallFromConnection(confCall, connection, true); if (changed) { updatedCalls.add(confCall); } if (DBG) Log.d(TAG, "Updating a conference call: " + confCall); // It is possible that through a conference call split, there may be lingering conference // calls where this connection was the main connection. We clean those up here. } else { final Call oldConfCall = getCallFromMap(mConfCallMap, connection, false); // We found a conference call for this connection, which is no longer a conference call. // Kill it! if (oldConfCall != null) { if (DBG) Log.d(TAG, "Cleaning up an old conference call: " + oldConfCall); mConfCallMap.remove(connection); oldConfCall.setState(State.IDLE); changed = true; // add to the list of calls to update updatedCalls.add(oldConfCall); } } return changed; }
/** * Go through the Calls from CallManager and return the list of calls that were updated. 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(); telephonyCalls.addAll(mCallManager.getRingingCalls()); 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) { 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. final boolean shouldUpdate = connection.getState() != com.android.internal.telephony.Call.State.DISCONNECTED && connection.getState() != com.android.internal.telephony.Call.State.IDLE && !connection.getState().isRinging(); final boolean isDisconnecting = connection.getState() == com.android.internal.telephony.Call.State.DISCONNECTING; // For disconnecting calls, we still need to send the update to the UI but we do // not create a new call if the call did not exist. final boolean shouldCreate = shouldUpdate && !isDisconnecting; // New connections return a Call with INVALID state, which does not translate to // a state in the internal.telephony.Call object. This ensures that staleness // check below fails and we always add the item to the update list if it is new. final Call call = getCallFromMap(mCallMap, connection, shouldCreate /* create */); if (call == null || !shouldUpdate) { if (DBG) Log.d(TAG, "update skipped"); continue; } boolean changed = updateCallFromConnection(call, connection, false); if (fullUpdate || changed) { out.add(call); } } // We do a second loop to address conference call scenarios. We do this as a separate // loop to ensure all child calls are up to date before we start updating the parent // conference calls. 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 = mConfCallMap.get(orphanedConnection); call.setState(Call.State.IDLE); out.add(call); mConfCallMap.remove(orphanedConnection); } } }