我們先看下7.0 MO大致流程:
- package/app/Dialer -- DialpadFragment
用戶點擊撥號盤的撥號按鈕猜年,此時開始呼叫長征第一步奄喂,dialpadfragment的onclick方法會響應(yīng)點擊事件。
@Override
public void onClick(View view) {
int resId = view.getId();
if (resId == R.id.dialpad_floating_action_button) {
handleDialButtonPressed();
}
}
我們只看關(guān)鍵方法,點擊事件調(diào)用handleDialButtonPressed方法,號碼不為空時氓润,調(diào)用DialerUtils的startActivityWithErrorToast方法。當(dāng)然其方法內(nèi)部還有一些其他的判斷條件憋沿,比如號碼是空時旺芽,會調(diào)用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()會檢查是否有呼叫權(quán)限,包含是否是默認(rèn)dialer壶辜,和是否有Manifest.permission.CALL_PHONE)權(quán)限:
public static boolean hasCallPhonePermission(Context context) {
return isDefaultDialer(context)
|| hasPermission(context, Manifest.permission.CALL_PHONE);
}
- package/app/ContactsCommon -- TelecomManagerCompat
當(dāng)版本大于等于6.0時悯舟,調(diào)用telecomManager.placeCall()方法,否則直接startActivity執(zhí)行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
調(diào)用ITelecomService的placeCall方法抵怎,此處是aidl調(diào)用,對應(yīng)的我們需要找到接收的地方岭参,按照命名規(guī)則應(yīng)該是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
創(chuàng)建UserCallIntentProcessorFactory反惕,調(diào)用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方法,發(fā)送廣播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
調(diào)用getTelecomSystem方法返回TelecomSystem對象演侯,調(diào)用getCallIntentProcessor()返回CallIntentProcessor對象姿染,然后調(diào)用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方法,調(diào)用CallsManager的startOutgoingCall()方法創(chuàng)建Call秒际,后new NewOutgoingCallIntentBroadcaster悬赏,調(diào)用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();
}
}
看創(chuàng)建Call的備注狡汉,貌似和UI有關(guān)呢,不過我們先關(guān)注呼叫流程闽颇,回頭再整理UI啟動流程盾戴,這里加個*標(biāo)記下,先接著查看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時兵多,直接調(diào)用CallsManager的placeOutgoingCall()方法尖啡;
callImmediately為false時,創(chuàng)建NewOutgoingCallBroadcastIntentReceiver實例來接收該廣播中鼠,而后調(diào)用到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
創(chuàng)建CreateConnectionProcessor可婶,并調(diào)用process方法:
void startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar) {
mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this,
phoneAccountRegistrar, mContext);
mCreateConnectionProcessor.process();
}
- packages/services/Telecomm -- CreateConnectionProcessor
主要關(guān)注attemptNextPhoneAccount方法:
public void process() {
adjustAttemptsForConnectionManager();
adjustAttemptsForEmergency(mCall.getTargetPhoneAccount());
mAttemptRecordIterator = mAttemptRecords.iterator();
attemptNextPhoneAccount();
}
調(diào)用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建立成功后沿癞,會回調(diào)onSuccess方法援雇,調(diào)用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);
}
}
接著就是調(diào)用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的實例筐赔,也就是說調(diào)用的是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);
}
當(dāng)然上面都是上層的流程揖铜,接著就是RIL層和modem信令的事情嘍茴丰。