Quantcast
Channel: CSDN博客移动开发推荐文章
Viewing all articles
Browse latest Browse all 5930

android拨号流程

$
0
0

今天学习”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

<activity android:name="OutgoingCallBroadcaster"
                android:enabled="false"
                android:theme="@style/OutgoingCallBroadcasterTheme"
                android:permission="android.permission.CALL_PHONE"
                android:screenOrientation="nosensor"
                android:configChanges="orientation|screenSize|keyboardHidden"
                android:excludeFromRecents="true">
            <!-- CALL action intent filters, for the various ways
                 of initiating an outgoing call. -->
            <intent-filter>
                <action android:name="android.intent.action.CALL" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:scheme="tel" />
            </intent-filter>
            <intent-filter android:icon="@drawable/ic_launcher_sip_call">
                <action android:name="android.intent.action.CALL" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:scheme="sip" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.CALL" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:scheme="voicemail" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.CALL" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:mimeType="vnd.android.cursor.item/phone" />
                <data android:mimeType="vnd.android.cursor.item/phone_v2" />
                <data android:mimeType="vnd.android.cursor.item/person" />
            </intent-filter>
</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());
        }
    ....


}

关于mCi的初始化工作,可以参考上一篇短信的发送流程

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处理
作者:mockingbirds 发表于2016/12/6 22:10:14 原文链接
阅读:22 评论:0 查看评论

Viewing all articles
Browse latest Browse all 5930

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>