Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android撥號流程

Android撥號流程

編輯:關於Android編程

今天學習”android中的撥號流程”,大部分情況,用戶是通過dialer輸入號碼,撥號通話的,那麼就從dialer開始吧。

DialpadFragment

DialpadFragment是撥打電話界面,當點擊撥打電話按鈕會回調其onClick方法:

public void onClick(View view) {
        switch (view.getId()) {
            case R.id.dialpad_floating_action_button:
                view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
                handleDialButtonPressed();
                break;
            ....
        }
}

handleDialButtonPress方法中,主要代碼如下:

private void handleDialButtonPressed() {
    ....
    final Intent intent = CallUtil.getCallIntent(number);
    if (!isDigitsShown) {
       // must be dial conference add extra
       intent.putExtra(EXTRA_DIAL_CONFERENCE_URI, true);
    }
    intent.putExtra(ADD_PARTICIPANT_KEY, mAddParticipant && isPhoneInUse());
    DialerUtils.startActivityWithErrorToast(getActivity(), intent);
    hideAndClearDialpad(false);
    ....

}

CallUtil#getCallIntent

可以看到這裡構造了一個intent,並且啟動了該intent對應的activity

public static Intent getCallIntent(String number) {
    return getCallIntent(getCallUri(number));
}

public static Intent getCallIntent(Uri uri) {
    return new Intent(Intent.ACTION_CALL, uri);
}

Intent.ACTION_CALL這樣的action對應的是/packages/services/Telephony模塊中的OutgoingCallBroadcaster類,該類是一個activity

OutgoingCallBroadcaster

此時程序進入了OutgoingCallBroadcaster類

@Override
protected void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        setContentView(R.layout.outgoing_call_broadcaster);
        ....
        // 調用processIntent處理傳遞過來的intent
        processIntent(intent);
    ....
}

processIntent方法主要處理下面三種action

CALL (action for usual outgoing voice calls)CALL_PRIVILEGED (can come from built-in apps like contacts / voice dialer / bluetooth)CALL_EMERGENCY (from the EmergencyDialer that’s reachable from the lockscreen.)對於數據為tel: URI的電話處理流程為:OutgoingCallReceiver -> SipCallOptionHandler ->InCallScreen.對於數據為sip: URI的網絡電話,則跳過NEW_OUTGOING_CALL廣播,直接調用SipCallOptionHandler ->InCallScreen對於數據為voicemail: URIs的語音信箱處理同電話處理流程類似

OutgoingCallBroadcaster#processIntent

private void processIntent(Intent intent) {
        final Configuration configuration = getResources().getConfiguration();

        // 如果當前設備不具有語音通信能力,則直接返回
        if (!PhoneGlobals.sVoiceCapable) {
            handleNonVoiceCapable(intent);
            return;
        }

        String action = intent.getAction();
        String number = PhoneNumberUtils.getNumberFromIntent(intent, this);
        // Check the number, don't convert for sip uri
        if (number != null) {
            if (!PhoneNumberUtils.isUriNumber(number)) {
                number = PhoneNumberUtils.convertKeypadLettersToDigits(number);
                number = PhoneNumberUtils.stripSeparators(number);
            }
        } else {
            Log.w(TAG, "The number obtained from Intent is null.");
        }

        // 下面代碼獲取調用Intent.ACTION_CALL所在包,檢查當前包是否具有撥打電話的權限
        AppOpsManager appOps = (AppOpsManager)getSystemService(Context.APP_OPS_SERVICE);
        int launchedFromUid;
        String launchedFromPackage;
        try {
            // 獲取啟動"ACTION_CALL"的uid和package
            launchedFromUid = ActivityManagerNative.getDefault().getLaunchedFromUid(
                    getActivityToken());
            launchedFromPackage = ActivityManagerNative.getDefault().getLaunchedFromPackage(
                    getActivityToken());
        } catch (RemoteException e) {
            launchedFromUid = -1;
            launchedFromPackage = null;
        }
        // 若當前UID和所在的package不具有"OP_CALL_PHONE"權限,則直接返回
        if (appOps.noteOpNoThrow(AppOpsManager.OP_CALL_PHONE, launchedFromUid, launchedFromPackage)
                != AppOpsManager.MODE_ALLOWED) {
            Log.w(TAG, "Rejecting call from uid " + launchedFromUid + " package "
                    + launchedFromPackage);
            finish();
            return;
        }

        // 如果callNow是true,表示當前是一個類似於緊急撥號的特殊通話,此時直接開啟通話,就不會走NEW_OUTGOING_CALL
        boolean callNow;

        // 對於緊急號碼和非緊急號碼設置不同的action
        final boolean isExactEmergencyNumber =
                (number != null) && PhoneNumberUtils.isLocalEmergencyNumber(this, number);
        final boolean isPotentialEmergencyNumber =
                (number != null) && PhoneNumberUtils.isPotentialLocalEmergencyNumber(this, number);

        if (Intent.ACTION_CALL_PRIVILEGED.equals(action)) {
            if (isPotentialEmergencyNumber) {
                action = Intent.ACTION_CALL_EMERGENCY;
            } else {
                action = Intent.ACTION_CALL;
            }
            intent.setAction(action);
        }
         // 當用戶輸入的號碼為空的時候,intent.getBooleanExtra(EXTRA_SEND_EMPTY_FLASH, false) == true
         if (intent.getBooleanExtra(EXTRA_SEND_EMPTY_FLASH, false)) {
                PhoneUtils.sendEmptyFlash(PhoneGlobals.getPhone());
                finish();
                return;
            } else {
                callNow = true;
            }

        ....


        if (callNow) {
            // 如果是緊急號碼或者輸入的號碼合法,則直接跳轉到InCallScreen界面
            PhoneGlobals.getInstance().callController.placeCall(intent);
        }

        // 構造一個"ACTION_NEW_OUTGOING_CALL" intent
        Intent broadcastIntent = new Intent(Intent.ACTION_NEW_OUTGOING_CALL);
        if (number != null) {
            broadcastIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, number);
        }
        CallGatewayManager.checkAndCopyPhoneProviderExtras(intent, broadcastIntent);
        broadcastIntent.putExtra(EXTRA_ALREADY_CALLED, callNow);
        broadcastIntent.putExtra(EXTRA_ORIGINAL_URI, uri.toString());
        broadcastIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);

        // 發送一個打電話超時的message
        mHandler.sendEmptyMessageDelayed(EVENT_OUTGOING_CALL_TIMEOUT,
                OUTGOING_CALL_TIMEOUT_THRESHOLD);
        // 主要會發送根據構造出的intent,發送一個有序廣播,並且在OutgoingCallReceiver中處理
        sendOrderedBroadcastAsUser(broadcastIntent, UserHandle.OWNER,
                android.Manifest.permission.PROCESS_OUTGOING_CALLS,
                AppOpsManager.OP_PROCESS_OUTGOING_CALLS,
                new OutgoingCallReceiver(),
                null,  // scheduler
                Activity.RESULT_OK,  // initialCode
                number,  // initialData: initial value for the result data
                null);  // initialExtras
}

