Android7.0 撥號(hào)流程源碼分析

撥號(hào)流程概覽

1. 應(yīng)用進(jìn)程

從點(diǎn)擊撥號(hào)按鈕流程說起
packages/apps/Dialer/src/com/android/dialer/dialpad/DialpadFragment.java

@Override
public void onClick(View view) {
    int resId = view.getId();
    if (resId == R.id.dialpad_floating_action_button) {
        view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
        // 執(zhí)行該方法
        handleDialButtonPressed();
    } else if (resId == R.id.deleteButton) {
        keyPressed(KeyEvent.KEYCODE_DEL);
    } ...
}

handleDialButtonPressed又會(huì)調(diào)用handleDialButtonPressed(Constants.DIAL_NUMBER_INTENT_NORMAL);
packages/apps/Dialer/src/com/android/dialer/dialpad/DialpadFragment.java

private void handleDialButtonPressed(int type) {
    if (isDigitsEmpty()) { // No number entered.
        handleDialButtonClickWithEmptyDigits();
    } else {
        final String number = mDigits.getText().toString();
        // 1. 通過mProhibitedPhoneNumberRegexp正則表達(dá)式判斷號(hào)碼是否被禁止
        if (number != null
                && !TextUtils.isEmpty(mProhibitedPhoneNumberRegexp)
                && number.matches(mProhibitedPhoneNumberRegexp)) {
            Log.i(TAG, "The phone number is prohibited explicitly by a rule.");
            ...
        } else {
            final Intent intent;
            /** M: [IP Dial] check the type of call @{ */
            if (type != Constants.DIAL_NUMBER_INTENT_NORMAL) {
                intent = IntentUtil.getCallIntent(IntentUtil.getCallUri(number),
                        LogState.INITIATION_DIALPAD, type);
            } else {
                // 2. 封裝撥號(hào)Intent
                intent = new CallIntentBuilder(number).
                        setCallInitiationType(LogState.INITIATION_DIALPAD)
                        .build();
            }
            /** @} */
            // 3. 啟動(dòng)對(duì)應(yīng)Intent
            DialerUtils.startActivityWithErrorToast(getActivity(), intent);
            hideAndClearDialpad(false);
        }
    }
}

跟蹤步驟2流程可知該Intent由以下幾個(gè)部分組成

  • Action為:Intent.ACTION_CALL(android.intent.action.CALL)
  • Flag為:FLAG_ACTIVITY_NEW_TASK
  • 號(hào)碼uri為 tel:10010

步驟3中startActivityWithErrorToast方法會(huì)調(diào)用到如下方法
packages/apps/Dialer/src/com/android/dialer/util/DialerUtils.java

public static void startActivityWithErrorToast(Context context, Intent intent, int msgId) {
    try {
        if ((IntentUtil.CALL_ACTION.equals(intent.getAction())
                        && context instanceof Activity)) {
            ...
            // 1. 調(diào)用TelecomUtil的placeCall繼續(xù)處理
            final boolean hasCallPermission = TelecomUtil.placeCall((Activity) context, intent);
            if (!hasCallPermission) {
                Toast.makeText(context, "Cannot place call without Phone permission",
                        Toast.LENGTH_SHORT);
            }
        }
        ...
    } catch (ActivityNotFoundException e) {
        Toast.makeText(context, msgId, Toast.LENGTH_SHORT).show();
    }
}

packages/apps/Dialer/src/com/android/dialer/util/TelecomUtil.java

public static boolean placeCall(Activity activity, Intent intent) {
    // 1.
    if (hasCallPhonePermission(activity)) {
        // 2.
        TelecomManagerCompat.placeCall(activity, getTelecomManager(activity), intent);
        return true;
    }
    return false;
}

hasCallPhonePermission方法返回true的條件

  • 當(dāng)前Dialer為系統(tǒng)默認(rèn)Dialer
  • 檢查Manifest.permission.CALL_PHONE權(quán)限是否已經(jīng)授權(quán)

packages/apps/ContactsCommon/src/com/android/contacts/common/compat/telecom/TelecomManagerCompat.java

public static void placeCall(@Nullable Activity activity,
            @Nullable TelecomManager telecomManager, @Nullable Intent intent) {
    if (activity == null || telecomManager == null || intent == null) {
        return;
    }
    // 當(dāng)前平臺(tái)N > M,返回true
    if (CompatUtils.isMarshmallowCompatible()) {
        telecomManager.placeCall(intent.getData(), intent.getExtras());
        return;
    }
    activity.startActivityForResult(intent, 0);
}

frameworks/base/telecomm/java/android/telecom/TelecomManager.java

public void placeCall(Uri address, Bundle extras) {
    // 1. 通過AIDL得到TelecomService的服務(wù)端的代理
    ITelecomService service = getTelecomService();
    if (service != null) {
        if (address == null) {
            Log.w(TAG, "Cannot place call to empty address.");
        }
        try {
            // 2. TelecomService進(jìn)一步處理call
            service.placeCall(address, extras == null ? new Bundle() : extras,
                    mContext.getOpPackageName());
        } catch (RemoteException e) {
            Log.e(TAG, "Error calling ITelecomService#placeCall", e);
        }
    }
}
  1. 首先通過getTelecomService方法內(nèi)部為ITelecomService.Stub.asInterface(ServiceManager.getService(Context.TELECOM_SERVICE)) 得到TelecomService的服務(wù)端代理浮庐。通過AIDL使用規(guī)則搜索new ITelecomService.Stub()碴开,即可看到服務(wù)端在TelecomServiceImpl中實(shí)現(xiàn)。
  2. 調(diào)用placeCall進(jìn)一步處理院仿,此處address就是tel:10010

2. Telecom進(jìn)程處理

packages/services/Telecomm/src/com/android/server/telecom/TelecomServiceImpl.java

private final ITelecomService.Stub mBinderImpl = new ITelecomService.Stub() {
    /**
     * @see android.telecom.TelecomManager#placeCall
     */
    @Override
    public void placeCall(Uri handle, Bundle extras, String callingPackage) {
        try {
            ...
            synchronized (mLock) {
                final UserHandle userHandle = Binder.getCallingUserHandle();
                long token = Binder.clearCallingIdentity();
                try {
                    final Intent intent = new Intent(Intent.ACTION_CALL, handle);
                    if (extras != null) {
                        extras.setDefusable(true);
                        intent.putExtras(extras);
                    }
                    // 1. create得到的是UserCallIntentProcessor對(duì)象谬以,然后進(jìn)一步處理?yè)艽螂娫挼腎ntent
                    mUserCallIntentProcessorFactory.create(mContext, userHandle)
                            .processIntent(
                                    intent, callingPackage, hasCallAppOp && hasCallPermission);
                } 
                ...
}

通過UserCallIntentProcessor的processIntent方法進(jìn)一步處理?yè)艽螂娫挼腎ntent

packages/services/Telecomm/src/com/android/server/telecom/components/UserCallIntentProcessor.java

/**
 * Processes intents sent to the activity.
 *
 * @param intent The intent.
 */
public void processIntent(Intent intent, String callingPackageName,
        boolean canCallNonEmergency) {
    // 1. 判斷是否支持語(yǔ)音通話声登,通過讀取config_voice_capable值
    if (!isVoiceCapable()) {
        return;
    }
    String action = intent.getAction();

    // 2. 判斷ACTION是否為打電話
    if (Intent.ACTION_CALL.equals(action) ||
            Intent.ACTION_CALL_PRIVILEGED.equals(action) ||
            Intent.ACTION_CALL_EMERGENCY.equals(action)) {
        processOutgoingCallIntent(intent, callingPackageName, canCallNonEmergency);
    }
}
private void processOutgoingCallIntent(Intent intent, String callingPackageName,
            boolean canCallNonEmergency) {
    ...
    // 3. 將是否為默認(rèn)Dialer或是系統(tǒng)的Dialer的boolean值放入Intent的Extra中
    intent.putExtra(CallIntentProcessor.KEY_IS_PRIVILEGED_DIALER,
            isDefaultOrSystemDialer(callingPackageName));

    intent.putExtra(CallIntentProcessor.KEY_INITIATING_USER, mUserHandle);
    sendBroadcastToReceiver(intent);
}
private boolean sendBroadcastToReceiver(Intent intent) {
    // 4. 進(jìn)一步完善Intent
    intent.putExtra(CallIntentProcessor.KEY_IS_INCOMING_CALL, false);
    intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
    intent.setClass(mContext, PrimaryCallReceiver.class);
    Log.d(this, "Sending broadcast as user to CallReceiver");
    // 5. 發(fā)送廣播,只有系統(tǒng)用戶可以接收
    mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
    return true;
}
  1. 判斷config_voice_capable判斷語(yǔ)音通話是否支持哈街,該值在frameworks/base/core/res/res/values/strings.xml中配置
  2. 判斷Action是否為ACTION_CALL,Intent.ACTION_CALL_PRIVILEGED,Intent.ACTION_CALL_EMERGENCY之一,是才繼續(xù)處理
  3. MO 的Intent添加是否為默認(rèn)Dialer或者系統(tǒng)Dialer的信息
    • 默認(rèn)Dialer通過獲取數(shù)據(jù)庫(kù)判斷
    Settings.Secure.getStringForUser(context.getContentResolver(),
                Settings.Secure.DIALER_DEFAULT_APPLICATION, user);
    
    • 系統(tǒng)Dialer通過packages/services/Telecomm/res/values/config.xml中如下屬性獲取
    <string name="ui_default_package" translatable="false">com.android.dialer</string>
    
  4. MO 的Intent添加如下信息
    • 是否來電 由于是去電留瞳,故為false
    • 指定接收廣播的類為PrimaryCallReceiver
  5. 發(fā)送廣播,指定只有System用戶可以接收該廣播

packages/services/Telecomm/src/com/android/server/telecom/components/PrimaryCallReceiver.java

@Override
public void onReceive(Context context, Intent intent) {
    Log.startSession("PCR.oR");
    synchronized (getTelecomSystem().getLock()) {
        if (!ExtensionManager.getCallMgrExt()
                .shouldPreventVideoCallIfLowBattery(context, intent)) {
            getTelecomSystem().getCallIntentProcessor().processIntent(intent);
        }
    }
    Log.endSession();
}

getTelecomSystem().getCallIntentProcessor()返回的類型為CallIntentProcessor,通過processIntent方法將接收到的Intent進(jìn)一步處理

packages/services/Telecomm/src/com/android/server/telecom/CallIntentProcessor.java

public void processIntent(Intent intent) {
    final boolean isUnknownCall = intent.getBooleanExtra(KEY_IS_UNKNOWN_CALL, false);
    Log.i(this, "onReceive - isUnknownCall: %s", isUnknownCall);
    
    Trace.beginSection("processNewCallCallIntent");
    if (isUnknownCall) {
        processUnknownCallIntent(mCallsManager, intent);
    } else {
        processOutgoingCallIntent(mContext, mCallsManager, intent);
    }
    Trace.endSection();
}

前面并沒有設(shè)置KEY_IS_UNKNOWN_CALL屬性,故為false骚秦,走processOutgoingCallIntent

packages/services/Telecomm/src/com/android/server/telecom/CallIntentProcessor.java

/**
 * Processes CALL, CALL_PRIVILEGED, and CALL_EMERGENCY intents.
 *
 * @param intent Call intent containing data about the handle to call.
 */
static void processOutgoingCallIntent(
        Context context,
        CallsManager callsManager,
        Intent intent) {
    ...
    // 1. 創(chuàng)建Call對(duì)象
    Call call = callsManager
            .startOutgoingCall(handle, phoneAccountHandle, clientExtras, initiatingUser);
    if (call != null) {
        NewOutgoingCallIntentBroadcaster broadcaster = new NewOutgoingCallIntentBroadcaster(
                context, callsManager, call, intent, new PhoneNumberUtilsAdapterImpl(),
                isPrivilegedDialer);
        // 2. 
        final int result = broadcaster.processIntent();
        final boolean success = result == DisconnectCause.NOT_DISCONNECTED;

        if (!success && call != null) {
            disconnectCallAndShowErrorDialog(context, call, result);
        }
    }
}
  1. 先看下call對(duì)象是如何創(chuàng)建并拉起IncallUI界面
    packages/services/Telecomm/src/com/android/server/telecom/CallsManager.java
Call startOutgoingCall(Uri handle, PhoneAccountHandle phoneAccountHandle, Bundle extras,
        UserHandle initiatingUser) {
    boolean isReusedCall = true;
    Call call = reuseOutgoingCall(handle);
    // 1. 創(chuàng)建Call對(duì)象    
    if (call == null) {
        call = new Call(getNextCallId(), mContext,
                this,
                mLock,
                mConnectionServiceRepository,
                mContactsAsyncHelper,
                mCallerInfoAsyncQueryFactory,
                handle,
                null /* gatewayInfo */,
                null /* connectionManagerPhoneAccount */,
                null /* phoneAccountHandle */,
                Call.CALL_DIRECTION_OUTGOING /* callDirection */,
                false /* forceAttachToExistingConnection */,
                false /* isConference */
        );
        call.setInitiatingUser(initiatingUser);
        call.initAnalytics();
        isReusedCall = false;
    }
    ...
    // 獲取當(dāng)前激活的卡列表
    List<PhoneAccountHandle> accounts = constructPossiblePhoneAccounts(handle, initiatingUser);
    Log.v(this, "startOutgoingCall found accounts = " + accounts);
    if (phoneAccountHandle == null && accounts.size() > 0 && !call.isEmergencyCall()) {
        if(accounts.size() > 1) {
            // 雙激活卡下取通話主卡賬戶,沒有通話主卡則為空
            PhoneAccountHandle defaultPhoneAccountHandle =
                    mPhoneAccountRegistrar.getOutgoingPhoneAccountForScheme(handle.getScheme(),
                            initiatingUser);
            if (defaultPhoneAccountHandle != null &&
                    accounts.contains(defaultPhoneAccountHandle)) {
                phoneAccountHandle = defaultPhoneAccountHandle;
            }
        } else {
            // 單激活卡直接取該卡賬戶
            phoneAccountHandle = accounts.get(0);
        }
    }
    ...

    call.setTargetPhoneAccount(phoneAccountHandle);

    boolean isPotentialInCallMMICode = isPotentialInCallMMICode(handle);

    if (!isReusedCall && !makeRoomForOutgoingCall(call, call.isEmergencyCall())) {
        // just cancel at this point.
        Log.d(this, "No remaining room for outgoing call: %s", call);
        showToastInfomation(mContext.getResources()
                .getString(R.string.outgoing_call_failed));
        if (mCalls.contains(call)) {
            // This call can already exist if it is a reused call,
            // See {@link #reuseOutgoingCall}.
            call.disconnect();
        }
        return null;
    }

    boolean needsAccountSelection = phoneAccountHandle == null && accounts.size() > 1 &&
            !call.isEmergencyCall();

    Log.i(this, "MO - dump: needSelect = %s; phoneAccountHandle = %s; accounts.size() = %s.",
            needsAccountSelection, phoneAccountHandle, accounts.size());

    
    // 是否需要彈出雙卡選擇框(雙卡下沒有指定賬戶呼出非緊急號(hào)碼且當(dāng)前無通話主卡)
    if (needsAccountSelection) {
        // 設(shè)置當(dāng)前call為等待用戶選擇SIM卡狀態(tài)CallState.SELECT_PHONE_ACCOUNT
        call.setState(CallState.SELECT_PHONE_ACCOUNT, "needs account selection");
        extras = new Bundle(extras);
        extras.putParcelableList(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS, accounts);
    } else {
        // 設(shè)置當(dāng)前call為連接狀態(tài)CallState.CONNECTING
        call.setState(
                CallState.CONNECTING,
                phoneAccountHandle == null ? "no-handle" : phoneAccountHandle.toString());
    }

    setIntentExtrasAndStartTime(call, extras);

    // 如果是MMI Code 不添加call到mCalls列表中
    if ((isPotentialMMICode(handle) || isPotentialInCallMMICode)
            && TelecomUtils.isSupportMMICode(call.getTargetPhoneAccount())
            && !needsAccountSelection) {
        call.addListener(this);
    } else if (!mCalls.contains(call)) {
        // We check if mCalls already contains the call because we could potentially be reusing
        // a call which was previously added (See {@link #reuseOutgoingCall}).
        // 添加當(dāng)前call到mCalls列表中
        addCall(call);
    }
    return call;
}

看下addCall方法

private void addCall(Call call) {
    ...
    call.addListener(this);
    // 1. 將call添加到mCall列表中
    mCalls.add(call);
    ...
    // 2. 遍歷所有mListeners調(diào)用onCallAdded方法
    for (CallsManagerListener listener : mListeners) {
        listener.onCallAdded(call);
    }
    Trace.endSection();
}

mListeners主要來源于CallManager的構(gòu)造方法中添加的Listener

mListeners.add(mInCallWakeLockController);
mListeners.add(statusBarNotifier);
mListeners.add(mCallLogManager);
mListeners.add(mPhoneStateBroadcaster);
mListeners.add(mInCallController);
mListeners.add(mCallAudioManager);
mListeners.add(missedCallNotifier);
mListeners.add(mHeadsetMediaButton);
mListeners.add(mProximitySensorManager);
///M: add listener for phone recording
mListeners.add(PhoneRecorderHandler.getInstance());

這里說下mInCallController,其實(shí)InCallController對(duì)象她倘,內(nèi)部封裝了與incallui服務(wù)的相關(guān)操作,實(shí)際上就是一個(gè)遠(yuǎn)程服務(wù)代理類作箍,當(dāng)callsmanager添加一路call時(shí)硬梁, 回調(diào)InCallController的onCallAdded方法

@Override
public void onCallAdded(Call call) {
    if (!isBoundToServices()) {
        bindToServices(call);
    } else {
        adjustServiceBindingsForEmergency();
        addCall(call);

        for (Map.Entry<ComponentName, IInCallService> entry : mInCallServices.entrySet()) {
            ComponentName componentName = entry.getKey();
            IInCallService inCallService = entry.getValue();
            ParcelableCall parcelableCall = ParcelableCallUtils.toParcelableCall(call,
                    true /* includeVideoProvider */, mCallsManager.getPhoneAccountRegistrar());
            try {
                // 1.
                inCallService.addCall(parcelableCall);
            } catch (RemoteException ignored) {
            }
        }
    }
}

調(diào)用inCallService的addCall方法告訴incallui當(dāng)前添加了一路通話,incallui收到后會(huì)拉起界面胞得,具體過程在此就不詳述了荧止。

  1. 接著剛才得到call對(duì)象后的下一步分析,通過NewOutgoingCallIntentBroadcaster對(duì)象的的processIntent()進(jìn)一步做處理

packages/services/Telecomm/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java

public int processIntent() {
    Intent intent = mIntent;
    ...
    boolean isVoicemailNumber = PhoneAccount.SCHEME_VOICEMAIL.equals(handle.getScheme());
    // 1. 如果語(yǔ)音信箱的號(hào)碼調(diào)用CallsManager的placeOutgoingCall方法直接撥號(hào)
    if (isVoicemailNumber) {
        if (Intent.ACTION_CALL.equals(action)
                || Intent.ACTION_CALL_PRIVILEGED.equals(action)) {
            // 標(biāo)識(shí)call已準(zhǔn)備就緒
            mCall.setNewOutgoingCallIntentBroadcastIsDone();
            ...
            mCallsManager.placeOutgoingCall(mCall, handle, null, speakerphoneOn,
                    VideoProfile.STATE_AUDIO_ONLY);

            return DisconnectCause.NOT_DISCONNECTED;
        } else {
            Log.d(this, "Unhandled intent %s. Ignoring and not placing call.", intent);
            return DisconnectCause.OUTGOING_CANCELED;
        }
    }

    ...
    // 判斷是否為潛在的緊急撥號(hào)
    final boolean isPotentialEmergencyNumber = isPotentialEmergencyNumber(number);
    Log.v(this, "isPotentialEmergencyNumber = %s", isPotentialEmergencyNumber);
    // 2. 根據(jù)是否是緊急撥號(hào)重新修正Action
    rewriteCallIntentAction(intent, isPotentialEmergencyNumber);
    action = intent.getAction();
    boolean callImmediately = false;

    if (Intent.ACTION_CALL.equals(action)) {
        if (isPotentialEmergencyNumber) {
            // 如果是系統(tǒng)Dialer或者默認(rèn)dialer撥緊急號(hào)碼則callImmediately會(huì)置為true,否則會(huì)啟動(dòng)系統(tǒng)dialer由用戶進(jìn)一步?jīng)Q定如何操作
            if (!mIsDefaultOrSystemPhoneApp) {
                launchSystemDialer(intent.getData());
                return DisconnectCause.OUTGOING_CANCELED;
            } else {
                callImmediately = true;
            }
        }
    } else if (Intent.ACTION_CALL_EMERGENCY.equals(action)) {
        if (!isPotentialEmergencyNumber) {
            return DisconnectCause.OUTGOING_CANCELED;
        }
        callImmediately = true;
    } else {
        return DisconnectCause.INVALID_NUMBER;
    }
    // 3. 如果是緊急撥號(hào)跃巡,callImmediately會(huì)賦值為true危号,直接調(diào)用 mCallsManager.placeOutgoingCall進(jìn)行撥號(hào)
    if (callImmediately) {
        ...
        mCallsManager.placeOutgoingCall(mCall, Uri.fromParts(scheme, number, null), null,
                speakerphoneOn, videoState);
    }

    UserHandle targetUser = mCall.getInitiatingUser();
    // 4. 調(diào)用該方法發(fā)送廣播
    broadcastIntent(intent, number, !callImmediately, targetUser);
    return DisconnectCause.NOT_DISCONNECTED;
}
  1. VoiceMail直接調(diào)用mCallsManager.placeOutgoingCall,并return
  2. rewriteCallIntentAction內(nèi)部會(huì)判斷如果Action是Intent.ACTION_CALL_PRIVILEGED素邪,然后還是緊急撥號(hào)外莲,則轉(zhuǎn)化Action為Intent.ACTION_CALL_EMERGENCY,不是緊急撥號(hào)則使用Intent.ACTION_CALL
  3. 緊急撥號(hào),callImmediately置為true兔朦,直接調(diào)用 mCallsManager.placeOutgoingCall方法撥打電話

packages/services/Telecomm/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java

private void broadcastIntent(
        Intent originalCallIntent,
        String number,
        boolean receiverRequired,
        UserHandle targetUser) {
    Intent broadcastIntent = new Intent(Intent.ACTION_NEW_OUTGOING_CALL);
    if (number != null) {
        broadcastIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, number);
    }
    broadcastIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
    checkAndCopyProviderExtras(originalCallIntent, broadcastIntent);
    mContext.sendOrderedBroadcastAsUser(
            broadcastIntent,
            targetUser,
            android.Manifest.permission.PROCESS_OUTGOING_CALLS,
            AppOpsManager.OP_PROCESS_OUTGOING_CALLS,
            receiverRequired ? new NewOutgoingCallBroadcastIntentReceiver() : null,
            null,  // scheduler
            Activity.RESULT_OK,  // initialCode
            number,  // initialData: initial value for the result data (number to be modified)
            null);  // initialExtras
}

發(fā)送了一個(gè)Action為Intent.ACTION_NEW_OUTGOING_CALL的有序廣播偷线,如果是非緊急撥號(hào),則指定最終廣播接收者(也就是最后一個(gè)接收該廣播)為NewOutgoingCallBroadcastIntentReceiver沽甥,緊急撥號(hào)則為空声邦。

NewOutgoingCallIntentBroadcaster$NewOutgoingCallBroadcastIntentReceiver.java

**
 * Processes the result of the outgoing call broadcast intent, and performs callbacks to
 * the OutgoingCallIntentBroadcasterListener as necessary.
 */
public class NewOutgoingCallBroadcastIntentReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        try {
            // 1. 得到最終撥號(hào)的號(hào)碼
            String resultNumber = getResultData();
            ...

            Uri resultHandleUri = Uri.fromParts(
                    mPhoneNumberUtilsAdapter.isUriNumber(resultNumber) ?
                            PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL,
                    resultNumber, null);

            Uri originalUri = mIntent.getData();
            // 2. 打印號(hào)碼是否被被其它廣播接收者更改過的log
            if (originalUri.getSchemeSpecificPart().equals(resultNumber)) {
            } else {
            }

            GatewayInfo gatewayInfo = getGateWayInfoFromIntent(intent, resultHandleUri);
            // 標(biāo)識(shí)call準(zhǔn)備就緒
            mCall.setNewOutgoingCallIntentBroadcastIsDone();
            // 3. 調(diào)用該方法
            mCallsManager.placeOutgoingCall(mCall, resultHandleUri, gatewayInfo,
                    mIntent.getBooleanExtra(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE,
                            false),
                    mIntent.getIntExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
                            VideoProfile.STATE_AUDIO_ONLY));

    ...
}
  1. 得到最終的號(hào)碼
  2. 判斷號(hào)碼是否被中間的廣播接收者更改過
  3. 調(diào)用最重要的mCallsManager.placeOutgoingCall方法進(jìn)行撥號(hào),無論voicemail摆舟,emergencyNumber,正常撥號(hào)都會(huì)調(diào)用該方法進(jìn)行最終的撥號(hào)亥曹。

