private void onDisconnect(Connection conn) { Log.i(TAG, "onDisconnect"); mVoicePrivacyState = false; final Call call = getCallFromMap(mCallMap, conn, false); if (call != null) { final boolean wasConferenced = call.getState() == State.CONFERENCED; updateCallFromConnection(call, conn, false); for (int i = 0; i < mListeners.size(); ++i) { mListeners.get(i).onDisconnect(call); } // If it was a conferenced call, we need to run the entire update // to make the proper changes to parent conference calls. if (wasConferenced) { onPhoneStateChanged(null); } mCallMap.remove(conn); } if (MSimTelephonyManager.getDefault().isMultiSimEnabled() && (call != null)) { mCallManager.clearDisconnected(call.getSubscription()); } else { mCallManager.clearDisconnected(); } PhoneGlobals.getInstance().updateWakeState(); }
/** Returns a mask of capabilities for the connection such as merge, hold, etc. */ private int getCapabilitiesFor(Connection connection, Call call, boolean isForConference) { final boolean callIsActive = (call.getState() == Call.State.ACTIVE); final boolean callIsBackground = (call.getState() == Call.State.ONHOLD); final Phone phone = connection.getCall().getPhone(); boolean canAddCall = false; boolean canMergeCall = false; boolean canSwapCall = false; boolean canRespondViaText = false; boolean canMute = false; boolean canAddParticipant = false; boolean canModifyCall = false; boolean voicePrivacy = false; final boolean supportHold; final boolean canHold; final boolean genericConf = isForConference && (connection.getCall().getPhone().getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA); if (!MSimTelephonyManager.getDefault().isMultiSimEnabled()) { supportHold = PhoneUtils.okToSupportHold(mCallManager); canHold = (supportHold ? PhoneUtils.okToHoldCall(mCallManager) : false); // only applies to active calls if (callIsActive) { canMergeCall = PhoneUtils.okToMergeCalls(mCallManager); canSwapCall = PhoneUtils.okToSwapCalls(mCallManager); } canAddCall = PhoneUtils.okToAddCall(mCallManager); } else { final int subscription = call.getSubscription(); supportHold = PhoneUtils.okToSupportHold(mCallManager, subscription); canHold = (supportHold ? PhoneUtils.okToHoldCall(mCallManager, subscription) : false); // only applies to active calls if (callIsActive) { canMergeCall = PhoneUtils.okToMergeCalls(mCallManager, subscription); canSwapCall = PhoneUtils.okToSwapCalls(mCallManager, subscription); } canAddCall = PhoneUtils.okToAddCall(mCallManager, subscription); } if (callIsActive || callIsBackground) { canModifyCall = PhoneUtils.isVTModifyAllowed(connection); } canAddParticipant = PhoneUtils.canAddParticipant(mCallManager) && canAddCall; // "Mute": only enabled when the foreground call is ACTIVE. // (It's meaningless while on hold, or while DIALING/ALERTING.) // It's also explicitly disabled during emergency calls or if // emergency callback mode (ECM) is active. boolean isEmergencyCall = false; if (connection != null) { isEmergencyCall = PhoneNumberUtils.isLocalEmergencyNumber(connection.getAddress(), phone.getContext()); } boolean isECM = PhoneUtils.isPhoneInEcm(phone); if (isEmergencyCall || isECM) { // disable "Mute" item canMute = false; } else { canMute = callIsActive; } canRespondViaText = RejectWithTextMessageManager.allowRespondViaSmsForCall(call, connection); // special rules section! // CDMA always has Add if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) { canAddCall = true; } // Voice Privacy for CDMA if ((phone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) && mVoicePrivacyState) { voicePrivacy = true; } int retval = 0x0; if (canHold) { retval |= Capabilities.HOLD; } if (supportHold) { retval |= Capabilities.SUPPORT_HOLD; } if (canAddCall) { retval |= Capabilities.ADD_CALL; } if (canMergeCall) { retval |= Capabilities.MERGE_CALLS; } if (canSwapCall) { retval |= Capabilities.SWAP_CALLS; } if (canRespondViaText) { retval |= Capabilities.RESPOND_VIA_TEXT; } if (canMute) { retval |= Capabilities.MUTE; } if (canAddParticipant) { retval |= Capabilities.ADD_PARTICIPANT; } if (genericConf) { retval |= Capabilities.GENERIC_CONFERENCE; } if (canModifyCall) { retval |= Capabilities.MODIFY_CALL; } if (voicePrivacy) { retval |= Capabilities.VOICE_PRIVACY; } return retval; }
/** * Updates the Call properties to match the state of the connection object that it represents. * * @param call The call object to update. * @param connection The connection object from which to update call. * @param isForConference There are slight differences in how we populate data for conference * calls. This boolean tells us which method to use. */ private boolean updateCallFromConnection( Call call, Connection connection, boolean isForConference) { boolean changed = false; final int newState = translateStateFromTelephony(connection, isForConference); if (call.getState() != newState) { setNewState(call, newState, connection); changed = true; } mapCallDetails(call, connection); final Call.DisconnectCause newDisconnectCause = translateDisconnectCauseFromTelephony(connection.getDisconnectCause()); if (call.getDisconnectCause() != newDisconnectCause) { call.setDisconnectCause(newDisconnectCause); changed = true; } final long oldConnectTime = call.getConnectTime(); if (oldConnectTime != connection.getConnectTime()) { call.setConnectTime(connection.getConnectTime()); changed = true; } // creation time should be fixed call.setCreateTime(connection.getCreateTime()); if (!isForConference) { // Number final String oldNumber = call.getNumber(); String newNumber = connection.getAddress(); RawGatewayInfo info = mCallGatewayManager.getGatewayInfo(connection); if (!info.isEmpty()) { newNumber = info.trueNumber; } if (TextUtils.isEmpty(oldNumber) || !oldNumber.equals(newNumber)) { call.setNumber(newNumber); changed = true; } // Number presentation final int newNumberPresentation = connection.getNumberPresentation(); if (call.getNumberPresentation() != newNumberPresentation) { call.setNumberPresentation(newNumberPresentation); changed = true; } // Name final String oldCnapName = call.getCnapName(); if (TextUtils.isEmpty(oldCnapName) || !oldCnapName.equals(connection.getCnapName())) { call.setCnapName(connection.getCnapName()); changed = true; } // Name Presentation final int newCnapNamePresentation = connection.getCnapNamePresentation(); if (call.getCnapNamePresentation() != newCnapNamePresentation) { call.setCnapNamePresentation(newCnapNamePresentation); changed = true; } } else { // update the list of children by: // 1) Saving the old set // 2) Removing all children // 3) Adding the correct children into the Call // 4) Comparing the new children set with the old children set ImmutableSortedSet<Integer> oldSet = call.getChildCallIds(); call.removeAllChildren(); if (connection.getCall() != null) { for (Connection childConn : connection.getCall().getConnections()) { final Call childCall = getCallFromMap(mCallMap, childConn, false); if (childCall != null && childConn.isAlive()) { call.addChildId(childCall.getCallId()); } } } changed |= !oldSet.equals(call.getChildCallIds()); } // Subscription id, this shall be done when Call object created. if (call.getSubscription() == MSimConstants.INVALID_SUBSCRIPTION) { call.setSubscription(connection.getCall().getPhone().getSubscription()); } /** !!! Uses values from connection and call collected above so this part must be last !!! */ final int newCapabilities = getCapabilitiesFor(connection, call, isForConference); if (call.getCapabilities() != newCapabilities) { call.setCapabilities(newCapabilities); changed = true; } return changed; }