CallController#placeCall

public void placeCall(Intent intent) {
        ....
        if (!(Intent.ACTION_CALL.equals(action)
              || Intent.ACTION_CALL_EMERGENCY.equals(action)
              || Intent.ACTION_CALL_PRIVILEGED.equals(action))) {
            Log.wtf(TAG, "placeCall: unexpected intent action " + action);
            throw new IllegalArgumentException("Unexpected action: " + action);
        }

        // Check to see if this is an OTASP call (the "activation" call
        // used to provision CDMA devices), and if so, do some
        // OTASP-specific setup.
        Phone phone = mApp.mCM.getDefaultPhone();
        if (TelephonyCapabilities.supportsOtasp(phone)) {
            checkForOtaspCall(intent);
        }

        mApp.setRestoreMuteOnInCallResume(false);

        CallStatusCode status = placeCallInternal(intent);

        switch (status) {
            // Call was placed successfully:
            case SUCCESS:
            case EXITED_ECM:
                if (DBG) log("==> placeCall(): success from placeCallInternal(): " + status);
                break;

            default:
                log("==> placeCall(): failure code from placeCallInternal(): " + status);
                handleOutgoingCallError(status);
                break;
        }

        // 最終無論如何都會顯示InCallScreen,並且根據當前錯誤碼狀態顯示指定的錯誤提示信息
    }

CallController#placeCallInternal

private CallStatusCode placeCallInternal(Intent intent) {
    ....
    int callStatus = PhoneUtils.placeCall(mApp,
                                              phone,
                                              number,
                                              contactUri,
                                              (isEmergencyNumber || isEmergencyIntent),
                                              rawGatewayInfo,
                                              mCallGatewayManager);
    ....
}

PhoneUtils#placeCall

public static int placeCall(Context context, Phone phone, String number, Uri contactRef,
            boolean isEmergencyCall, RawGatewayInfo gatewayInfo, CallGatewayManager callGateway) {
     ....
     int status = CALL_STATUS_DIALED;
     try {
            // 和RIL建立連接,mCM是PhoneGlobals的屬性同時是CallManager類型
            connection = app.mCM.dial(phone, numberToDial, VideoProfile.STATE_AUDIO_ONLY);
        } catch (CallStateException ex) {
            return CALL_STATUS_FAILED;
        }

      if (null == connection) {
            status = CALL_STATUS_FAILED;
      }  

      //
      startGetCallerInfo(context, connection, null, null, gatewayInfo);

      // 設置音頻相關
      setAudioMode();

            final boolean speakerActivated = activateSpeakerIfDocked(phone);

            final BluetoothManager btManager = app.getBluetoothManager();

      if (initiallyIdle && !speakerActivated && isSpeakerOn(app)
                    && !btManager.isBluetoothHeadsetAudioOn()) {
                PhoneUtils.turnOnSpeaker(app, false, true);
      }
     ....

     return status; 
}