packages/services/Telecomm/src/com/android/server/telecom/CallsManager.java

public void placeOutgoingCall(Call call, Uri handle, GatewayInfo gatewayInfo,
        boolean speakerphoneOn, int videoState) {

    ...
    // 1. 指定了激活的SIM卡或者是緊急撥號(hào),就調(diào)用call.startCreateConnection方法
    if (call.getTargetPhoneAccount() != null || call.isEmergencyCall()) {
        call.startCreateConnection(mPhoneAccountRegistrar);
    }
    ...
}

packages/services/Telecomm/src/com/android/server/telecom/Call.java

void startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar) {
    mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this,
            phoneAccountRegistrar, mContext);
    mCreateConnectionProcessor.process();
}

創(chuàng)建CreateConnectionProcessor盏檐,并調(diào)用process方法

packages/services/Telecomm/src/com/android/server/telecom/CreateConnectionProcessor.java

public void process() {
    clearTimeout();
    mAttemptRecords = new ArrayList<>();
    if (mCall.getTargetPhoneAccount() != null) {
        mAttemptRecords.add(new CallAttemptRecord(
                mCall.getTargetPhoneAccount(), mCall.getTargetPhoneAccount()));
    }
    adjustAttemptsForConnectionManager();
    adjustAttemptsForEmergency();
    mAttemptRecordIterator = mAttemptRecords.iterator();
    // 執(zhí)行該方法
    attemptNextPhoneAccount();
}

