void conference() {
    if (DBG) log("conference");

    ImsCall fgImsCall = mForegroundCall.getImsCall();
    if (fgImsCall == null) {
      log("conference no foreground ims call");
      return;
    }

    ImsCall bgImsCall = mBackgroundCall.getImsCall();
    if (bgImsCall == null) {
      log("conference no background ims call");
      return;
    }

    // Keep track of the connect time of the earliest call so that it can be set on the
    // {@code ImsConference} when it is created.
    long conferenceConnectTime =
        Math.min(
            mForegroundCall.getEarliestConnectTime(), mBackgroundCall.getEarliestConnectTime());
    ImsPhoneConnection foregroundConnection = mForegroundCall.getFirstConnection();
    if (foregroundConnection != null) {
      foregroundConnection.setConferenceConnectTime(conferenceConnectTime);
    }

    try {
      fgImsCall.merge(bgImsCall);
    } catch (ImsException e) {
      log("conference " + e.getMessage());
    }
  }
  void switchWaitingOrHoldingAndActive() throws CallStateException {
    if (DBG) log("switchWaitingOrHoldingAndActive");

    if (mRingingCall.getState() == ImsPhoneCall.State.INCOMING) {
      throw new CallStateException("cannot be in the incoming state");
    }

    if (mForegroundCall.getState() == ImsPhoneCall.State.ACTIVE) {
      ImsCall imsCall = mForegroundCall.getImsCall();
      if (imsCall == null) {
        throw new CallStateException("no ims call");
      }

      // Swap the ImsCalls pointed to by the foreground and background ImsPhoneCalls.
      // If hold or resume later fails, we will swap them back.
      mSwitchingFgAndBgCalls = true;
      mCallExpectedToResume = mBackgroundCall.getImsCall();
      mForegroundCall.switchWith(mBackgroundCall);

      // Hold the foreground call; once the foreground call is held, the background call will
      // be resumed.
      try {
        imsCall.hold();
      } catch (ImsException e) {
        mForegroundCall.switchWith(mBackgroundCall);
        throw new CallStateException(e.getMessage());
      }
    } else if (mBackgroundCall.getState() == ImsPhoneCall.State.HOLDING) {
      resumeWaitingOrHolding();
    }
  }
  /**
   * Accepts a call with the specified video state. The video state is the video state that the user
   * has agreed upon in the InCall UI.
   *
   * @param videoState The video State
   * @throws CallStateException
   */
  void acceptCall(int videoState) throws CallStateException {
    if (DBG) log("acceptCall");

    if (mForegroundCall.getState().isAlive() && mBackgroundCall.getState().isAlive()) {
      throw new CallStateException("cannot accept call");
    }

    if ((mRingingCall.getState() == ImsPhoneCall.State.WAITING)
        && mForegroundCall.getState().isAlive()) {
      setMute(false);
      switchWaitingOrHoldingAndActive();
    } else if (mRingingCall.getState().isRinging()) {
      if (DBG) log("acceptCall: incoming...");
      // Always unmute when answering a new call
      setMute(false);
      try {
        ImsCall imsCall = mRingingCall.getImsCall();
        if (imsCall != null) {
          imsCall.accept(ImsCallProfile.getCallTypeFromVideoState(videoState));
        } else {
          throw new CallStateException("no valid ims call");
        }
      } catch (ImsException e) {
        throw new CallStateException("cannot accept call");
      }
    } else {
      throw new CallStateException("phone not ringing");
    }
  }
  /* package */ void sendDtmf(char c, Message result) {
    if (DBG) log("sendDtmf");

    ImsCall imscall = mForegroundCall.getImsCall();
    if (imscall != null) {
      imscall.sendDtmf(c, result);
    }
  }
  /*package*/ void stopDtmf() {
    if (DBG) log("stopDtmf");

    ImsCall imscall = mForegroundCall.getImsCall();
    if (imscall != null) {
      imscall.stopDtmf();
    } else {
      loge("stopDtmf : no foreground call");
    }
  }
  /* package */ void hangup(ImsPhoneCall call) throws CallStateException {
    if (DBG) log("hangup call");

    if (call.getConnections().size() == 0) {
      throw new CallStateException("no connections");
    }

    ImsCall imsCall = call.getImsCall();
    boolean rejectCall = false;

    if (call == mRingingCall) {
      if (Phone.DEBUG_PHONE) log("(ringing) hangup incoming");
      rejectCall = true;
    } else if (call == mForegroundCall) {
      if (call.isDialingOrAlerting()) {
        if (Phone.DEBUG_PHONE) {
          log("(foregnd) hangup dialing or alerting...");
        }
      } else {
        if (Phone.DEBUG_PHONE) {
          log("(foregnd) hangup foreground");
        }
        // held call will be resumed by onCallTerminated
      }
    } else if (call == mBackgroundCall) {
      if (Phone.DEBUG_PHONE) {
        log("(backgnd) hangup waiting or background");
      }
    } else {
      throw new CallStateException(
          "ImsPhoneCall " + call + "does not belong to ImsPhoneCallTracker " + this);
    }

    call.onHangupLocal();

    try {
      if (imsCall != null) {
        if (rejectCall) imsCall.reject(ImsReasonInfo.CODE_USER_DECLINE);
        else imsCall.terminate(ImsReasonInfo.CODE_USER_TERMINATED);
      } else if (mPendingMO != null && call == mForegroundCall) {
        // is holding a foreground call
        mPendingMO.update(null, ImsPhoneCall.State.DISCONNECTED);
        mPendingMO.onDisconnect();
        removeConnection(mPendingMO);
        mPendingMO = null;
        updatePhoneState();
        removeMessages(EVENT_DIAL_PENDINGMO);
      }
    } catch (ImsException e) {
      throw new CallStateException(e.getMessage());
    }

    mPhone.notifyPreciseCallStateChanged();
  }
        @Override
        public void onReceive(Context context, Intent intent) {
          if (intent.getAction().equals(ImsManager.ACTION_IMS_INCOMING_CALL)) {
            if (DBG) log("onReceive : incoming call intent");

            if (mImsManager == null) return;

            if (mServiceId < 0) return;

            try {
              // Network initiated USSD will be treated by mImsUssdListener
              boolean isUssd = intent.getBooleanExtra(ImsManager.EXTRA_USSD, false);
              if (isUssd) {
                if (DBG) log("onReceive : USSD");
                mUssdSession = mImsManager.takeCall(mServiceId, intent, mImsUssdListener);
                if (mUssdSession != null) {
                  mUssdSession.accept(ImsCallProfile.CALL_TYPE_VOICE);
                }
                return;
              }

              // Normal MT call
              ImsCall imsCall = mImsManager.takeCall(mServiceId, intent, mImsCallListener);
              ImsPhoneConnection conn =
                  new ImsPhoneConnection(
                      mPhone.getContext(), imsCall, ImsPhoneCallTracker.this, mRingingCall);
              addConnection(conn);

              IImsVideoCallProvider imsVideoCallProvider =
                  imsCall.getCallSession().getVideoCallProvider();
              if (imsVideoCallProvider != null) {
                ImsVideoCallProviderWrapper imsVideoCallProviderWrapper =
                    new ImsVideoCallProviderWrapper(imsVideoCallProvider);
                conn.setVideoProvider(imsVideoCallProviderWrapper);
              }

              if ((mForegroundCall.getState() != ImsPhoneCall.State.IDLE)
                  || (mBackgroundCall.getState() != ImsPhoneCall.State.IDLE)) {
                conn.update(imsCall, ImsPhoneCall.State.WAITING);
              }

              mPhone.notifyNewRingingConnection(conn);
              mPhone.notifyIncomingRing();

              updatePhoneState();
              mPhone.notifyPreciseCallStateChanged();
            } catch (ImsException e) {
              loge("onReceive : exception " + e);
            } catch (RemoteException e) {
            }
          }
        }
  /* package */
  void cancelUSSD() {
    if (mUssdSession == null) return;

    try {
      mUssdSession.terminate(ImsReasonInfo.CODE_USER_TERMINATED);
    } catch (ImsException e) {
    }
  }
  private void dialInternal(ImsPhoneConnection conn, int clirMode, int videoState) {
    if (conn == null) {
      return;
    }

    if (conn.getAddress() == null
        || conn.getAddress().length() == 0
        || conn.getAddress().indexOf(PhoneNumberUtils.WILD) >= 0) {
      // Phone number is invalid
      conn.setDisconnectCause(DisconnectCause.INVALID_NUMBER);
      sendEmptyMessageDelayed(EVENT_HANGUP_PENDINGMO, TIMEOUT_HANGUP_PENDINGMO);
      return;
    }

    // Always unmute when initiating a new call
    setMute(false);
    int serviceType =
        PhoneNumberUtils.isEmergencyNumber(conn.getAddress())
            ? ImsCallProfile.SERVICE_TYPE_EMERGENCY
            : ImsCallProfile.SERVICE_TYPE_NORMAL;
    int callType = ImsCallProfile.getCallTypeFromVideoState(videoState);
    // TODO(vt): Is this sufficient?  At what point do we know the video state of the call?
    conn.setVideoState(videoState);

    try {
      String[] callees = new String[] {conn.getAddress()};
      ImsCallProfile profile = mImsManager.createCallProfile(mServiceId, serviceType, callType);
      profile.setCallExtraInt(ImsCallProfile.EXTRA_OIR, clirMode);

      ImsCall imsCall = mImsManager.makeCall(mServiceId, profile, callees, mImsCallListener);
      conn.setImsCall(imsCall);

      IImsVideoCallProvider imsVideoCallProvider = imsCall.getCallSession().getVideoCallProvider();
      if (imsVideoCallProvider != null) {
        ImsVideoCallProviderWrapper imsVideoCallProviderWrapper =
            new ImsVideoCallProviderWrapper(imsVideoCallProvider);
        conn.setVideoProvider(imsVideoCallProviderWrapper);
      }
    } catch (ImsException e) {
      loge("dialInternal : " + e);
      conn.setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED);
      sendEmptyMessageDelayed(EVENT_HANGUP_PENDINGMO, TIMEOUT_HANGUP_PENDINGMO);
    } catch (RemoteException e) {
    }
  }
  /* package */
  void resumeWaitingOrHolding() throws CallStateException {
    if (DBG) log("resumeWaitingOrHolding");

    try {
      if (mForegroundCall.getState().isAlive()) {
        // resume foreground call after holding background call
        // they were switched before holding
        ImsCall imsCall = mForegroundCall.getImsCall();
        if (imsCall != null) imsCall.resume();
      } else if (mRingingCall.getState() == ImsPhoneCall.State.WAITING) {
        // accept waiting call after holding background call
        ImsCall imsCall = mRingingCall.getImsCall();
        if (imsCall != null) imsCall.accept(ImsCallProfile.CALL_TYPE_VOICE);
      } else {
        // Just resume background call.
        // To distinguish resuming call with swapping calls
        // we do not switch calls.here
        // ImsPhoneConnection.update will chnage the parent when completed
        ImsCall imsCall = mBackgroundCall.getImsCall();
        if (imsCall != null) imsCall.resume();
      }
    } catch (ImsException e) {
      throw new CallStateException(e.getMessage());
    }
  }
        @Override
        public void onCallTerminated(ImsCall imsCall, ImsReasonInfo reasonInfo) {
          if (DBG) log("mImsUssdListener onCallTerminated reasonCode=" + reasonInfo.getCode());

          if (imsCall == mUssdSession) {
            mUssdSession = null;
            if (mPendingUssd != null) {
              CommandException ex = new CommandException(CommandException.Error.GENERIC_FAILURE);
              AsyncResult.forMessage(mPendingUssd, null, ex);
              mPendingUssd.sendToTarget();
              mPendingUssd = null;
            }
          }
          imsCall.close();
        }
  /* package */
  void sendUSSD(String ussdString, Message response) {
    if (DBG) log("sendUSSD");

    try {
      if (mUssdSession != null) {
        mUssdSession.sendUssd(ussdString);
        AsyncResult.forMessage(response, null, null);
        response.sendToTarget();
        return;
      }

      String[] callees = new String[] {ussdString};
      ImsCallProfile profile =
          mImsManager.createCallProfile(
              mServiceId, ImsCallProfile.SERVICE_TYPE_NORMAL, ImsCallProfile.CALL_TYPE_VOICE);
      profile.setCallExtraInt(ImsCallProfile.EXTRA_DIALSTRING, ImsCallProfile.DIALSTRING_USSD);

      mUssdSession = mImsManager.makeCall(mServiceId, profile, callees, mImsUssdListener);
    } catch (ImsException e) {
      loge("sendUSSD : " + e);
      mPhone.sendErrorResponse(response, e);
    }
  }