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;
  }