private void attemptNextPhoneAccount() {
    ...
    PhoneAccountHandle phoneAccount = attempt.connectionManagerPhoneAccount;
    // 1. 得到ConnectionServiceWrapper對(duì)象
    mService = mRepository.getService(phoneAccount.getComponentName(),
            phoneAccount.getUserHandle());
    mCall.setConnectionManagerPhoneAccount(attempt.connectionManagerPhoneAccount);
    mCall.setTargetPhoneAccount(attempt.targetPhoneAccount);
    mCall.setConnectionService(mService);
    setTimeoutIfNeeded(mService, attempt);
    // 2. 建立連接
    mService.createConnection(mCall, this);
    ...
}

packages/services/Telecomm/src/com/android/server/telecom/ConnectionServiceWrapper.java

public void createConnection(final Call call, final CreateConnectionResponse response) {
    BindCallback callback = new BindCallback() {
        @Override
        public void onSuccess() {
            ...
            // 2.
            mServiceInterface.createConnection(
                    call.getConnectionManagerPhoneAccount(),
                    callId,
                    new ConnectionRequest(
                            call.getTargetPhoneAccount(),
                            call.getHandle(),
                            extras,
                            call.getVideoState(),
                            callId),
                    call.shouldAttachToExistingConnection(),
                    call.isUnknown());
            ...
        }
        ...
    };
    // 1. 內(nèi)部會(huì)跨進(jìn)程綁定遠(yuǎn)程Service ConnectionService
    mBinder.bind(callback, call);
}
  1. 綁定遠(yuǎn)程Service ConnectionService,當(dāng)綁定成功后歇式,會(huì)調(diào)用onServiceConnected方法,該方法中會(huì)回調(diào)callback的onSuccess方法并將返回的binder對(duì)象通過IConnectionService.Stub.asInterface(binder);得到遠(yuǎn)程Service的代理胡野,賦值給mServiceInterface
  2. IPC調(diào)用遠(yuǎn)程Service ConnectionService的createConnection方法,對(duì)應(yīng)類為TelephonyConnectionService材失。

3. Telephony進(jìn)程

