今天学习”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 查看评论