我们先看下7.0 MO大致流程:
- package/app/Dialer – DialpadFragment
用户点击拨号盘的拨号按钮,此时开始呼叫长征第一步,dialpadfragment的onclick方法会响应点击事件。
@Override
public void onClick(View view) {
int resId = view.getId();
if (resId == R.id.dialpad_floating_action_button) {
handleDialButtonPressed();
}
}
我们只看关键方法,点击事件调用handleDialButtonPressed方法,号码不为空时,调用DialerUtils的startActivityWithErrorToast方法。当然其方法内部还有一些其他的判断条件,比如号码是空时,会调用handleDialButtonClickWithEmptyDigits方法,显示上次呼叫的号码。
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);
}
- package/app/Dialer – DialerUtils
携带Intent.ACTION_CALL的Intent Action会走到TelecomUtil placeCall流程,否则直接context.startActivity(intent);
public static void startActivityWithErrorToast(Context context, Intent intent, int msgId) {
if ((IntentUtil.CALL_ACTION.equals(intent.getAction())
&& context instanceof Activity)) {
final boolean hasCallPermission = TelecomUtil.placeCall((Activity) context, intent);
}
}
- package/app/Dialer – TelecomUtil
public static boolean placeCall(Activity activity, Intent intent) {
if (hasCallPhonePermission(activity)) {
TelecomManagerCompat.placeCall(activity, getTelecomManager(activity), intent);
return true;
}
return false;
}
hasCallPhonePermission()会检查是否有呼叫权限,包含是否是默认dialer,和是否有Manifest.permission.CALL_PHONE)权限:
public static boolean hasCallPhonePermission(Context context) {
return isDefaultDialer(context)
|| hasPermission(context, Manifest.permission.CALL_PHONE);
}
- package/app/ContactsCommon – TelecomManagerCompat
当版本大于等于6.0时,调用telecomManager.placeCall()方法,否则直接startActivity执行intent。所以我们接着看telecomManager.placeCall的方法:
public static void placeCall(@Nullable Activity activity,
@Nullable TelecomManager telecomManager, @Nullable Intent intent) {
if (CompatUtils.isMarshmallowCompatible()) {
telecomManager.placeCall(intent.getData(), intent.getExtras());
return;
}
activity.startActivityForResult(intent, 0);
}
- frameworks/base/telecomm – TelecomManager
调用ITelecomService的placeCall方法,此处是aidl调用,对应的我们需要找到接收的地方,按照命名规则应该是TelecomServiceImpl:
@RequiresPermission(android.Manifest.permission.CALL_PHONE)
public void placeCall(Uri address, Bundle extras) {
ITelecomService service = getTelecomService();
if (service != null) {
try {
service.placeCall(address, extras == null ? new Bundle() : extras,
mContext.getOpPackageName());
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecomService#placeCall", e);
}
}
}
- packages/services/Telecomm – TelecomServiceImpl
创建UserCallIntentProcessorFactory,调用processIntent方法处理呼叫:
private final ITelecomService.Stub mBinderImpl = new ITelecomService.Stub() {
@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);
mUserCallIntentProcessorFactory.create(mContext, userHandle)
.processIntent(
intent, callingPackage, hasCallAppOp && hasCallPermission);
}
}
}
}
}
- packages/services/Telecomm – UserCallIntentProcessor
processIntent判断是否是呼叫请求:
public void processIntent(Intent intent, String callingPackageName,
boolean canCallNonEmergency) {
if (Intent.ACTION_CALL.equals(action) ||
Intent.ACTION_CALL_PRIVILEGED.equals(action) ||
Intent.ACTION_CALL_EMERGENCY.equals(action)) {
processOutgoingCallIntent(intent, callingPackageName, canCallNonEmergency);
}
}
- Intent.ACTION_CALL: 可以拨打普通呼叫
public static final String ACTION_CALL = "android.intent.action.CALL";
- Intent.ACTION_CALL_PRIVILEGED:可以拨打任意类型号码
public static final String ACTION_CALL_PRIVILEGED = "android.intent.action.CALL_PRIVILEGED";
- Intent.ACTION_CALL_EMERGENCY:可以拨打紧急呼叫
public static final String ACTION_CALL_EMERGENCY = "android.intent.action.CALL_EMERGENCY";
processOutgoingCallIntent方法:
private void processOutgoingCallIntent(Intent intent, String callingPackageName,
boolean canCallNonEmergency) {
......
sendBroadcastToReceiver(intent);
}
sendBroadcastToReceiver方法,发送广播PrimaryCallReceiver:
private boolean sendBroadcastToReceiver(Intent 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");
mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
return true;
}
- packages/services/Telecomm – PrimaryCallReceiver
调用getTelecomSystem方法返回TelecomSystem对象,调用getCallIntentProcessor()返回CallIntentProcessor对象,然后调用processIntent方法
@Override
public void onReceive(Context context, Intent intent) {
Log.startSession("PCR.oR");
synchronized (getTelecomSystem().getLock()) {
getTelecomSystem().getCallIntentProcessor().processIntent(intent);
}
Log.endSession();
}
- packages/services/Telecomm – CallIntentProcessor
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();
}
processOutgoingCallIntent方法,调用CallsManager的startOutgoingCall()方法创建Call,后new NewOutgoingCallIntentBroadcaster,调用processIntent()方法:
static void processOutgoingCallIntent(
Context context,
CallsManager callsManager,
Intent intent) {
// Send to CallsManager to ensure the InCallUI gets kicked off before the broadcast returns
Call call = callsManager
.startOutgoingCall(handle, phoneAccountHandle, clientExtras, initiatingUser);
if (call != null) {
NewOutgoingCallIntentBroadcaster broadcaster = new NewOutgoingCallIntentBroadcaster(
context, callsManager, call, intent, callsManager.getPhoneNumberUtilsAdapter(),
isPrivilegedDialer);
final int result = broadcaster.processIntent();
}
}
看创建Call的备注,貌似和UI有关呢,不过我们先关注呼叫流程,回头再整理UI启动流程,这里加个*标记下,先接着查看processIntent方法:
public int processIntent() {
......
if (callImmediately) {
mCall.setNewOutgoingCallIntentBroadcastIsDone();
mCallsManager.placeOutgoingCall(mCall, Uri.fromParts(scheme, number, null), null,
speakerphoneOn, videoState);
}
......
broadcastIntent(intent, number, !callImmediately, targetUser);
}
private void broadcastIntent(
Intent originalCallIntent,
String number,
boolean receiverRequired,
UserHandle targetUser) {
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
}
callImmediately为true时,直接调用CallsManager的placeOutgoingCall()方法;
callImmediately为false时,创建NewOutgoingCallBroadcastIntentReceiver实例来接收该广播,而后调用到CallsManager的placeOutgoingCall()方法:
public class NewOutgoingCallBroadcastIntentReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
try {
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));
}
}
}
}
- packages/services/Telecomm – CallsManager
public void placeOutgoingCall(Call call, Uri handle, GatewayInfo gatewayInfo,
boolean speakerphoneOn, int videoState) {
if (call.getTargetPhoneAccount() != null || call.isEmergencyCall()) {
call.startCreateConnection(mPhoneAccountRegistrar);
}
}
- packages/services/Telecomm – Call
创建CreateConnectionProcessor,并调用process方法:
void startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar) {
mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this,
phoneAccountRegistrar, mContext);
mCreateConnectionProcessor.process();
}
- packages/services/Telecomm – CreateConnectionProcessor
主要关注attemptNextPhoneAccount方法:
public void process() {
adjustAttemptsForConnectionManager();
adjustAttemptsForEmergency(mCall.getTargetPhoneAccount());
mAttemptRecordIterator = mAttemptRecords.iterator();
attemptNextPhoneAccount();
}
调用ConnectionServiceWrapper mService的createConnection方法:
private void attemptNextPhoneAccount() {
......
mConnectionAttempt++;
mCall.setConnectionManagerPhoneAccount(attempt.connectionManagerPhoneAccount);
mCall.setTargetPhoneAccount(attempt.targetPhoneAccount);
mCall.setConnectionService(mService);
setTimeoutIfNeeded(mService, attempt);
mService.createConnection(mCall, this);
}
}
- packages/services/Telecomm – ConnectionServiceWrapper
通过bind建立成功后,会回调onSuccess方法,调用ConnectionService的createConnection()方法:
public void createConnection(final Call call, final CreateConnectionResponse response) {
Log.d(this, "createConnection(%s) via %s.", call, getComponentName());
BindCallback callback = new BindCallback() {
@Override
public void onSuccess() {
String callId = mCallIdMapper.getCallId(call);
mPendingResponses.put(callId, response);
try {
mServiceInterface.createConnection(
call.getConnectionManagerPhoneAccount(),
callId,
new ConnectionRequest(
call.getTargetPhoneAccount(),
call.getHandle(),
extras,
call.getVideoState(),
callId),
call.shouldAttachToExistingConnection(),
call.isUnknown());
}
}
mBinder.bind(callback, call);
}
- frameworks/base/telecomm – ConnectionService
由于TelephonyConnectionService是ConnectionService的实例,所以我们接着跟踪
TelephonyConnectionService的onCreateOutgoingConnection方法:
case MSG_CREATE_CONNECTION: {
SomeArgs args = (SomeArgs) msg.obj;
try {
else {
createConnection(
connectionManagerPhoneAccount,
id,
request,
isIncoming,
isUnknown);
}
}
break;
}
这里可以看到有来电和去电的逻辑,我们先跟踪呼出的逻辑:
private void createConnection(
final PhoneAccountHandle callManagerAccount,
final String callId,
final ConnectionRequest request,
boolean isIncoming,
boolean isUnknown) {
Connection connection = isUnknown ? onCreateUnknownConnection(callManagerAccount, request)
: isIncoming ? onCreateIncomingConnection(callManagerAccount, request)
: onCreateOutgoingConnection(callManagerAccount, request);
- packages/services/Telecomm – TelephonyConnectionService
@Override
public Connection onCreateOutgoingConnection(
PhoneAccountHandle connectionManagerPhoneAccount,
final ConnectionRequest request) {
.......
placeOutgoingConnection((TelephonyConnection) resultConnection, phone, request);
}
吼吼,是不是看到曙光了呢,有木有看到dial()方法,快接近曙光了有木有:
private void placeOutgoingConnection(
TelephonyConnection connection, Phone phone, int videoState, Bundle extras,
ConnectionRequest request) {
......
originalConnection = phone.dial(number, null, request.getVideoState(), bundle);
}
- frameworks/opt/telephony – GsmCdmaPhone
@Override
public Connection dial(String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras)
throws CallStateException {
if (isPhoneTypeGsm()) {
return dialInternal(dialString, null, VideoProfile.STATE_AUDIO_ONLY, intentExtras);
} else {
return dialInternal(dialString, null, videoState, intentExtras);
}
}
接着就是调用GsmCdmaCallTracker的dial方法:
@Override
protected Connection dialInternal(String dialString, UUSInfo uusInfo, int videoState,
Bundle intentExtras)
throws CallStateException {
return mCT.dial(newDialString);
}
- frameworks/opt/telephony – GsmCdmaCallTracker
mCi就是RIL的实例,也就是说调用的是RIL的dial()方法:
public synchronized Connection dial(String dialString, int clirMode, UUSInfo uusInfo,
Bundle intentExtras)
throws CallStateException {
} else {
// Always unmute when initiating a new call
setMute(false);
mCi.dial(mPendingMO.getAddress(), clirMode, uusInfo, obtainCompleteMessage());
}
updatePhoneState();
mPhone.notifyPreciseCallStateChanged();
return mPendingMO;
}
- frameworks/opt/telephony – RIL
到这就是常见的 > DIAL命令了,有木有:
@Override
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);
}
当然上面都是上层的流程,接着就是RIL层和modem信令的事情喽。