TelephonyConnectionService的createConnection方法在父類中實(shí)現(xiàn)
frameworks/base/telecomm/java/android/telecom/ConnectionService.java

@Override
public void createConnection(
        PhoneAccountHandle connectionManagerPhoneAccount,
        String id,
        ConnectionRequest request,
        boolean isIncoming,
        boolean isUnknown) {
    SomeArgs args = SomeArgs.obtain();
    args.arg1 = connectionManagerPhoneAccount;
    args.arg2 = id;
    args.arg3 = request;
    args.argi1 = isIncoming ? 1 : 0;
    args.argi2 = isUnknown ? 1 : 0;
    // 解析參數(shù),并發(fā)送MSG_CREATE_CONNECTION消息交由Handler處理
    mHandler.obtainMessage(MSG_CREATE_CONNECTION, args).sendToTarget();
}

private final Handler mHandler = new Handler(Looper.getMainLooper()) {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            ...
            case MSG_CREATE_CONNECTION: {
                SomeArgs args = (SomeArgs) msg.obj;
                try {
                    final PhoneAccountHandle connectionManagerPhoneAccount =
                            (PhoneAccountHandle) args.arg1;
                    final String id = (String) args.arg2;
                    final ConnectionRequest request = (ConnectionRequest) args.arg3;
                    final boolean isIncoming = args.argi1 == 1;
                    final boolean isUnknown = args.argi2 == 1;
                    ...
                    // 走該方法
                    createConnection(
                            connectionManagerPhoneAccount,
                            id,
                            request,
                            isIncoming,
                            isUnknown);
            ...
}   
private void createConnection(
        final PhoneAccountHandle callManagerAccount,
        final String callId,
        final ConnectionRequest request,
        boolean isIncoming,
        boolean isUnknown) {
    ...
    // 1. 來電走onCreateIncomingConnection,去電走onCreateOutgoingConnection
    Connection connection = isUnknown ? onCreateUnknownConnection(callManagerAccount, request)
            : isIncoming ? onCreateIncomingConnection(callManagerAccount, request)
            : onCreateOutgoingConnection(callManagerAccount, request);
    ...
    connection.setTelecomCallId(callId);
    if (connection.getState() != Connection.STATE_DISCONNECTED) {
        addConnection(callId, connection);
    }
    ...    

去電通過onCreateOutgoingConnection創(chuàng)建通話連接,實(shí)現(xiàn)在實(shí)體類TelephonyConnectionService實(shí)現(xiàn)硫豆。

packages/services/Telephony/src/com/android/services/telephony/TelephonyConnectionService.java

@Override
public Connection onCreateOutgoingConnection(
        PhoneAccountHandle connectionManagerPhoneAccount,
        final ConnectionRequest request) {
    if (PhoneAccount.SCHEME_VOICEMAIL.equals(scheme)) {
        ...
        number = phone.getVoiceMailNumber();
        // 1. Voicemail撥號(hào)龙巨,但Voicemail號(hào)碼為空錯(cuò)誤
        if (TextUtils.isEmpty(number)) {
            return Connection.createFailedConnection(
                    DisconnectCauseUtil.toTelecomDisconnectCause(
                            android.telephony.DisconnectCause.VOICEMAIL_NUMBER_MISSING,
                            "Voicemail scheme provided but no voicemail number set."));
        }
        // Convert voicemail: to tel:
    }
    // 飛行模式撥號(hào)錯(cuò)誤
    // 還有其他一些出錯(cuò)情況的判斷,出錯(cuò)就會(huì)界面給出對(duì)應(yīng)提示
    ...
    // 3. 繼續(xù)撥號(hào)這段邏輯
    placeOutgoingConnection(connection, phone, request);

packages/services/Telephony/src/com/android/services/telephony/TelephonyConnectionService.java

private void placeOutgoingConnection(
            TelephonyConnection connection, Phone phone, ConnectionRequest request) {
    String number = connection.getAddress().getSchemeSpecificPart();
    ...
    com.android.internal.telephony.Connection originalConnection;
    try {
        originalConnection =
                phone.dial(number, null, request.getVideoState(), request.getExtras());
    }
    ...
}

調(diào)用phone對(duì)象的dial()方法實(shí)現(xiàn)真正撥號(hào).在Android N上熊响,cdma和gsm都由GsmCdmaPhone對(duì)象統(tǒng)一處理旨别。

frameworks/opt/telephony/src/java/com/android/internal/telephony/GsmCdmaPhone.java

@Override
public Connection dial(String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras)
        throws CallStateException {
    ...
    // VOLTE開啟或者VOWIFI開啟都會(huì)為true
    boolean imsUseEnabled = isImsUseEnabled()
             && imsPhone != null
             && (imsPhone.isVolteEnabled() || imsPhone.isWifiCallingEnabled() ||
             (imsPhone.isVideoEnabled() && VideoProfile.isVideo(videoState)))
             && (imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE);

    ...
    if ((imsUseEnabled && (!isUt || useImsForUt)) || useImsForEmergency) {
        try {
            if (videoState == VideoProfile.STATE_AUDIO_ONLY) {
                // 調(diào)用imsPhone撥打電話
                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);
                }...
            }
        } ...
    }
    ...
    // 正常撥號(hào)走此處
    return dialInternal(dialString, null, videoState, intentExtras);
}

frameworks/opt/telephony/src/java/com/android/internal/telephony/GsmCdmaPhone.java

@Override
protected Connection dialInternal(String dialString, UUSInfo uusInfo, int videoState,
                                  Bundle intentExtras)
        throws CallStateException {
    if (isPhoneTypeGsm()) {
        if (handleInCallMmiCommands(newDialString)) {
            return null;
        }
        
        String networkPortion = PhoneNumberUtils.extractNetworkPortionAlt(newDialString);
        GsmMmiCode mmi =
                GsmMmiCode.newFromDialString(networkPortion, this, mUiccApplication.get());
        if (mmi == null) {
            // 1. Gsm撥號(hào)
            return mCT.dial(newDialString, uusInfo, intentExtras);
        } else if (mmi.isTemporaryModeCLIR()) {
            return mCT.dial(mmi.mDialingNumber, mmi.getCLIRMode(), uusInfo, intentExtras);
        } else {
            mPendingMMIs.add(mmi);
            mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));
            try {
                mmi.processCode();
            } catch (CallStateException e) {
                //do nothing
            }
            return null;
        }
    } else {
        // 2. 非Gsm撥號(hào)
        return mCT.dial(newDialString);
    }
}

實(shí)際都是通過mCT的dial進(jìn)行呼出,mCT實(shí)際為GsmCdmaCallTracker對(duì)象,這里以CDMA進(jìn)行分析汗茄,也就是走注釋2處秸弛。

frameworks/opt/telephony/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java

private Connection dial(String dialString, int clirMode) throws CallStateException {
    // 1. 將Call設(shè)置為IDLE狀態(tài),并通知狀態(tài)發(fā)生改變
    clearDisconnected();
    ...
    mPendingMO = new GsmCdmaConnection(mPhone, checkForTestEmergencyNumber(dialString),
                this, mForegroundCall);
    if(!isPhoneInEcmMode || (isPhoneInEcmMode && isEmergencyCall)) {
        /// M: Proprietary ECC handling @{
        //mCi.dial(mPendingMO.getAddress(), clirMode, obtainCompleteMessage());
        if (isEmergencyCall) {
            mCi.emergencyDial(mPendingMO.getAddress(), clirMode, null,
                    obtainCompleteMessage());
        } else {
            // 2. 通過RILJ撥打電話
            mCi.dial(mPendingMO.getAddress(), clirMode, obtainCompleteMessage());
        }
        /// @}
    }
    ...
    // 3. 在此更新Phone狀態(tài)集精確的Call狀態(tài)
    updatePhoneState();
    mPhone.notifyPreciseCallStateChanged();
    return mPendingMO;
}
  1. 設(shè)置IDLE方法如下
