void setUiTTYMode(int uiTtyMode, Message onComplete) {
   try {
     mImsManager.setUiTTYMode(mPhone.getContext(), mServiceId, uiTtyMode, onComplete);
   } catch (ImsException e) {
     loge("setTTYMode : " + e);
     mPhone.sendErrorResponse(onComplete, e);
   }
 }
  private void processCallStateChange(ImsCall imsCall, ImsPhoneCall.State state, int cause) {
    if (DBG) log("processCallStateChange " + imsCall + " state=" + state + " cause=" + cause);

    if (imsCall == null) return;

    boolean changed = false;
    ImsPhoneConnection conn = findConnection(imsCall);

    if (conn == null) {
      // TODO : what should be done?
      return;
    }

    changed = conn.update(imsCall, state);

    if (state == ImsPhoneCall.State.DISCONNECTED) {
      changed = conn.onDisconnect(cause) || changed;
      // detach the disconnected connections
      conn.getCall().detach(conn);
      removeConnection(conn);
    }

    if (changed) {
      if (conn.getCall() == mHandoverCall) return;
      updatePhoneState();
      mPhone.notifyPreciseCallStateChanged();
    }
  }
  void clearDisconnected() {
    if (DBG) log("clearDisconnected");

    internalClearDisconnected();

    updatePhoneState();
    mPhone.notifyPreciseCallStateChanged();
  }
        @Override
        public void onCallResumeReceived(ImsCall imsCall) {
          if (DBG) log("onCallResumeReceived");

          if (mOnHoldToneStarted) {
            mPhone.stopOnHoldTone();
            mOnHoldToneStarted = false;
          }
        }
  public void dispose() {
    if (DBG) log("dispose");
    mRingingCall.dispose();
    mBackgroundCall.dispose();
    mForegroundCall.dispose();
    mHandoverCall.dispose();

    clearDisconnected();
    mPhone.getContext().unregisterReceiver(mReceiver);
  }
 private void handleEcmTimer(int action) {
   mPhone.handleTimerInEmergencyCallbackMode(action);
   switch (action) {
     case ImsPhone.CANCEL_ECM_TIMER:
       break;
     case ImsPhone.RESTART_ECM_TIMER:
       break;
     default:
       log("handleEcmTimer, unsupported action " + action);
   }
 }
  /* 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 onCallHoldReceived(ImsCall imsCall) {
          if (DBG) log("onCallHoldReceived");

          ImsPhoneConnection conn = findConnection(imsCall);
          if (conn != null && conn.getState() == ImsPhoneCall.State.ACTIVE) {
            if (!mOnHoldToneStarted && ImsPhoneCall.isLocalTone(imsCall)) {
              mPhone.startOnHoldTone();
              mOnHoldToneStarted = true;
            }
          }
        }
 @Override
 public void onCallResumeFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) {
   // TODO : What should be done?
   // If we are in the midst of swapping the FG and BG calls and we got a resume fail, we
   // need to swap back the FG and BG calls.
   if (mSwitchingFgAndBgCalls && imsCall == mCallExpectedToResume) {
     mForegroundCall.switchWith(mBackgroundCall);
     mCallExpectedToResume = null;
     mSwitchingFgAndBgCalls = false;
   }
   mPhone.notifySuppServiceFailed(Phone.SuppService.RESUME);
 }
  private void getImsService() {
    if (DBG) log("getImsService");
    mImsManager = ImsManager.getInstance(mPhone.getContext(), mPhone.getPhoneId());
    try {
      mServiceId =
          mImsManager.open(
              ImsServiceClass.MMTEL,
              createIncomingCallPendingIntent(),
              mImsConnectionStateListener);

      // Get the ECBM interface and set IMSPhone's listener object for notifications
      getEcbmInterface().setEcbmStateListener(mPhone.mImsEcbmStateListener);
      if (mPhone.isInEcm()) {
        // Call exit ECBM which will invoke onECBMExited
        mPhone.exitEmergencyCallbackMode();
      }
      int mPreferredTtyMode =
          Settings.Secure.getInt(
              mPhone.getContext().getContentResolver(),
              Settings.Secure.PREFERRED_TTY_MODE,
              Phone.TTY_MODE_OFF);
      mImsManager.setUiTTYMode(mPhone.getContext(), mServiceId, mPreferredTtyMode, null);

    } catch (ImsException e) {
      loge("getImsService: " + e);
      // Leave mImsManager as null, then CallStateException will be thrown when dialing
      mImsManager = null;
    }
  }
  @Override
  public void handleMessage(Message msg) {
    AsyncResult ar;
    if (DBG) log("handleMessage what=" + msg.what);

    switch (msg.what) {
      case EVENT_HANGUP_PENDINGMO:
        if (mPendingMO != null) {
          mPendingMO.onDisconnect();
          removeConnection(mPendingMO);
          mPendingMO = null;
        }

        updatePhoneState();
        mPhone.notifyPreciseCallStateChanged();
        break;
      case EVENT_RESUME_BACKGROUND:
        try {
          resumeWaitingOrHolding();
        } catch (CallStateException e) {
          if (Phone.DEBUG_PHONE) {
            loge("handleMessage EVENT_RESUME_BACKGROUND exception=" + e);
          }
        }
        break;
      case EVENT_DIAL_PENDINGMO:
        dialInternal(mPendingMO, mClirMode, VideoProfile.VideoState.AUDIO_ONLY);
        break;

      case EVENT_EXIT_ECM_RESPONSE_CDMA:
        // no matter the result, we still do the same here
        if (pendingCallInEcm) {
          dialInternal(mPendingMO, pendingCallClirMode, pendingCallVideoState);
          pendingCallInEcm = false;
        }
        mPhone.unsetOnEcbModeExitResponse(this);
        break;
    }
  }
  boolean canDial() {
    boolean ret;
    int serviceState = mPhone.getServiceState().getState();
    String disableCall = SystemProperties.get(TelephonyProperties.PROPERTY_DISABLE_CALL, "false");

    ret =
        (serviceState != ServiceState.STATE_POWER_OFF)
            && mPendingMO == null
            && !mRingingCall.isRinging()
            && !disableCall.equals("true")
            && (!mForegroundCall.getState().isAlive() || !mBackgroundCall.getState().isAlive());

    return ret;
  }
  ImsPhoneCallTracker(ImsPhone phone) {
    this.mPhone = phone;

    IntentFilter intentfilter = new IntentFilter();
    intentfilter.addAction(ImsManager.ACTION_IMS_INCOMING_CALL);
    mPhone.getContext().registerReceiver(mReceiver, intentfilter);

    Thread t =
        new Thread() {
          public void run() {
            getImsService();
          }
        };
    t.start();
  }
        @Override
        public void onCallMerged(ImsCall call, boolean swapCalls) {
          if (DBG) log("onCallMerged");

          mForegroundCall.merge(mBackgroundCall, mForegroundCall.getState());
          if (swapCalls) {
            try {
              switchWaitingOrHoldingAndActive();
            } catch (CallStateException e) {
              if (Phone.DEBUG_PHONE) {
                loge("Failed swap fg and bg calls on merge exception=" + e);
              }
            }
          }
          updatePhoneState();
          mPhone.notifyPreciseCallStateChanged();
        }
        @Override
        public void onCallUssdMessageReceived(ImsCall call, int mode, String ussdMessage) {
          if (DBG) log("mImsUssdListener onCallUssdMessageReceived mode=" + mode);

          int ussdMode = -1;

          switch (mode) {
            case ImsCall.USSD_MODE_REQUEST:
              ussdMode = CommandsInterface.USSD_MODE_REQUEST;
              break;

            case ImsCall.USSD_MODE_NOTIFY:
              ussdMode = CommandsInterface.USSD_MODE_NOTIFY;
              break;
          }

          mPhone.onIncomingUSSD(ussdMode, ussdMessage);
        }
        /**
         * onCallStartFailed will be invoked when: case 1) Dialing fails case 2) Ringing call is
         * disconnected by local or remote user
         */
        @Override
        public void onCallStartFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) {
          if (DBG) log("onCallStartFailed reasonCode=" + reasonInfo.getCode());

          if (mPendingMO != null) {
            // To initiate dialing circuit-switched call
            if (reasonInfo.getCode() == ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED
                && mBackgroundCall.getState() == ImsPhoneCall.State.IDLE
                && mRingingCall.getState() == ImsPhoneCall.State.IDLE) {
              mForegroundCall.detach(mPendingMO);
              removeConnection(mPendingMO);
              mPendingMO.finalize();
              mPendingMO = null;
              mPhone.initiateSilentRedial();
              return;
            } else {
              int cause = getDisconnectCauseFromReasonInfo(reasonInfo);
              processCallStateChange(imsCall, ImsPhoneCall.State.DISCONNECTED, cause);
            }
            mPendingMO = null;
          }
        }
  private void updatePhoneState() {
    PhoneConstants.State oldState = mState;

    if (mRingingCall.isRinging()) {
      mState = PhoneConstants.State.RINGING;
    } else if (mPendingMO != null || !(mForegroundCall.isIdle() && mBackgroundCall.isIdle())) {
      mState = PhoneConstants.State.OFFHOOK;
    } else {
      mState = PhoneConstants.State.IDLE;
    }

    if (mState == PhoneConstants.State.IDLE && oldState != mState) {
      mVoiceCallEndedRegistrants.notifyRegistrants(new AsyncResult(null, null, null));
    } else if (oldState == PhoneConstants.State.IDLE && oldState != mState) {
      mVoiceCallStartedRegistrants.notifyRegistrants(new AsyncResult(null, null, null));
    }

    if (DBG) log("updatePhoneState oldState=" + oldState + ", newState=" + mState);

    if (mState != oldState) {
      mPhone.notifyPhoneStateChanged();
    }
  }
  /* 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);
    }
  }
 private PendingIntent createIncomingCallPendingIntent() {
   Intent intent = new Intent(ImsManager.ACTION_IMS_INCOMING_CALL);
   intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
   return PendingIntent.getBroadcast(
       mPhone.getContext(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
 }
 @Override
 public void onCallMergeFailed(ImsCall call, ImsReasonInfo reasonInfo) {
   if (DBG) log("onCallMergeFailed reasonInfo=" + reasonInfo);
   mPhone.notifySuppServiceFailed(Phone.SuppService.CONFERENCE);
 }
 @Override
 public void onCallSessionTtyModeReceived(ImsCall call, int mode) {
   mPhone.onTtyModeReceived(mode);
 }
 @Override
 public void onImsConnected() {
   if (DBG) log("onImsConnected");
   mPhone.setServiceState(ServiceState.STATE_IN_SERVICE);
   mPhone.setImsRegistered(true);
 }
 @Override
 public void onImsDisconnected() {
   if (DBG) log("onImsDisconnected");
   mPhone.setServiceState(ServiceState.STATE_OUT_OF_SERVICE);
   mPhone.setImsRegistered(false);
 }
 @Override
 public void onImsResumed() {
   if (DBG) log("onImsResumed");
   mPhone.setServiceState(ServiceState.STATE_IN_SERVICE);
 }
 @Override
 public void onImsSuspended() {
   if (DBG) log("onImsSuspended");
   mPhone.setServiceState(ServiceState.STATE_OUT_OF_SERVICE);
 }
  /** oirMode is one of the CLIR_ constants */
  synchronized Connection dial(String dialString, int clirMode, int videoState)
      throws CallStateException {
    boolean isPhoneInEcmMode =
        SystemProperties.getBoolean(TelephonyProperties.PROPERTY_INECM_MODE, false);
    boolean isEmergencyNumber = PhoneNumberUtils.isEmergencyNumber(dialString);

    if (DBG) log("dial clirMode=" + clirMode);

    // note that this triggers call state changed notif
    clearDisconnected();

    if (mImsManager == null) {
      throw new CallStateException("service not available");
    }

    if (!canDial()) {
      throw new CallStateException("cannot dial in current state");
    }

    if (isPhoneInEcmMode && isEmergencyNumber) {
      handleEcmTimer(ImsPhone.CANCEL_ECM_TIMER);
    }

    boolean holdBeforeDial = false;

    // The new call must be assigned to the foreground call.
    // That call must be idle, so place anything that's
    // there on hold
    if (mForegroundCall.getState() == ImsPhoneCall.State.ACTIVE) {
      if (mBackgroundCall.getState() != ImsPhoneCall.State.IDLE) {
        // we should have failed in !canDial() above before we get here
        throw new CallStateException("cannot dial in current state");
      }
      // foreground call is empty for the newly dialed connection
      holdBeforeDial = true;
      switchWaitingOrHoldingAndActive();
    }

    ImsPhoneCall.State fgState = ImsPhoneCall.State.IDLE;
    ImsPhoneCall.State bgState = ImsPhoneCall.State.IDLE;

    mClirMode = clirMode;

    synchronized (mSyncHold) {
      if (holdBeforeDial) {
        fgState = mForegroundCall.getState();
        bgState = mBackgroundCall.getState();

        // holding foreground call failed
        if (fgState == ImsPhoneCall.State.ACTIVE) {
          throw new CallStateException("cannot dial in current state");
        }

        // holding foreground call succeeded
        if (bgState == ImsPhoneCall.State.HOLDING) {
          holdBeforeDial = false;
        }
      }

      mPendingMO =
          new ImsPhoneConnection(
              mPhone.getContext(), checkForTestEmergencyNumber(dialString), this, mForegroundCall);
    }
    addConnection(mPendingMO);

    if (!holdBeforeDial) {
      if ((!isPhoneInEcmMode) || (isPhoneInEcmMode && isEmergencyNumber)) {
        dialInternal(mPendingMO, clirMode, videoState);
      } else {
        try {
          getEcbmInterface().exitEmergencyCallbackMode();
        } catch (ImsException e) {
          e.printStackTrace();
          throw new CallStateException("service not available");
        }
        mPhone.setOnEcbModeExitResponse(this, EVENT_EXIT_ECM_RESPONSE_CDMA, null);
        pendingCallClirMode = clirMode;
        pendingCallVideoState = videoState;
        pendingCallInEcm = true;
      }
    }

    updatePhoneState();
    mPhone.notifyPreciseCallStateChanged();

    return mPendingMO;
  }
 Connection dial(String dialString, int videoState) throws CallStateException {
   SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mPhone.getContext());
   int oirMode = sp.getInt(PhoneBase.CLIR_KEY, CommandsInterface.CLIR_DEFAULT);
   return dial(dialString, oirMode, videoState);
 }