android N 撥打電話流程(MO)

本流程圖基於MTK平臺 Android 7.0,撥打的普通電話,本流程只作爲溝通學習使用

整體流程圖

part_one

part_two

流程中部分重點知識

packages-apps目錄

  • dialer應用的DialpadFragment.onClick中,通過用戶輸入號碼並點擊撥號按鈕(R.id.dialpad_floating_action_button)發起MO
  • 在handleDialButtonPressed方法裏面會判斷輸入框中是否含有號碼,然後通過IntentUtil構造一個intent,並通過startActivityWithErrorToast啓動intent,這裏會判斷 mProhibitedPhoneNumberRegexp = getResources().getString(R.string.config_prohibited_phone_number_regexp);`是否含有默認拒絕撥打的號碼,如果含有彈出含有“Can\’t call this number”字符串的DialogFragment提示用戶。
  • 在startActivityWithErrorToast中,會獲取當前觸摸屏幕的位置信息,並存入intent中,後續啓動incallUI的時候可能會使用Point touchPoint = TouchPointManager.getInstance().getPoint(); 
    if (touchPoint.x != 0 || touchPoint.y != 0) { 
    Bundle extras; 
    // Make sure to not accidentally clobber any existing extras 
    if (intent.hasExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS)) { 
    extras = intent.getParcelableExtra( 
    TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS); 
    } else { 
    extras = new Bundle(); 

    extras.putParcelable(TouchPointManager.TOUCH_POINT, touchPoint); 
    intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, extras); 
    }
  • 在TelecomUtil的這個方法中判斷是否有權限撥打號碼,包括判斷是否是默認的dialer,和是否有”android.permission.CALL_PHONE”;這個權限public static boolean hasCallPhonePermission(Context context) { 
    return isDefaultDialer(context) 
    || hasPermission(context, Manifest.permission.CALL_PHONE); 
    }

packages-services-Telecomm目錄

TelecomServiceImpl.placeCall

  • 這裏會做一些檢查,比如:權限檢查和包檢查,即使call 的權限是關閉的也會走這裏,因爲一些特殊的通話,比如:緊急通話,在後面的UserCallIntentProcessor中會把不是緊急號碼切權限是關閉的通話給結束。
                final boolean hasCallAppOp = mAppOpsManager.noteOp(AppOpsManager.OP_CALL_PHONE,
                        Binder.getCallingUid(), callingPackage) == AppOpsManager.MODE_ALLOWED;

                final boolean hasCallPermission = mContext.checkCallingPermission(CALL_PHONE) ==
                        PackageManager.PERMISSION_GRANTED;
  • 1
  • 2
  • 3
  • 4
  • 5

UserCallIntentProcessor

processIntent

    public void processIntent(Intent intent, String callingPackageName,
            boolean canCallNonEmergency) {
        // Ensure call intents are not processed on devices that are not capable of calling.
        if (!isVoiceCapable()) {//通過com.android.internal.R.bool.config_voice_capable判斷是否支持通話,比如:流量卡就不支持通話,並不會啓動incallUI
            return;
        }

        String action = intent.getAction();

        if (Intent.ACTION_CALL.equals(action) ||
                Intent.ACTION_CALL_PRIVILEGED.equals(action) ||
                Intent.ACTION_CALL_EMERGENCY.equals(action)) {
            processOutgoingCallIntent(intent, callingPackageName, canCallNonEmergency);
        }
    }
    public static final String ACTION_DIAL = "android.intent.action.DIAL";//普通電話進入dialer界面的撥號盤
    public static final String ACTION_CALL = "android.intent.action.CALL";//只能撥打普通電話不能撥打緊急號碼,M即以上必須有android.Manifest.permission#CALL_PHONE權限
    public static final String ACTION_CALL_EMERGENCY = "android.intent.action.CALL_EMERGENCY"; //只能撥打緊急號碼的action
    public static final String ACTION_CALL_PRIVILEGED = "android.intent.action.CALL_PRIVILEGED";//可以撥打任意類型電話的action
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

processOutgoingCallIntent

  • 判斷是正常的通話類型是否是SIP phone
        if (!PhoneAccount.SCHEME_VOICEMAIL.equals(scheme)) {
            handle = Uri.fromParts(PhoneNumberUtils.isUriNumber(uriString) ?
                    PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL, uriString, null);//構造intent的data
        }
    public static boolean isUriNumber(String number) {
        // Note we allow either "@" or "%40" to indicate a URI, in case
        // the passed-in string is URI-escaped.  (Neither "@" nor "%40"
        // will ever be found in a legal PSTN number.)
        return number != null && (number.contains("@") || number.contains("%40"));
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 判斷是否有DISALLOW_OUTGOING_CALLS的限制,如果撥打的是emergency call則忽略這個限制,如果撥打的正常號碼且具有這個限制,或者權限沒有通過,不允許打正常的電話,則彈出錯誤的對話框 like:“Only emergency calls are allowed.”or “This application cannot make outgoing calls without the Phone permission.”
  • 如果是video_call並且打的是emergency_call則需要將video的狀態改成voice來打emergency_call
 intent.putExtra(CallIntentProcessor.KEY_IS_PRIVILEGED_DIALER,
                isDefaultOrSystemDialer(callingPackageName));//判斷是否是默認或者系統的dialer應用,如果是纔可以打emergency_call
  • 1
  • 2

PrimaryCallReceiver

    public void onReceive(Context context, Intent intent) {
        Log.startSession("PCR.oR");
        synchronized (getTelecomSystem().getLock()) {
            if (!ExtensionManager.getCallMgrExt()
                    .shouldPreventVideoCallIfLowBattery(context, intent)) {//如果是video_call的話,判斷當前的電量是否處於低電模式,現在默認是返回false,後續會根據不同項目要求做定製
                getTelecomSystem().getCallIntentProcessor().processIntent(intent);
            }
        }
        Log.endSession();
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

CallIntentProcessor

processOutgoingCallIntent

  • 這個方法裏面主要還是重新拿出intent裏面的數據,並判斷當前是否是主要的dialer,是否sip_phone,是否有phone賬戶,是否是video狀態,用哪一張卡打,是否是IMS通話請求,是否是會議通話請求,根據這些判斷再重新構造intent的Extras,然後通過前面得到的狀態創建一個call,如果是conference的話直接創建鏈接,如果不是則繼續往下執行
        if (intent.hasExtra(TelecomUtils.EXTRA_SLOT)) {
            int slotId = intent.getIntExtra(TelecomUtils.EXTRA_SLOT, -1);
            phoneAccountHandle = TelecomUtils
                    .getPhoneAccountHandleWithSlotId(context, slotId, phoneAccountHandle);//如果有多張卡的話會拿到重那張卡撥打號碼
        }
  • 1
  • 2
  • 3
  • 4
  • 5
        // Send to CallsManager to ensure the InCallUI gets kicked off before the broadcast returns
        Call call = callsManager
                .startOutgoingCall(handle, phoneAccountHandle, clientExtras, initiatingUser);//通過這裏來啓動incallUI
  • 1
  • 2
  • 3

NewOutgoingCallIntentBroadcaster

processIntent

  • 先判斷是否是VoicemailNumber,然後再得到格式化號碼包括大小寫轉換成數字,分隔符的分離,判斷是否是潛在的緊急號碼,再將action爲ACTION_CALL_PRIVILEGED類型的通話變成ACTION_CALL_EMERGENCY或者ACTION_CALL, 
    * - CALL (intent launched by all third party dialers) 
    * - CALL_PRIVILEGED (intent launched by system apps e.g. system Dialer, voice Dialer) 
    * - CALL_EMERGENCY (intent launched by lock screen emergency dialer) 如果是撥打緊急號碼的話會直接調用placeOutgoingCall,否則會執行後面的流程發送廣播。
        boolean isUriNumber = mPhoneNumberUtilsAdapter.isUriNumber(number);//判斷通話類型,是否是sip_phone
        if (!isUriNumber) {
            number = mPhoneNumberUtilsAdapter.convertKeypadLettersToDigits(number);//將號碼字符串的字母轉換成數字
            number = mPhoneNumberUtilsAdapter.stripSeparators(number);//將號碼中的分割符分離
        }

        final boolean isPotentialEmergencyNumber = isPotentialEmergencyNumber(number);//判斷是否是潛在的緊急號碼
        Log.v(this, "isPotentialEmergencyNumber = %s", isPotentialEmergencyNumber);

        rewriteCallIntentAction(intent, isPotentialEmergencyNumber);//將ACTION_CALL_PRIVILEGED變成ACTION_CALL_EMERGENCY或者ACTION_CALL
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

CallsManager

placeOutgoingCall

  • 這裏主要是對CDMA模式的call的廣播,然後就是根據一些狀態判斷speaker的開關,是否是緊急號碼的判斷,然後就是開始創建鏈接

CreateConnectionProcessor

attemptNextPhoneAccount

        if (mCallResponse != null && attempt != null) {
            Log.i(this, "Trying attempt %s", attempt);
            PhoneAccountHandle phoneAccount = attempt.connectionManagerPhoneAccount;
            mService = mRepository.getService(phoneAccount.getComponentName(),
                    phoneAccount.getUserHandle());//得到創建鏈接的service
            if (mService == null) {
                Log.i(this, "Found no connection service for attempt %s", attempt);
                attemptNextPhoneAccount();
            } else {
                mCall.setConnectionManagerPhoneAccount(attempt.connectionManagerPhoneAccount);//設置phoneaccount的ConnectionManager
                mCall.setTargetPhoneAccount(attempt.targetPhoneAccount);
                mCall.setConnectionService(mService);//設置service
                setTimeoutIfNeeded(mService, attempt);

                mService.createConnection(mCall, this);//創建鏈接
            }
        } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

ConnectionServiceWrapper

createConnection

  • 通過binder創建鏈接,如果創建成功,先會判斷callid是否爲null以應對某些特定情況下鏈接創建成功前呼叫就已經斷開了,這種情況下會打印錯誤log和DisconnectCause.ERROR的response返回,然後繼續往下執行創建鏈接。
try {
                    /// M: For VoLTE @{
                    boolean isConferenceDial = call.isConferenceDial();
                    if (isConferenceDial) {
                        logOutgoing("createConference(%s) via %s.", call, getComponentName());
                        mServiceInterface.createConference(//會議鏈接
                                call.getConnectionManagerPhoneAccount(),
                                callId,
                                new ConnectionRequest(
                                        call.getTargetPhoneAccount(),
                                        call.getHandle(),
                                        extras,
                                        call.getVideoState(),
                                        callId),
                                call.getConferenceParticipants(),
                                call.isIncoming());
                    } else {
                        mServiceInterface.createConnection(//普通鏈接
                                call.getConnectionManagerPhoneAccount(),
                                callId,
                                new ConnectionRequest(
                                        call.getTargetPhoneAccount(),
                                        call.getHandle(),
                                        extras,
                                        call.getVideoState(),
                                        callId),
                                call.shouldAttachToExistingConnection(),
                                call.isUnknown());
                    }
                    /// @}
                } catch (RemoteException e) {
                    Log.e(this, e, "Failure to createConnection -- %s", getComponentName());
                    mPendingResponses.remove(callId).handleCreateConnectionFailure(
                            new DisconnectCause(DisconnectCause.ERROR, e.toString()));
                }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

packages-services-Telephony目錄

TelephonyConnectionService

onCreateOutgoingConnection

這裏會做一系列的判斷:

  • 撥打號碼是否爲null,撥打電話的賬戶是否存在
  • 判斷是否是ECC重連(緊急號碼相關),如果是將voiceemail的通話類型轉換成tel,否則判斷當前號碼是否和*228正則表達式匹配,如果匹配就拒絕此次鏈接,因爲這些特殊的數字用於OTASP,如果不禁用可能將LTE鎖定到3G網絡下。
  • 判斷phone是否爲null,如果爲null並且config_checkSimStateBeforeOutgoingCall值爲true就會去檢查當前SIM卡的狀態,並確定是否彈出PIN碼的輸入框
  • 判斷飛行模式是否打開
  • 判斷如果是撥打緊急號碼且當前處於4G only數據鏈接狀態,者取消這次鏈接
  • 判斷當前網絡是否註冊成功
  • 判斷當前是否是緊急號碼並處於isInEcm模式,如果撥打的不是緊急號碼,但是當前處於ECM模式,則KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL這個bool值覺得是否結束當前鏈接(ECM模式是指緊急呼叫回撥模式,一般GSM網絡不支持,CDMA網絡支持)
  • 對service狀態的判斷,處理不部分異常信息
  • 如果是videocall則判斷TTY是否開啓,如果開啓則斷開鏈接(TTY聾啞人專用模式)
  • 鏈接的初始化,number,video狀態這些的配置
    // If configured, reject attempts to dial numbers matching this pattern.
    private static final Pattern CDMA_ACTIVATION_CODE_REGEX_PATTERN =
            Pattern.compile("\\*228[0-9]{0,2}");

        /// M: For ECC change feature @{
        if (isEmergencyNumber) {
            if (mSwitchPhoneHelper == null) {
                mSwitchPhoneHelper = new SwitchPhoneHelper(this, number);
            }
            if (mSwitchPhoneHelper.needToPrepareForDial()) {
                mSwitchPhoneHelper.prepareForDial(
                        new SwitchPhoneHelper.Callback() {
                            @Override
                            public void onComplete(boolean success) {
                                if (connection.getState() == Connection.STATE_DISCONNECTED) {
                                    Log.d(this, "prepareForDial, connection disconnect");
                                } else if (success) {
                                    Log.d(this, "startTurnOnRadio");
                                    startTurnOnRadio(connection, request, number);
                                } else {
                                    /// M: CC: ECC Retry @{
                                    // Assume only one ECC exists
                                    // Not trigger retry since Modem fails to
                                    // power on should be a bug
                                    if (TelephonyConnectionServiceUtil.getInstance()
                                            .isEccRetryOn()) {
                                        Log.d(this, "ECC Retry : clear ECC param");
                                        TelephonyConnectionServiceUtil.getInstance()
                                                .clearEccRetryParams();
                                    }
                                    /// @}
                                    Log.d(this, "prepareForDial, failed to turn on radio");
                                    connection.setDisconnected(
                                            DisconnectCauseUtil.toTelecomDisconnectCause(
                                            android.telephony.DisconnectCause.POWER_OFF,
                                            "Failed to turn on radio."));
                                    connection.destroy();
                                }
                            }
                        });
        /// @}
            } else {
                /// M: CC: ECC Retry @{
                if (!TelephonyConnectionServiceUtil.getInstance().isEccRetryOn()) {
                    Log.d(this, "ECC Retry : set param with Intial ECC.");
                    TelephonyConnectionServiceUtil.getInstance().setEccRetryParams(
                            request,
                            phone.getPhoneId());
                }
                /// @}
                if (useEmergencyCallHelper) {
                    if (mEmergencyCallHelper == null) {
                        mEmergencyCallHelper = new EmergencyCallHelper(this);
                    }
                    final Phone eccPhone = phone;
                    mEmergencyCallHelper.startTurnOnRadioSequence(eccPhone,
                            new EmergencyCallHelper.Callback() {
                                @Override
                                public void onComplete(boolean isRadioReady) {
                                    if (connection.getState() == Connection.STATE_DISCONNECTED) {
                                        Log.d(this, "onCreateOutgoingConnection,"
                                                + " connection disconnected");
                                        // If the connection has already been disconnected,
                                        // do nothing.
                                    } else if (isRadioReady) {
                                        ///M: 4G data only @{
                                        if (TelephonyConnectionServiceUtil.getInstance()
                                                .isDataOnlyMode(eccPhone)) {
                                            Log.d(this, "startTurnOnRadioSequence, 4G data only");
                                            /// M: CC: ECC Retry @{
                                            // Assume only one ECC exists
                                            if (TelephonyConnectionServiceUtil.getInstance()
                                                    .isEccRetryOn()) {
                                                Log.d(this, "ECC Retry : clear ECC param");
                                                TelephonyConnectionServiceUtil.getInstance()
                                                        .clearEccRetryParams();
                                            }
                                            /// @}
                                            connection.setDisconnected(
                                                    DisconnectCauseUtil.toTelecomDisconnectCause(
                                                    android.telephony.DisconnectCause
                                                            .OUTGOING_CANCELED, null));
                                            connection.destroy();
                                            return;
                                        }
                                        /// @}
                                        connection.setInitialized();
                                        placeOutgoingConnection(connection, eccPhone, request);
                                    } else {
                                        /// M: CC: ECC Retry @{
                                        // Assume only one ECC exists
                                        // Not trigger retry since Modem fails to
                                        // power on should be a bug
                                        if (TelephonyConnectionServiceUtil.getInstance()
                                                .isEccRetryOn()) {
                                            Log.d(this, "ECC Retry : clear ECC param");
                                            TelephonyConnectionServiceUtil.getInstance()
                                                    .clearEccRetryParams();
                                        }
                                        /// @}
                                        Log.d(this, "onCreateOutgoingConnection,"
                                                + " failed to turn on radio");
                                        connection.setDisconnected(
                                                DisconnectCauseUtil.toTelecomDisconnectCause(
                                                android.telephony.DisconnectCause.POWER_OFF,
                                                "Failed to turn on radio."));
                                        connection.destroy();
                                    }
                                }
                            });
                } else {
                    placeOutgoingConnection(connection, phone, request);
                }
            }
        } else {
            placeOutgoingConnection(connection, phone, request);
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117

frameworks-opt-telephony目錄

dial

  • 這裏會判斷是否是IMScall,GSMcall,WiFicall,緊急通話,然後通過不同的phone繼續下發dial指令
    public Connection dial(String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras)
            throws CallStateException {
        if (!isPhoneTypeGsm() && uusInfo != null) {
            throw new CallStateException("Sending UUS information NOT supported in CDMA!");
        }

        boolean isEmergency = PhoneNumberUtils.isEmergencyNumber(dialString);
        Phone imsPhone = mImsPhone;

        CarrierConfigManager configManager =
                (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
        boolean alwaysTryImsForEmergencyCarrierConfig = configManager.getConfigForSubId(getSubId())
                .getBoolean(CarrierConfigManager.KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL);

        boolean imsUseEnabled = isImsUseEnabled()
                 && imsPhone != null
                 && (imsPhone.isVolteEnabled() || imsPhone.isWifiCallingEnabled() ||
                 (imsPhone.isVideoEnabled() && VideoProfile.isVideo(videoState)))
                 && (imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE);

        boolean useImsForEmergency = imsPhone != null
                && isEmergency
                && alwaysTryImsForEmergencyCarrierConfig
                && ImsManager.isNonTtyOrTtyOnVolteEnabled(mContext)
                && (imsPhone.getServiceState().getState() != ServiceState.STATE_POWER_OFF);

        /// M: @{
        if (!isPhoneTypeGsm()) {
            useImsForEmergency = false;     //TODO: remove this workaround for ECC fail
        }
        /// @}

        String dialPart = PhoneNumberUtils.extractNetworkPortionAlt(PhoneNumberUtils.
                stripSeparators(dialString));
        boolean isUt = (dialPart.startsWith("*") || dialPart.startsWith("#"))
                && dialPart.endsWith("#");

        boolean useImsForUt = imsPhone != null && imsPhone.isUtEnabled();

        if (DBG) {
            logd("imsUseEnabled=" + imsUseEnabled
                    + ", useImsForEmergency=" + useImsForEmergency
                    + ", useImsForUt=" + useImsForUt
                    + ", isUt=" + isUt
                    + ", imsPhone=" + imsPhone
                    + ", imsPhone.isVolteEnabled()="
                    + ((imsPhone != null) ? imsPhone.isVolteEnabled() : "N/A")
                    + ", imsPhone.isVowifiEnabled()="
                    + ((imsPhone != null) ? imsPhone.isWifiCallingEnabled() : "N/A")
                    + ", imsPhone.isVideoEnabled()="
                    + ((imsPhone != null) ? imsPhone.isVideoEnabled() : "N/A")
                    + ", imsPhone.getServiceState().getState()="
                    + ((imsPhone != null) ? imsPhone.getServiceState().getState() : "N/A"));
        }

        Phone.checkWfcWifiOnlyModeBeforeDial(mImsPhone, mContext);

        /// M: should be removed later, just for debug @{
        Rlog.w(LOG_TAG, "IMS: imsphone = " + imsPhone + "isEmergencyNumber = "
                + PhoneNumberUtils.isEmergencyNumber(dialString));
        if (imsPhone != null) {
            Rlog.w(LOG_TAG, "service state = " + imsPhone.getServiceState().getState());
        }
        /// @}

        if ((imsUseEnabled && (!isUt || useImsForUt)) || useImsForEmergency) {
            /// M: CC: Check GSM call state to avoid InCallMMI dispatching to IMS @{
            // [ALPS02516173],[ALPS02615800]
            if (isInCSCall()) {
                if (DBG) Rlog.d(LOG_TAG, "has CS Call. Don't try IMS PS Call!");
            } else {
            /// @}
                try {
                    /// M: ALPS02137073 3G VT Refactory
                    if (videoState == VideoProfile.STATE_AUDIO_ONLY) {
                        if (DBG) Rlog.d(LOG_TAG, "Trying IMS PS call");
                        return imsPhone.dial(dialString, uusInfo, videoState, intentExtras);
                    } else {
                        if (SystemProperties.get("persist.mtk_vilte_support").equals("1")) {
                            if (DBG) {
                                Rlog.d(LOG_TAG, "Trying IMS PS video call");
                            }
                            return imsPhone.dial(dialString, uusInfo, videoState, intentExtras);
                        } else {
                            /// M: CC: For 3G VT only @{
                            if (DBG) {
                                Rlog.d(LOG_TAG, "Trying (non-IMS) CS video call");
                            }
                            return dialInternal(dialString, uusInfo, videoState, intentExtras);
                            /// @}
                        }
                    }
                } catch (CallStateException e) {
                    if (DBG) logd("IMS PS call exception " + e +
                            "imsUseEnabled =" + imsUseEnabled + ", imsPhone =" + imsPhone);
                    if (!Phone.CS_FALLBACK.equals(e.getMessage())) {
                        CallStateException ce = new CallStateException(e.getMessage());
                        ce.setStackTrace(e.getStackTrace());
                        throw ce;
                    }
                }
            /// M: CC: Check GSM call state to avoid InCallMMI dispatching to IMS @{
            }
            /// @}
        }

        /// M: CC: FTA requires call should be dialed out even out of service @{
        if (SystemProperties.getInt("gsm.gcf.testmode", 0) != 2) {
            if (mSST != null && mSST.mSS.getState() == ServiceState.STATE_OUT_OF_SERVICE
                    && mSST.mSS.getDataRegState() != ServiceState.STATE_IN_SERVICE
                    && !isEmergency) {
                throw new CallStateException("cannot dial in current state");
            }
        }
        /// @}
        if (DBG) logd("Trying (non-IMS) CS call");

        if (isPhoneTypeGsm()) {
            /// M: CC: For 3G VT only @{
            //return dialInternal(dialString, null, VideoProfile.STATE_AUDIO_ONLY, intentExtras);
            return dialInternal(dialString, null, videoState, intentExtras);
            /// @}
        } else {
            return dialInternal(dialString, null, videoState, intentExtras);
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127

dialInternal

這裏主要是判斷是否是MMI code如果是的話就只執行MMIcode的指令,否則繼續往下下發撥號動作。

    @Override
    protected Connection dialInternal(String dialString, UUSInfo uusInfo, int videoState,
                                      Bundle intentExtras)
            throws CallStateException {

        // Need to make sure dialString gets parsed properly
        /// M: Ignore stripping for VoLTE SIP uri. @{
        // String newDialString = PhoneNumberUtils.stripSeparators(dialString);
        String newDialString = dialString;
        if (!PhoneNumberUtils.isUriNumber(dialString)) {
            // Need to make sure dialString gets parsed properly
            newDialString = PhoneNumberUtils.stripSeparators(dialString);
        }
        /// @}

        if (isPhoneTypeGsm()) {
            // handle in-call MMI first if applicable
            if (handleInCallMmiCommands(newDialString)) {
                return null;
            }

            // Only look at the Network portion for mmi
            String networkPortion = PhoneNumberUtils.extractNetworkPortionAlt(newDialString);
            GsmMmiCode mmi =
                    GsmMmiCode.newFromDialString(networkPortion, this, mUiccApplication.get());
            if (DBG) logd("dialing w/ mmi '" + mmi + "'...");

            if (mmi == null) {

                /// M: CC: For 3G VT only @{
                //return mCT.dial(newDialString, uusInfo, intentExtras);
                if (videoState == VideoProfile.STATE_AUDIO_ONLY) {
                    return mCT.dial(newDialString, uusInfo, intentExtras);
                } else {
                    if (!is3GVTEnabled()) {
                        throw new CallStateException("cannot vtDial for non-3GVT-capable device");
                    }
                    return mCT.vtDial(newDialString, uusInfo, intentExtras);
                }
                /// @}
            } else if (mmi.isTemporaryModeCLIR()) {
                /// M: CC: For 3G VT only @{
                //return mCT.dial(mmi.mDialingNumber, mmi.getCLIRMode(), uusInfo, intentExtras);
                if (videoState == VideoProfile.STATE_AUDIO_ONLY) {
                    return mCT.dial(mmi.mDialingNumber, mmi.getCLIRMode(), uusInfo, intentExtras);
                } else {
                    if (!is3GVTEnabled()) {
                        throw new CallStateException("cannot vtDial for non-3GVT-capable device");
                    }
                    return mCT.vtDial(mmi.mDialingNumber, mmi.getCLIRMode(), uusInfo, intentExtras);
                }
                /// @}
            } else {
                mPendingMMIs.add(mmi);
                /// M: @{
                Rlog.d(LOG_TAG, "dialInternal: " + dialString + ", mmi=" + mmi);
                dumpPendingMmi();
                /// @}
                mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));
                try {
                    mmi.processCode();
                } catch (CallStateException e) {
                    //do nothing
                }

                // FIXME should this return null or something else?
                return null;
            }
        } else {
            return mCT.dial(newDialString);
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72

GsmCdmaCallTracker

dial

  • 清楚所有已斷開的鏈接,並通知call的狀態改變
  • 如果當前有一個call處於前臺,我們需要將它hold住,並通過一個500毫秒的延時來執行hold這個操作,直到我們收到EVENT_SWITCH_RESULT執行結束的標記,如果不延時在多方會議通話的時候可能出現問題
  • 設置爲mute
    public synchronized Connection dial(String dialString, int clirMode, UUSInfo uusInfo,
                                        Bundle intentExtras)
            throws CallStateException {
        // note that this triggers call state changed notif
        clearDisconnected();

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

        String origNumber = dialString;
        dialString = convertNumberIfNecessary(mPhone, dialString);

        // 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() == GsmCdmaCall.State.ACTIVE) {
            // this will probably be done by the radio anyway
            // but the dial might fail before this happens
            // and we need to make sure the foreground call is clear
            // for the newly dialed connection

            /// M: CC: Proprietary CRSS handling @{
            mWaitingForHoldRequest.set();
            /// @}

            switchWaitingOrHoldingAndActive();
            // This is a hack to delay DIAL so that it is sent out to RIL only after
            // EVENT_SWITCH_RESULT is received. We've seen failures when adding a new call to
            // multi-way conference calls due to DIAL being sent out before SWITCH is processed
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                // do nothing
            }

            // Fake local state so that
            // a) foregroundCall is empty for the newly dialed connection
            // b) hasNonHangupStateChanged remains false in the
            // next poll, so that we don't clear a failed dialing call
            fakeHoldForegroundBeforeDial();
        }

        if (mForegroundCall.getState() != GsmCdmaCall.State.IDLE) {
            //we should have failed in !canDial() above before we get here
            throw new CallStateException("cannot dial in current state");
        }

        mPendingMO = new GsmCdmaConnection(mPhone, checkForTestEmergencyNumber(dialString),
                this, mForegroundCall);
        mHangupPendingMO = false;

        if ( mPendingMO.getAddress() == null || mPendingMO.getAddress().length() == 0
                || mPendingMO.getAddress().indexOf(PhoneNumberUtils.WILD) >= 0) {
            // Phone number is invalid
            mPendingMO.mCause = DisconnectCause.INVALID_NUMBER;

            /// M: CC: Proprietary CRSS handling @{
            mWaitingForHoldRequest.reset();
            /// @}

            // handlePollCalls() will notice this call not present
            // and will mark it as dropped.
            pollCallsWhenSafe();
        } else {
            // Always unmute when initiating a new call
            setMute(false);
            /// M: CC: Proprietary CRSS handling @{
            //mCi.dial(mPendingMO.getAddress(), clirMode, uusInfo, obtainCompleteMessage());
            if (!mWaitingForHoldRequest.isWaiting()) {
                /// M: CC: Proprietary ECC handling@{
                /// M: CC: ECC Retry @{
                if (PhoneNumberUtils.isEmergencyNumber(mPhone.getSubId(), dialString)
                /// @}
                        && !PhoneNumberUtils.isSpecialEmergencyNumber(dialString)) {
                    int serviceCategory = PhoneNumberUtils.getServiceCategoryFromEcc(dialString);
                    mCi.setEccServiceCategory(serviceCategory);
                    mCi.emergencyDial(mPendingMO.getAddress(), clirMode, uusInfo,
                            obtainCompleteMessage(EVENT_DIAL_CALL_RESULT));
                /// @}
                } else {
                    mCi.dial(mPendingMO.getAddress(), clirMode, uusInfo,
                            obtainCompleteMessage(EVENT_DIAL_CALL_RESULT));
                }
            } else {
                mWaitingForHoldRequest.set(mPendingMO.getAddress(), clirMode, uusInfo);
            }
            /// @}

        }

        if (mNumberConverted) {
            mPendingMO.setConverted(origNumber);
            mNumberConverted = false;
        }

        updatePhoneState();
        mPhone.notifyPreciseCallStateChanged();

        return mPendingMO;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101

Log信息

    Line 10357: 12-19 09:17:32.193 I/Telecom (  988): CallIntentProcessor: onReceive - isUnknownCall: false: PCR.oR@AOY 
    Line 10473: 12-19 09:17:32.288 V/Telecom (  988): CallsManager: startOutgoingCall found accounts = [PhoneAccountHandle{TelephonyConnectionService, 89860115881029413909, UserHandle{0}}]: PCR.oR@AOY 
    Line 10553: 12-19 09:17:32.351 V/Telecom (  988): NewOutgoingCallIntentBroadcaster: Processing call intent in OutgoingCallIntentBroadcaster.: PCR.oR@AOY 
    Line 10872: 12-19 09:17:32.406 V/Telecom (  988): NewOutgoingCallIntentBroadcaster: isPotentialEmergencyNumber = false: PCR.oR@AOY 
    Line 10877: 12-19 09:17:32.407 I/Telecom (  988): NewOutgoingCallIntentBroadcaster: Sending NewOutgoingCallBroadcast for [TC@2, CONNECTING, null, tel:10010, A, childs(0), has_parent(false), [Capabilities:], [Properties:]] to UserHandle{0}: PCR.oR@AOY 
    Line 10878: 12-19 09:17:32.408 V/Telecom (  988): NewOutgoingCallIntentBroadcaster: Broadcasting intent: Intent { act=android.intent.action.NEW_OUTGOING_CALL flg=0x10000000 (has extras) }.: PCR.oR@AOY 
    Line 11902: 12-19 09:17:33.374 V/Telecom (  988): NewOutgoingCallBroadcastIntentReceiver: onReceive: Intent { act=android.intent.action.NEW_OUTGOING_CALL flg=0x10000010 (has extras) }: NOCBIR.oR@AOw 
    Line 11903: 12-19 09:17:33.374 I/Telecom (  988): NewOutgoingCallBroadcastIntentReceiver: Received new-outgoing-call-broadcast for [TC@2, CONNECTING, null, tel:10010, A, childs(0), has_parent(false), [Capabilities:], [Properties:]] with data 10010: NOCBIR.oR@AOw 
    Line 11950: 12-19 09:17:33.426 V/Telecom (  988): NewOutgoingCallBroadcastIntentReceiver: Call number unmodified after new outgoing call intent broadcast.: NOCBIR.oR@AOw 
    Line 11951: 12-19 09:17:33.426 D/Telecom (  988): CallsManager: broadcastCallPlacedIntent Entry: NOCBIR.oR@AOw 
    Line 11954: 12-19 09:17:33.427 D/Telecom (  988): CallsManager: Creating a new outgoing call with handle: tel:10010: NOCBIR.oR@AOw 
    Line 12034: 12-19 09:17:33.495 D/Telecom (  988): CallsManager: [TC@2, CONNECTING, null, tel:10010, A, childs(0), has_parent(false), [Capabilities:], [Properties:]] Starting with speakerphone because car is docked.: NOCBIR.oR@AOw 
    Line 12038: 12-19 09:17:33.498 V/Telecom (  988): CreateConnectionProcessor: process: NOCBIR.oR@AOw 
    Line 12044: 12-19 09:17:33.513 I/Telecom (  988): PhoneAccountRegistrar: SimCallManager queried, returning: null: NOCBIR.oR@AOw
    Line 12045: 12-19 09:17:33.513 V/Telecom (  988): CreateConnectionProcessor: setConnectionManager, not changing: NOCBIR.oR@AOw
    Line 12046: 12-19 09:17:33.514 V/Telecom (  988): CreateConnectionProcessor: attemptNextPhoneAccount: NOCBIR.oR@AOw 
    Line 12047: 12-19 09:17:33.515 I/Telecom (  988): CreateConnectionProcessor: Trying attempt CallAttemptRecord(PhoneAccountHandle{TelephonyConnectionService, 89860115881029413909, UserHandle{0}},PhoneAccountHandle{TelephonyConnectionService, 89860115881029413909, UserHandle{0}}): NOCBIR.oR@AOw 
    Line 12050: 12-19 09:17:33.517 D/Telecom (  988): ConnectionServiceWrapper: createConnection([TC@2, CONNECTING, com.android.phone/com.android.services.telephony.TelephonyConnectionService, tel:10010, A, childs(0), has_parent(false), [Capabilities:], [Properties:]]) via ComponentInfo{com.android.phone/com.android.services.telephony.TelephonyConnectionService}.: NOCBIR.oR@AOw 
    Line 12051: 12-19 09:17:33.517 D/Telecom (  988): ConnectionServiceWrapper: bind(): NOCBIR.oR@AOw 
    Line 12073: 12-19 09:17:33.553 D/Telecom (  988): ConnectionServiceWrapper: Telecom -> ConnectionService: addConnectionServiceAdapter com.android.server.telecom.ConnectionServiceWrapper$Adapter@edb67c1: SBC.oSC@AO0 
    Line 12077: 12-19 09:17:33.558 D/TelecomFramework( 1489): : Enqueueing pre-init request TC@2 
    Line 12090: 12-19 09:17:33.585 D/TelecomFramework( 1489): TelephonyConnectionService: createConnection, callManagerAccount: PhoneAccountHandle{TelephonyConnectionService, 89860115881029413909, UserHandle{0}}, callId: TC@2, request: ConnectionRequest tel:10010 Bundle[mParcelledData.dataSize=392], isIncoming: false, isUnknown: false 
    Line 12092: 12-19 09:17:33.587 I/Telephony( 1489): TelephonyConnectionService: onCreateOutgoingConnection, request: ConnectionRequest tel:10010 Bundle[mParcelledData.dataSize=392] 
    Line 12093: 12-19 09:17:33.589 D/Telephony( 1489): TelephonyConnectionService: onCreateOutgoingConnection, ConnectionRequest.PhoneAccountHandle{TelephonyConnectionService, 89860115881029413909, UserHandle{0}} 
                12-19 09:17:33.645 D/Telephony( 1489): TelephonyConnectionService: Service state:0, isAirplaneModeOn:false
    Line 12196: 12-19 09:17:33.764 D/GsmCdmaPhone( 1489): [GsmCdmaPhone] imsUseEnabled=false, useImsForEmergency=false, useImsForUt=false, isUt=false, imsPhone=Handler (com.android.internal.telephony.imsphone.ImsPhone) {cea3f01}, imsPhone.isVolteEnabled()=false, imsPhone.isVowifiEnabled()=false, imsPhone.isVideoEnabled()=false, imsPhone.getServiceState().getState()=1 
    Line 12200: 12-19 09:17:33.765 W/GsmCdmaPhone( 1489): IMS: imsphone = Handler (com.android.internal.telephony.imsphone.ImsPhone) {cea3f01}isEmergencyNumber = false 
    Line 12201: 12-19 09:17:33.765 W/GsmCdmaPhone( 1489): service state = 1 
    Line 12202: 12-19 09:17:33.765 D/GsmCdmaPhone( 1489): [GsmCdmaPhone] Trying (non-IMS) CS call 
    Line 12240: 12-19 09:17:33.796 D/GsmCdmaPhone( 1489): [GsmCdmaPhone] dialing w/ mmi 'null'... 
    Line 12258: 12-19 09:17:33.808 D/RILJ    ( 1489): [3939]> SET_MUTE false [SUB0] 
    Line 12270: 12-19 09:17:33.815 I/AT      ( 1015): AT> AT+CMUT=0 (RIL_CMD_READER_2, tid:515433460816)  
    Line 12273: 12-19 09:17:33.817 D/RILJ    ( 1489): [3940]> DIAL [SUB0] 
    Line 12426: 12-19 09:17:33.874 D/TelecomFramework( 1489): TelephonyConnectionService: createConnection, connection: [TelephonyConnection objId:156910251 telecomCallID:TC@2 type:gsm state:DIALING capabilities:[Capabilities: CAPABILITY_SUPPORT_HOLD CAPABILITY_MUTE CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO CAPABILITY_SEPARATE_FROM_CONFERENCE CAPABILITY_DISCONNECT_FROM_CONFERENCE] properties:[Properties:] address:tel:10010 originalConnection: callId: TC@2 incoming: false state: DIALING post dial state: NOT_STARTED partOfConf:N] 
    Line 12440: 12-19 09:17:33.881 V/TelecomFramework( 1489): TelephonyConnectionService: createConnection, number: 10010, state: DIALING, capabilities: [Capabilities: CAPABILITY_SUPPORT_HOLD CAPABILITY_MUTE CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO CAPABILITY_SEPARATE_FROM_CONFERENCE CAPABILITY_DISCONNECT_FROM_CONFERENCE], properties: [Properties:]
    12-19 09:17:33.881 D/TelecomFramework( 1489): TelephonyConnectionService: createConnection, calling handleCreateConnectionSuccessful TC@2
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

總結

通過上面流程的分析,和部分邏輯代碼的跟蹤,我們可以大致瞭解整個撥號流程所涉及到的目錄和類,大致明白了整個過程中的一些狀態判斷和邏輯處理,後續還會對incallUI界面的構成和流程中某些細分領域的詳細研究。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章