public GsmCdmaCall mRingingCall = new GsmCdmaCall(this);
public GsmCdmaCall mForegroundCall = new GsmCdmaCall(this);
public GsmCdmaCall mBackgroundCall = new GsmCdmaCall(this);
public void clearDisconnected() {
    // 1. clearDisconnected內(nèi)部會(huì)將Call狀態(tài)設(shè)置為IDLE
    mRingingCall.clearDisconnected();
    mForegroundCall.clearDisconnected();
    mBackgroundCall.clearDisconnected();
    // 2. 內(nèi)部會(huì)調(diào)用mPhone的notifyPhoneStateChanged更新Phone的狀態(tài)
    updatePhoneState();
    // 3. 更新精確Phone狀態(tài)
    mPhone.notifyPreciseCallStateChanged();
}

Call的所有狀態(tài)為枚舉類型

public enum State {
    IDLE, ACTIVE, HOLDING, DIALING, ALERTING, INCOMING, WAITING, DISCONNECTED, DISCONNECTING;
}
  1. mCi是CommandsInterface類型,跟蹤賦值可以看到其實(shí)通過Phone對(duì)象獲取的洪碳,而Phone對(duì)象是由PhoneFactory創(chuàng)建的递览,代碼如下

frameworks/opt/telephony/src/java/com/android/internal/telephony/PhoneFactory.java

public static void makeDefaultPhone(Context context) {
    ...
    int numPhones = TelephonyManager.getDefault().getPhoneCount();
    int[] networkModes = new int[numPhones];
    sPhones = new Phone[numPhones];
    sCommandsInterfaces = new RIL[numPhones];
    sTelephonyNetworkFactories = new TelephonyNetworkFactory[numPhones];

    for (int i = 0; i < numPhones; i++) {
        networkModes[i] = RILConstants.PREFERRED_NETWORK_MODE;
        // 1. 創(chuàng)建RIL
        sCommandsInterfaces[i] = new RIL(context, networkModes[i],
                cdmaSubscription, i);
    }
    SubscriptionController.init(context, sCommandsInterfaces);
    RadioManager.init(context, numPhones, sCommandsInterfaces);

    sUiccController = UiccController.make(context, sCommandsInterfaces);

    for (int i = 0; i < numPhones; i++) {
        Phone phone = null;
        // 2. 創(chuàng)建Phone對(duì)象,實(shí)際對(duì)應(yīng)GsmCdmaPhone,并傳入對(duì)應(yīng)的RIL對(duì)象,sCommandsInterfaces[i]就對(duì)應(yīng)Phone中的mCi變量
        int phoneType = TelephonyManager.getPhoneType(networkModes[i]);
        if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
            phone = new GsmCdmaPhone(context,
                    sCommandsInterfaces[i], sPhoneNotifier, i,
                    PhoneConstants.PHONE_TYPE_GSM,
                    TelephonyComponentFactory.getInstance());
        } else if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
            phone = new GsmCdmaPhone(context,
                    sCommandsInterfaces[i], sPhoneNotifier, i,
                    PhoneConstants.PHONE_TYPE_CDMA_LTE,
                    TelephonyComponentFactory.getInstance());
        }
        sPhones[i] = phone;
    }

    sPhone = sPhones[0];
    sCommandsInterface = sCommandsInterfaces[0];
}

知道了mCi是RIL瞳腌,由于是java文件也稱作RILJ绞铃。我們繼續(xù)分析mCi.dial撥打電話的方法。

4. RILJ與RILC的交互

frameworks/opt/telephony/src/java/com/android/internal/telephony/RIL.java

@Override
public void dial(String address, int clirMode, UUSInfo uusInfo, Message result) {
    if (!PhoneNumberUtils.isUriNumber(address)) {
        // 1. 封裝為RILRequest
       RILRequest rr = RILRequest.obtain(RIL_REQUEST_DIAL, result);
        
        rr.mParcel.writeString(address);
        rr.mParcel.writeInt(clirMode);
        ...
        // 2. 調(diào)用send
        send(rr);
    } else {
        RILRequest rr = RILRequest.obtain(RIL_REQUEST_DIAL_WITH_SIP_URI, result);
        
        rr.mParcel.writeString(address);
        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
        
        mEventLog.writeRilDial(rr.mSerial, clirMode, uusInfo);
        send(rr);
    }
}
private void send(RILRequest rr) {
    Message msg;

    if (mSocket == null) {
        rr.onError(RADIO_NOT_AVAILABLE, null);
        rr.release();
        return;
    }
    msg = mSender.obtainMessage(EVENT_SEND, rr);
    acquireWakeLock(rr, FOR_WAKELOCK);
    // 3. 交由mSender子線程的Handler處理
    msg.sendToTarget();
}

將撥號(hào)信息及撥號(hào)請(qǐng)求RIL_REQUEST_DIAL封裝在RILRequest中嫂侍,在注釋3處交由與RILC的服務(wù)端進(jìn)行通信的子線程mSender中

class RILSender extends Handler implements Runnable {
    public RILSender(Looper looper) {
        super(looper);
    }
    byte[] dataLength = new byte[4];

    @Override public void
    handleMessage(Message msg) {
        RILRequest rr = (RILRequest)(msg.obj);
        RILRequest req = null;

        switch (msg.what) {
            case EVENT_SEND:
            case EVENT_SEND_ACK:
                try {
                    LocalSocket s;
                    // 1. 得到客戶端LocalSocket對(duì)象
                    s = mSocket;
                    ...
                    byte[] data;
                    data = rr.mParcel.marshall();
                    ...

                    // parcel length in big endian
                    dataLength[0] = dataLength[1] = 0;
                    dataLength[2] = (byte)((data.length >> 8) & 0xff);
                    dataLength[3] = (byte)((data.length) & 0xff);

                    // 2. 向服務(wù)端發(fā)送數(shù)據(jù)長(zhǎng)度
                    s.getOutputStream().write(dataLength);
                    // 3. 向服務(wù)端發(fā)送數(shù)據(jù)
                    s.getOutputStream().write(data);
                    if (msg.what == EVENT_SEND_ACK) {
                        rr.release();
                        return;
                    }
                } catch (IOException ex) {
                  ...

當(dāng)服務(wù)端給出回應(yīng)后后會(huì)在RILReceiver中接收到,代碼如下

frameworks/opt/telephony/src/java/com/android/internal/telephony/RIL.java

class RILReceiver implements Runnable {
    byte[] buffer;

    RILReceiver() {
        buffer = new byte[RIL_MAX_COMMAND_BYTES];
    }

    @Override
    public void
    run() {
        int retryCount = 0;
        String rilSocket = "rild";

        try {for (;;) {
            LocalSocket s = null;
            LocalSocketAddress l;
            ...
            try {
                s = new LocalSocket();
                
                l = new LocalSocketAddress(rilSocket,
                        LocalSocketAddress.Namespace.RESERVED);
                // 1. 連接name為rild的服務(wù)端Socket,也就是RILC部分
                s.connect(l);
            } catch (IOException ex){
                ...
            }
            mSocket = s;
            int length = 0;
            try {
                InputStream is = mSocket.getInputStream();
                for (;;) {
                    Parcel p;
                    // 2. 阻塞讀取服務(wù)端發(fā)來的消息儿捧,一有消息就會(huì)繼續(xù)執(zhí)行接下來的代碼荚坞,如果length為-1,就會(huì)推出循環(huán)             
                    length = readRilMessage(is, buffer);

                    if (length < 0) {
                        break;
                    }

                    p = Parcel.obtain();
                    p.unmarshall(buffer, 0, length);
                    p.setDataPosition(0);

                    // 3. 接收到上報(bào)的消息后就會(huì)通過該方法處理
                    processResponse(p);
                    p.recycle();
                }
            } catch (java.io.IOException ex) {
                ...
            }

            //4. 退出循環(huán)菲盾,也就切斷了和Modem的聯(lián)系颓影,需要設(shè)置Radio狀態(tài)為RADIO_UNAVAILABLE
            setRadioState (RadioState.RADIO_UNAVAILABLE);

            try {
                mSocket.close();
            } catch (IOException ex) {
            }

            mSocket = null;
            ...
    }
}

主要看下上報(bào)消息的處理

private void processResponse (Parcel p) {
    int type;

    type = p.readInt();

    if (type == RESPONSE_UNSOLICITED || type == RESPONSE_UNSOLICITED_ACK_EXP) {
        // 處理URC消息,也就是modem主動(dòng)上報(bào)消息的處理
        processUnsolicited (p, type);
    } else if (type == RESPONSE_SOLICITED || type == RESPONSE_SOLICITED_ACK_EXP) {
        // 處理非URC消息,modem回復(fù)上層下發(fā)的請(qǐng)求亿汞,例如撥號(hào)
        RILRequest rr = processSolicited (p, type);
        if (rr != null) {
            if (type == RESPONSE_SOLICITED) {
                decrementWakeLock(rr);
            }
            rr.release();
            return;
        }
    } else if (type == RESPONSE_SOLICITED_ACK) {
        ...
    }
}

發(fā)起撥號(hào)請(qǐng)求瞭空,modem回應(yīng)的消息應(yīng)在processSolicited中處理,繼續(xù)看該方法疗我。

private RILRequest processSolicited (Parcel p, int type) {
    int serial, error;
    boolean found = false;

    serial = p.readInt();
    error = p.readInt();

    RILRequest rr;
    // 1. 根據(jù)serial找到對(duì)應(yīng)的RILRequest
    rr = findAndRemoveRequestFromList(serial);
    ...
     try {switch (rr.mRequest) {
        case RIL_REQUEST_GET_SIM_STATUS: ret =  responseIccCardStatus(p); break;
        case RIL_REQUEST_ENTER_SIM_PIN: ret =  responseInts(p); break;
        case RIL_REQUEST_ENTER_SIM_PUK: ret =  responseInts(p); break;
        case RIL_REQUEST_ENTER_SIM_PIN2: ret =  responseInts(p); break;
        case RIL_REQUEST_ENTER_SIM_PUK2: ret =  responseInts(p); break;
        case RIL_REQUEST_CHANGE_SIM_PIN: ret =  responseInts(p); break;
        case RIL_REQUEST_CHANGE_SIM_PIN2: ret =  responseInts(p); break;
        case RIL_REQUEST_ENTER_NETWORK_DEPERSONALIZATION: ret =  responseInts(p); break;
        case RIL_REQUEST_GET_CURRENT_CALLS: ret =  responseCallList(p); break;
        // 2. 撥號(hào)時(shí)建立的RILRequest的mRequest為RIL_REQUEST_DIAL,responseVoid返回的null,表示只關(guān)心Modem是否回應(yīng),無需其他內(nèi)容
        case RIL_REQUEST_DIAL: ret =  responseVoid(p); break;
        ...
     }
     ...
     if (error == 0) {
        if (rr.mResult != null) {
            AsyncResult.forMessage(rr.mResult, ret, null);
            // 3. 發(fā)送到對(duì)應(yīng)的Handler處理
            rr.mResult.sendToTarget();
        }
    }
    return rr;
}

注釋3處南捂,撥號(hào)后接收到Modem的回應(yīng)吴裤,封裝消息后又交由對(duì)應(yīng)Handler處理。mResults是什么溺健?mResult是調(diào)用RIL的dial方法傳入的Message類型的result麦牺,該方法調(diào)用是在
GsmCdmaCallTracker中,具體如下
frameworks/opt/telephony/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java

public class GsmCdmaCallTracker extends CallTracker {
    private Connection dial(String dialString, int clirMode) throws CallStateException {
        ...
        // 1. 調(diào)用obtainCompleteMessage返回一個(gè)Message
        mCi.dial(mPendingMO.getAddress(), clirMode, obtainCompleteMessage());
        ...
    }
    public Message obtainCompleteMessage() {
        return obtainCompleteMessage(EVENT_OPERATION_COMPLETE);
    }
    public Message obtainCompleteMessage(int what) {
        mPendingOperations++;
        mLastRelevantPoll = null;
        mNeedsPoll = true;
        // 2. 返回一個(gè)what為EVENT_OPERATION_COMPLETE的Message
        return obtainMessage(what);
    }
    @Override
    public void handleMessage(Message msg) {
        AsyncResult ar;

        switch (msg.what) {
            // 3. 處理what為EVENT_OPERATION_COMPLETE消息
            case EVENT_OPERATION_COMPLETE:
                operationComplete();
            break;
    ...
    
}

GsmCdmaCallTracker間接繼承Handler鞭缭,注釋1處調(diào)用RIL的dial時(shí)傳入了一個(gè)what為EVENT_OPERATION_COMPLETE的Message剖膳。當(dāng)在RIL中調(diào)用rr.mResult.sendToTarget()時(shí),就會(huì)在注釋3處接收到岭辣,然后調(diào)用operationComplete()方法

private void operationComplete() {
    mPendingOperations--;
    if (mPendingOperations == 0 && mNeedsPoll) {
        // 封裝了EVENT_POLL_CALLS_RESULT消息吱晒,調(diào)用RIL的getCurrentCalls獲取Call狀態(tài)
        mLastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT);
        mCi.getCurrentCalls(mLastRelevantPoll);
    } else if (mPendingOperations < 0) {
        // this should never happen
        Rlog.e(LOG_TAG,"GsmCdmaCallTracker.pendingOperations < 0");
        mPendingOperations = 0;
    }
}

接下來的流程又進(jìn)入與RIL層的socket通信,過程與前面一樣沦童,最后GsmCdmaCallTracker收到EVENT_POLL_CALLS_RESULT消息進(jìn)行處理仑濒,調(diào)用了handlePollCalls((AsyncResult)msg.obj)方法

@Override
protected synchronized void handlePollCalls(AsyncResult ar) {
    ...
    // 1. 更新Phone狀態(tài)
    updatePhoneState();
    if (unknownConnectionAppeared) {
        if (isPhoneTypeGsm()) {
            for (Connection c : newUnknownConnectionsGsm) {
                mPhone.notifyUnknownConnection(c);
            }
        } else {
            mPhone.notifyUnknownConnection(newUnknownConnectionCdma);
        }
    }

    if (hasNonHangupStateChanged || newRinging != null || hasAnyCallDisconnected) {
        // 2. 更新Call精確的狀態(tài)
        mPhone.notifyPreciseCallStateChanged();
    }
}

注冊(cè)了該phone電話狀態(tài)監(jiān)聽的對(duì)象將會(huì)收到通知

Call的所有狀態(tài)定義在Call中

public enum State {
    IDLE, ACTIVE, HOLDING, DIALING, ALERTING, INCOMING, WAITING, DISCONNECTED, DISCONNECTING;
    public boolean isAlive() {
        return !(this == IDLE || this == DISCONNECTED || this == DISCONNECTING);
    }
    public boolean isRinging() {
        return this == INCOMING || this == WAITING;
    }
    public boolean isDialing() {
        return this == DIALING || this == ALERTING;
    }
}

Call狀態(tài)與PreciseCallState封裝的精確Call狀態(tài)對(duì)應(yīng)關(guān)系如下

public static int convertPreciseCallState(Call.State state) {
    switch (state) {
        case ACTIVE:
            return PreciseCallState.PRECISE_CALL_STATE_ACTIVE;
        case HOLDING:
            return PreciseCallState.PRECISE_CALL_STATE_HOLDING;
        case DIALING:
            return PreciseCallState.PRECISE_CALL_STATE_DIALING;
        case ALERTING:
            return PreciseCallState.PRECISE_CALL_STATE_ALERTING;
        case INCOMING:
            return PreciseCallState.PRECISE_CALL_STATE_INCOMING;
        case WAITING:
            return PreciseCallState.PRECISE_CALL_STATE_WAITING;
        case DISCONNECTED:
            return PreciseCallState.PRECISE_CALL_STATE_DISCONNECTED;
        case DISCONNECTING:
            return PreciseCallState.PRECISE_CALL_STATE_DISCONNECTING;
        default:
            return PreciseCallState.PRECISE_CALL_STATE_IDLE;
    }
}

具體notifyPreciseCallStateChanged發(fā)送過程自行閱讀源碼。

后續(xù)modem會(huì)主動(dòng)上報(bào)一些電話狀態(tài)的變化:RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED偷遗,對(duì)于URC消息在processUnsolicited 中處理

private void processUnsolicited (Parcel p, int type) {
    int response;
    Object ret;

    response = p.readInt();
    ...
    switch(response) {
        ...
        case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED:
            // 1. 利用Resistrant機(jī)制通知對(duì)應(yīng)的所有注冊(cè)者
            mCallStateRegistrants
                .notifyRegistrants(new AsyncResult(null, null, null));
        break;
        ...
}

先說下RegistrantList機(jī)制墩瞳,如果需要接收Modem上報(bào)的消息,就需要注冊(cè)到對(duì)應(yīng)的RegistrantList中氏豌,待Modem有對(duì)應(yīng)消息時(shí)喉酌,就會(huì)通知對(duì)應(yīng)的RegistrantList列表”么看下RIL中的不同類型的RegistrantList列表

// Radio狀態(tài)列表
protected RegistrantList mRadioStateChangedRegistrants = new RegistrantList();
protected RegistrantList mOnRegistrants = new RegistrantList();
protected RegistrantList mAvailRegistrants = new RegistrantList();
protected RegistrantList mOffOrNotAvailRegistrants = new RegistrantList();
protected RegistrantList mNotAvailRegistrants = new RegistrantList();
// Call狀態(tài)注冊(cè)著列表
protected RegistrantList mCallStateRegistrants = new RegistrantList();
// Network狀態(tài)注冊(cè)者列表
protected RegistrantList mNetworkStateRegistrants = new RegistrantList();

// 注冊(cè)接口方法
@Override
public void registerForCallStateChanged(Handler h, int what, Object obj) {
    Registrant r = new Registrant (h, what, obj);

    mCallStateRegistrants.add(r);
}
...

而監(jiān)聽Modem上傳Call狀態(tài)的是在GsmCdmaCallTracker構(gòu)造方法中注冊(cè)的

mCi.registerForCallStateChanged(this, EVENT_CALL_STATE_CHANGE, null);

當(dāng)Modem上報(bào)Call狀態(tài)時(shí)通過mCallStateRegistrants .notifyRegistrants(new AsyncResult(null, null, null))方法泪电,內(nèi)部會(huì)調(diào)用注冊(cè)傳入的Handler(也就是GsmCdmaCallTracker)的sendMessage發(fā)送消息,消息類型是EVENT_CALL_STATE_CHANGE就調(diào)用pollCallsWhenSafe()方法涣旨。

protected void pollCallsWhenSafe() {
    mNeedsPoll = true;
    if (checkNoOperationsPending()) {
        mLastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT);
        mCi.getCurrentCalls(mLastRelevantPoll);
    }
}

與RIL Socket通信得到Call狀態(tài)歪架,和前面獲取Call狀態(tài)過程相同。

至此霹陡,撥號(hào)流程就完了和蚪,通過實(shí)際問題打印log會(huì)有更深刻的認(rèn)識(shí)止状。下面對(duì)總結(jié)下大體流程。

參考

http://blog.csdn.net/michael_yt/article/details/53748915

https://www.cnblogs.com/lance2016/p/6002371.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末攒霹,一起剝皮案震驚了整個(gè)濱河市怯疤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌催束,老刑警劉巖集峦,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異抠刺,居然都是意外死亡塔淤,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門速妖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來高蜂,“玉大人,你說我怎么就攤上這事罕容”感簦” “怎么了?”我有些...
    開封第一講書人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵锦秒,是天一觀的道長(zhǎng)露泊。 經(jīng)常有香客問我,道長(zhǎng)旅择,這世上最難降的妖魔是什么惭笑? 我笑而不...
    開封第一講書人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮砌左,結(jié)果婚禮上脖咐,老公的妹妹穿的比我還像新娘。我一直安慰自己汇歹,他們只是感情好屁擅,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著产弹,像睡著了一般派歌。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上痰哨,一...
    開封第一講書人閱讀 51,679評(píng)論 1 305
  • 那天胶果,我揣著相機(jī)與錄音,去河邊找鬼斤斧。 笑死早抠,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的撬讽。 我是一名探鬼主播蕊连,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼悬垃,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了甘苍?” 一聲冷哼從身側(cè)響起尝蠕,我...
    開封第一講書人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎载庭,沒想到半個(gè)月后看彼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡囚聚,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年靖榕,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片靡挥。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡序矩,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出跋破,到底是詐尸還是另有隱情,我是刑警寧澤瓶蝴,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布毒返,位于F島的核電站,受9級(jí)特大地震影響舷手,放射性物質(zhì)發(fā)生泄漏拧簸。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一男窟、第九天 我趴在偏房一處隱蔽的房頂上張望盆赤。 院中可真熱鬧,春花似錦歉眷、人聲如沸牺六。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)淑际。三九已至,卻和暖如春扇住,著一層夾襖步出監(jiān)牢的瞬間春缕,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來泰國(guó)打工艘蹋, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留锄贼,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓女阀,卻偏偏與公主長(zhǎng)得像宅荤,于是被迫代替她去往敵國(guó)和親屑迂。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容