CallManager#dial

public Connection dial(Phone phone, String dialString, int videoState)
            throws CallStateException {
        Phone basePhone = getPhoneBase(phone);
        int subId = phone.getSubId();
        Connection result;
        // 檢查當前手機狀態是否可以撥打電話
        if (!canDial(phone)) {
            String newDialString = PhoneNumberUtils.stripSeparators(dialString);
            if (basePhone.handleInCallMmiCommands(newDialString)) {
                return null;
            } else {
                throw new CallStateException("cannot dial in current state");
            }
        }
        result = basePhone.dial(dialString, videoState);
        return result;
}

basePhone是一個phone對象,大部分情況是Phone的一個代理類PhoneProxy,然後根據PhoneProxy的mActivePhone判斷具體是那個Phone的子類的實現,有可能是下面類型:

com/android/internal/telephony/gsm/GSMPhone.java
com.android.internal.telephony.cdma.CDMAPhone
com.android.internal.telephony.sip.SipPhone
....
private static Phone getPhoneBase(Phone phone) {
        if (phone instanceof PhoneProxy) {
            return phone.getForegroundCall().getPhone();
        }
        return phone;
}

以GMSPhone#dial為例

GMSPhone#dial

@Override
public Connection
    dial(String dialString, int videoState) throws CallStateException {
        return dial(dialString, null, videoState, null);
    }

    @Override
    public Connection
    dial (String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras)
            throws CallStateException {
        ....
        return dialInternal(dialString, null, VideoProfile.STATE_AUDIO_ONLY, intentExtras);
}

GMSPhone#dialInternal

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

        // Need to make sure dialString gets parsed properly
        String newDialString = PhoneNumberUtils.stripSeparators(dialString);

        // 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());
        // mCT是GsmCallTracker類對象,這裡調用GsmCallTracker#dial
        if (mmi == null) {
            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));
            mmi.processCode();
            return null;
        }
    }

GsmCallTracker#dial

synchronized Connection
    dial (String dialString, int clirMode, UUSInfo uusInfo, Bundle intentExtras)
            throws CallStateException {
     ....
    if ( mPendingMO.getAddress() == null || mPendingMO.getAddress().length() == 0
                || mPendingMO.getAddress().indexOf(PhoneNumberUtils.WILD) >= 0
        ) {
           // 無效的號碼
            pollCallsWhenSafe();
        } else {
            // Always unmute when initiating a new call
            setMute(false);
            // mCi是CommandsInterface接口,其具體的實現類是com.android.internal.telephony.RIL
            mCi.dial(mPendingMO.getAddress(), clirMode, uusInfo, obtainCompleteMessage());
        }
    ....


}

RIL#dial

    public void
    dial(String address, int clirMode, UUSInfo uusInfo, Message result) {
        RILRequest rr = RILRequest.obtain(RIL_REQUEST_DIAL, result);

        rr.mParcel.writeString(address);
        rr.mParcel.writeInt(clirMode);

        if (uusInfo == null) {
            rr.mParcel.writeInt(0); // UUS information is absent
        } else {
            rr.mParcel.writeInt(1); // UUS information is present
            rr.mParcel.writeInt(uusInfo.getType());
            rr.mParcel.writeInt(uusInfo.getDcs());
            rr.mParcel.writeByteArray(uusInfo.getUserData());
        }

        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));

        send(rr);
    }

可以看到,最終也是通過send方法發送”EVENT_SEND”消息,給到自己處理,厲害了,天啦魯,居然和短信的發送走到同一條路上,然後就是交給modem去具體操作了。

流程總結

1. com.android.dialer.dialpad.DialpadFragment#onClick
2. com.android.dialer.dialpad.DialpadFragment#handleDialButtonPressed
3. com.android.phone.OutgoingCallBroadcaster#processIntent
4. com.android.phone.CallController#placeCall
5. com.android.phone.CallController#placeCallInternal
6. com.android.phone.PhoneUtils#placeCall(android.content.Context, com.android.internal.telephony.Phone, java.lang.String, android.net.Uri, boolean, com.android.phone.CallGatewayManager.RawGatewayInfo, com.android.phone.CallGatewayManager)
7. com.android.internal.telephony.CallManager#dial(com.android.internal.telephony.Phone, java.lang.String, int)
8. com.android.internal.telephony.PhoneProxy#dial(java.lang.String, int)
9. 以GSMPhone為例
   com.android.internal.telephony.gsm.GSMPhone#dialInternal
10.com.android.internal.telephony.RIL#dial(java.lang.String, int, com.android.internal.telephony.UUSInfo, android.os.Message)
11.com.android.internal.telephony.RIL#send
發送msg = mSender.obtainMessage(EVENT_SEND, rr);這樣的message給到RILSender處理

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved