今天講解一下Dialer呼出 直達底層
目錄結構
- packages/app/Dialer
撥打電話的入口,來電不會經過Dialer。但是撥打電話的出口不光是Dialer,在聯繫人和短信裏也有撥打電話的出口。代碼運行在dialer進程。 - packages/app/InCallUI
負責顯示通話界面的信息,來電信息。dialer進程。 - packages/services/Telecomm
處理Intent,發送廣播,設置call的狀態,audio狀態。system_process和telecomm:ui進程。 - packages/services/Telephony
向下層傳遞撥號,註冊了很多廣播,申請很多權限(service data sms wap network)。 phone進程 - framework/base/telecom
提供placeCall的接口(自android M開始),創建outgoingCall的connection,通知上層成功建立connection - framewrok/opt/telephony
撥號 也就是dial命令的下發,但是如果是Ims網絡就會有下面一步 - Vendor/ims
創建ImsConnection,ImsCall,撥號。phone進程。
流程圖:
主要的類以及作用
Dialer
- DialpadFragment.java 撥號鍵盤
- DialerUtils.java 撥號工具
- TelecomUtil.java 通訊工具
- InCallServiceImpl.java InCallUi開啓服務,他繼承的是InCallService。
service/telecomm
- TelecomService.java 系統初始化的時候就會進行綁定,並不 是在打電話的時候才綁TelecomSystem會被初始 化,期間構 建 一 些列必須源, 包括Phone和CI(和底層溝通的對象) 的構建。
- TelecomServiceImpl.java 提供Binder接口。
- UserCallIntentProcessor.java 用戶撥號處理,處理 內部校驗一些撥號的權限,以及其它作 權限看看是不是需要彈框 絕。 比如《只允許打緊急電話沒有電話許可,此應用程序不能發 出呼叫
- PrimaryCallReceiver.java 廣 播,作爲撥號的單一入口, 其到消息後,就轉發TelecomSystem的。
- CallIntentProcessor.java 判斷Intent裏面的數據,比如 號碼是否爲空等等....。
- CallsManager.java 官方聲明 :CallManager對象爲 Phone應用程序提供通用 Call API的方法。 SipPhone類 實現 Phone接口,但大部分由虛擬方法填充,實際現使用的是 SipManager,訪問 SIP通 信框架並控 制 SIP Call. 他的主要作用是 呼出入口以及控制InCallController
- InCallController.java 控制電話App的邏輯和UI 。
- Call.java 撥打電話或者來電入口。
- CreateConnectionProcessor.java 創建連接
- ConnectionServiceWrapper.java 主要作用是綁定該Adapter與framework/base/Telecomm中的ConnectionService 之間 創建一個監聽,再有創建了一個連接
- ServiceBinder.java serviceBinder是ConnectionSerivceWrapper中的父類
service/telephony
- TelephonyConnectionService.java 他繼承自ConnectionService這個 GSM和CDMA連接的服務。
- TelephneyConnection.java CDMA和GSM連接的基類。
- GsmConnection.java 管理GSM處理的單個電話呼叫。
- CmdaConnection.java 管理CDMA處理的單個電話呼叫。
framework/base/telecom 這個包中的全部可以給第三方調用
- TelecomManager.java 向外部提供接口,有很多地方會調用這個接口進行撥號。
- InCallService.java 提供創建連接創建連接 ,也是播出入口,在InCallUI中InCallServiceImpl實現了該服務, InCallService是UI和telecom的接口。InCallController綁定InCallService。
代碼流程:
第一步進入呼出Dialer
DialerUtils.java 的作用提供一些工具方法給Dialer進行處理
public static void startActivityWithErrorToast(
final Context context, final Intent intent, int msgId) {
try {
if ((Intent.ACTION_CALL.equals(intent.getAction()))) {
......
if (shouldWarnForOutgoingWps(context, intent.getData().getSchemeSpecificPart())) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setMessage(R.string.outgoing_wps_warning);//彈出對話框 撥打 WPS 電話會中斷現有通話。
builder.setPositiveButton(
R.string.dialog_continue,
new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
placeCallOrMakeToast(context, intent);
}
});
builder.setNegativeButton(android.R.string.cancel, null);
builder.create().show();
} else {
placeCallOrMakeToast(context, intent);//核心
}
} else {
context.startActivity(intent);
}
} catch (ActivityNotFoundException e) {
Toast.makeText(context, msgId, Toast.LENGTH_SHORT).show();
}
}
private static void placeCallOrMakeToast(Context context, Intent intent) {
final boolean hasCallPermission = TelecomUtil.placeCall(context, intent);//核心入口
}
TelecomUtil.java 提供通話工具
public static boolean placeCall(Context context, Intent intent) {
if (hasCallPhonePermission(context)) {
// 核心,主過程 這個方法主要是調用TelecomManager.placeCall();//TelecomManager.java屬於Framework/Telephony代碼
getTelecomManager(context).placeCall(intent.getData(), intent.getExtras());
return true;
}
return false;
}
第二部進入phone進程 framework/base/telecomm
base/telecomm主要的作用是提供接口給外部進行調用,比如短信,聯繫人 等等.....
TelecomManager.java
public void placeCall(Uri address, Bundle extras) {
ITelecomService service = getTelecomService(); //獲取到服務對象
if (service != null) {
if (address == null) {
Log.w(TAG, "Cannot place call to empty address.");
}
try {
service.placeCall(address, extras == null ? new Bundle() : extras,
mContext.getOpPackageName());
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecomService#placeCall", e);
}
}
}
第三步進入 system進程,也就是packager/sevice/Telecomm
進入System進程TelecomService是屬於系統框架向外提供的服務,隨着系統啓動而啓動,並由TelecomManager向外提供訪問接。在TelecomSystem第一次綁定時(系統初始化的時候會進行綁定,並不是在打電話的時候才綁定),TelecomSystem會被初始化期間構建一些列必須資源,包括Phone和CI(和底層溝通的對象)的構建
TelecomService.java
@Override
public IBinder onBind(Intent intent) {
Log.d(this, "onBind");
initializeTelecomSystem(this);//系統初始化的時候就會進行綁定,並不是在打電話的時候才綁定),TelecomSystem 會被初始化,期間構建一些列必須資源,包括Phone和CI(和底層溝通的對象)的構建
synchronized (getTelecomSystem().getLock()) {
return getTelecomSystem().getTelecomServiceImpl().getBinder();
}
}
static void initializeTelecomSystem(Context context) {
if (TelecomSystem.getInstance() == null) {
.....
// TelecomSystem被初始化 構造都是在打電話前執行的
TelecomSystem.setInstance(
new TelecomSystem(
context,
new MissedCallNotifierImpl.MissedCallNotifierImplFactory() {
@Override
public MissedCallNotifierImpl makeMissedCallNotifierImpl(
Context context,
PhoneAccountRegistrar phoneAccountRegistrar,
DefaultDialerCache defaultDialerCache) {
return new MissedCallNotifierImpl(context,
phoneAccountRegistrar, defaultDialerCache);
}
},
.....
.......
........
}
if (BluetoothAdapter.getDefaultAdapter() != null) {
context.startService(new Intent(context, BluetoothPhoneService.class));
}
}
TelecomSystem 系統進程中,通話模塊的核心
TelecomSystem 構造函數
public TelecomSystem(
Context context,
......) {
mContext = context.getApplicationContext();
......
.....
// 構建CallsManager
mCallsManager = new CallsManager(
mContext,
mLock,
mContactsAsyncHelper,
..........);
mIncomingCallNotifier = incomingCallNotifier;
incomingCallNotifier.setCallsManagerProxy(......)
mCallsManager.setIncomingCallNotifier(mIncomingCallNotifier);
mRespondViaSmsManager = new RespondViaSmsManager(mCallsManager, mLock);
mCallsManager.setRespondViaSmsManager(mRespondViaSmsManager);
//註冊廣播
mContext.registerReceiver(mUserSwitchedReceiver, USER_SWITCHED_FILTER);
mContext.registerReceiver(mUserStartingReceiver, USER_STARTING_FILTER);
mContext.registerReceiver(mBootCompletedReceiver, BOOT_COMPLETE_FILTER);
mBluetoothPhoneServiceImpl = bluetoothPhoneServiceImplFactory.makeBluetoothPhoneServiceImpl(
mContext, mLock, mCallsManager, mPhoneAccountRegistrar);
// 構建CallIntentProcessor, 執行Intent的請求
mCallIntentProcessor = new CallIntentProcessor(mContext, mCallsManager);
mTelecomBroadcastIntentProcessor = new TelecomBroadcastIntentProcessor(
mContext, mCallsManager);
// Register the receiver for the dialer secret codes, used to enable extended logging.
mDialerCodeReceiver = new DialerCodeReceiver(mCallsManager);
mContext.registerReceiver(mDialerCodeReceiver, DIALER_SECRET_CODE_FILTER,
Manifest.permission.CONTROL_INCALL_EXPERIENCE, null);
// 構建TelecomServiceImpl,這個是TelecomSystem的真正實現,返回的binder由這裏提供,
// 外部對TelecomService的操作實際上都是在TelecomServiceImpl裏面進行的。
mTelecomServiceImpl = new TelecomServiceImpl(
mContext, mCallsManager, mPhoneAccountRegistrar,
new CallIntentProcessor.AdapterImpl(),
new UserCallIntentProcessorFactory() {
@Override
public UserCallIntentProcessor create(Context context, UserHandle userHandle) {
return new UserCallIntentProcessor(context, userHandle);
}
},
defaultDialerCache,
new TelecomServiceImpl.SubscriptionManagerAdapterImpl(),
mLock);
Log.endSession();
}
上面的構造都是在打電話之前執行的,下面繼續回到調起InCallUi流程
上面placeCall() 方法會進入到 TelecomServiceImpl.java中ITelecomService.Stub中的placeCall()方法,ITelecomService.Stub是一提供一個撥號入口。
TelecomServiceImpl.java
@Override
public void placeCall(Uri handle, Bundle extras, String callingPackage) {
try {
Log.startSession("TSI.pC");
enforceCallingPackage(callingPackage);//權限檢查,傳入對應的調用位置(調用位置org.codeaurora.dialer)
PhoneAccountHandle phoneAccountHandle = null;
if (extras != null) {
phoneAccountHandle = extras.getParcelable(
TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
if (extras.containsKey(TelecomManager.EXTRA_IS_HANDOVER)) {
// This extra is for Telecom use only so should never be passed in.
extras.remove(TelecomManager.EXTRA_IS_HANDOVER);
}
}
boolean isSelfManaged = phoneAccountHandle != null &&
isSelfManagedConnectionService(phoneAccountHandle);
if (isSelfManaged) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_OWN_CALLS,
"Self-managed ConnectionServices require MANAGE_OWN_CALLS permission.");
if (!callingPackage.equals(
phoneAccountHandle.getComponentName().getPackageName())
&& !canCallPhone(callingPackage,
"CALL_PHONE permission required to place calls.")) {
throw new SecurityException("Self-managed ConnectionServices can only "
+ "place calls through their own ConnectionService.");
}
} else if (!canCallPhone(callingPackage, "placeCall")) {
throw new SecurityException("Package " + callingPackage
+ " is not allowed to place phone calls");
}
final boolean hasCallAppOp = mAppOpsManager.noteOp(AppOpsManager.OP_CALL_PHONE,
Binder.getCallingUid(), callingPackage) == AppOpsManager.MODE_ALLOWED;
final boolean hasCallPermission = mContext.checkCallingPermission(CALL_PHONE) ==
PackageManager.PERMISSION_GRANTED;
final boolean hasCallPrivilegedPermission = mContext.checkCallingPermission(
CALL_PRIVILEGED) == PackageManager.PERMISSION_GRANTED;
synchronized (mLock) {
final UserHandle userHandle = Binder.getCallingUserHandle();
long token = Binder.clearCallingIdentity();
try {
final Intent intent = new Intent(hasCallPrivilegedPermission ?
Intent.ACTION_CALL_PRIVILEGED : Intent.ACTION_CALL, handle);
if (extras != null) {
extras.setDefusable(true);
intent.putExtras(extras);
}
// 核心,主過程
mUserCallIntentProcessorFactory.create(mContext, userHandle)
.processIntent(
intent, callingPackage, isSelfManaged ||
(hasCallAppOp && hasCallPermission),
true /* isLocalInvocation */);
} finally {
Binder.restoreCallingIdentity(token);
}
}
} finally {
Log.endSession();
}
}
接下來UserCallIntentProcessor.java主要處理一些 內部校驗一些撥號的權限,以及其它操作權限看看是不是需要彈框拒絕。比如 《只允許打緊急電話,沒有電話許可,此應用程序不能發出呼叫》
UserCallIntentProcessor.java
public void processIntent(Intent intent, String callingPackageName,
boolean canCallNonEmergency, boolean isLocalInvocation) {
// 確保無法調用的設備上沒有處理調用意圖。
if (!isVoiceCapable()) {
return;
}
String action = intent.getAction();
if (Intent.ACTION_CALL.equals(action) ||
Intent.ACTION_CALL_PRIVILEGED.equals(action) ||
Intent.ACTION_CALL_EMERGENCY.equals(action)) {
processOutgoingCallIntent(intent, callingPackageName, canCallNonEmergency,
isLocalInvocation); //調用processOutgoingCallIntent()方法緊接着執行
}
}
// 核心,主過程 內部校驗一些撥號的權限,以及其它操作權限看看是不是需要彈框拒絕。比如 《只允許打緊急電話,沒有電話許可,此應用程序不能發出呼叫》
private void processOutgoingCallIntent(Intent intent, String callingPackageName,
boolean canCallNonEmergency, boolean isLocalInvocation) {
Uri handle = intent.getData();
String scheme = handle.getScheme();
String uriString = handle.getSchemeSpecificPart();
// 確保使用TEL方案撥打的sip uri轉換爲sip方案。
if (PhoneAccount.SCHEME_TEL.equals(scheme) && PhoneNumberUtils.isUriNumber(uriString)) {
handle = Uri.fromParts(PhoneAccount.SCHEME_SIP, uriString, null);
}
//檢查DISALLOW_OUTGOING_CALLS限制。注意:我們在託管配置文件用戶中跳過此檢查,因爲總是可以通過複製和粘貼電話號碼到個人撥號器來繞過此檢查。
if (!UserUtil.isManagedProfile(mContext, mUserHandle)) {
// restriction. 只有使用DISALLOW_OUTGOING_CALLS限制的用戶才允許使用緊急呼叫。
if (!TelephonyUtil.shouldProcessAsEmergency(mContext, handle)) {
final UserManager userManager = (UserManager) mContext.getSystemService(
Context.USER_SERVICE);
if (userManager.hasBaseUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS,
mUserHandle)) {
showErrorDialogForRestrictedOutgoingCall(mContext,
R.string.outgoing_call_not_allowed_user_restriction);
return;
} else if (userManager.hasUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS,
mUserHandle)) {
final DevicePolicyManager dpm =
mContext.getSystemService(DevicePolicyManager.class);
if (dpm == null) {
return;
}
final Intent adminSupportIntent = dpm.createAdminSupportIntent(
UserManager.DISALLOW_OUTGOING_CALLS);
if (adminSupportIntent != null) {
mContext.startActivity(adminSupportIntent);
}
return;
}
}
}
if (!canCallNonEmergency && !TelephonyUtil.shouldProcessAsEmergency(mContext, handle)) {
showErrorDialogForRestrictedOutgoingCall(mContext,
R.string.outgoing_call_not_allowed_no_permission);
return;
}
int videoState = intent.getIntExtra(
TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
VideoProfile.STATE_AUDIO_ONLY);
intent.putExtra(CallIntentProcessor.KEY_IS_PRIVILEGED_DIALER,
isDefaultOrSystemDialer(callingPackageName));
//在將意圖轉發給主用戶之前,保存當前用戶的用戶handle。
intent.putExtra(CallIntentProcessor.KEY_INITIATING_USER, mUserHandle);
// 核心,主過程 //檢查一些權限後,執行那個下面代碼發送廣播
sendIntentToDestination(intent, isLocalInvocation);
}
/**
* rebroadcasting it. 潛在地將意圖傳遞給僅作爲主要用戶運行的廣播接收器。如果調用方是電信服務的本地調用方,我們將發送意圖給電信,而不進行重播。
*/
private boolean sendIntentToDestination(Intent intent, boolean isLocalInvocation) {
intent.putExtra(CallIntentProcessor.KEY_IS_INCOMING_CALL, false);
intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
intent.setClass(mContext, PrimaryCallReceiver.class); // //開啓廣播 最後進入到 PrimaryCallReceiver
if (isLocalInvocation) {
synchronized (TelecomSystem.getInstance().getLock()) {
TelecomSystem.getInstance().getCallIntentProcessor().processIntent(intent);
}
} else {
mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM); //發送廣播
}
return true;
}
接下來進入關播PrimaryCallReceiver中 ,PrimaryCallReceiver作爲撥號的單一入口,其收到消息後就轉給TelecomSystem中的CallIntentProcessor進行處理
PrimartCallReceiver.java
@Override
public void onReceive(Context context, Intent intent) {//作爲撥號的單一入口, 其收到消息後,就轉發給 TelecomSystem的
Log.startSession("PCR.oR");
synchronized (getTelecomSystem().getLock()) {
getTelecomSystem().getCallIntentProcessor().processIntent(intent);//調用所有呼出和呼入電話的單一入口
}
Log.endSession();
}
CallIntentProcessor.java
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();
}
static void processOutgoingCallIntent(
Context context,
CallsManager callsManager,
Intent intent) {
Uri handle = intent.getData();
String scheme = handle.getScheme();
String uriString = handle.getSchemeSpecificPart();
boolean isSkipSchemaParsing = intent.getBooleanExtra(
TelephonyProperties.EXTRA_SKIP_SCHEMA_PARSING, false);
Log.d(CallIntentProcessor.class, "isSkipSchemaParsing = " + isSkipSchemaParsing);
if (PhoneAccount.SCHEME_TEL.equals(scheme) && PhoneNumberUtils.isUriNumber(uriString) && !isSkipSchemaParsing) {
handle = Uri.fromParts(PhoneAccount.SCHEME_SIP, uriString, null);
}
PhoneAccountHandle phoneAccountHandle = intent.getParcelableExtra(
TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
Bundle clientExtras = null;
if (intent.hasExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS)) {
clientExtras = intent.getBundleExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS);
}
if (clientExtras == null) {
clientExtras = new Bundle();
}
if (isSkipSchemaParsing) {
clientExtras.putBoolean(TelephonyProperties.EXTRA_SKIP_SCHEMA_PARSING,
isSkipSchemaParsing);
handle = Uri.fromParts(PhoneAccount.SCHEME_TEL, handle.toString(), null);
}
boolean isConferenceUri = intent.getBooleanExtra(
TelephonyProperties.EXTRA_DIAL_CONFERENCE_URI, false);
Log.d(CallIntentProcessor.class, "isConferenceUri = "+isConferenceUri);
if (isConferenceUri) {
clientExtras.putBoolean(TelephonyProperties.EXTRA_DIAL_CONFERENCE_URI, isConferenceUri);
}
boolean isAddParticipant = intent.getBooleanExtra(
TelephonyProperties.ADD_PARTICIPANT_KEY, false);
Log.d(CallIntentProcessor.class, "isAddparticipant = "+isAddParticipant);
if (isAddParticipant) {
clientExtras.putBoolean(TelephonyProperties.ADD_PARTICIPANT_KEY, isAddParticipant);
}
if (intent.hasExtra(TelecomManager.EXTRA_CALL_SUBJECT)) {
String callsubject = intent.getStringExtra(TelecomManager.EXTRA_CALL_SUBJECT);
clientExtras.putString(TelecomManager.EXTRA_CALL_SUBJECT, callsubject);
}
final int callDomain = intent.getIntExtra(
QtiCallConstants.EXTRA_CALL_DOMAIN, QtiCallConstants.DOMAIN_AUTOMATIC);
Log.d(CallIntentProcessor.class, "callDomain = " + callDomain);
clientExtras.putInt(QtiCallConstants.EXTRA_CALL_DOMAIN, callDomain);
final int videoState = intent.getIntExtra( TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
VideoProfile.STATE_AUDIO_ONLY);
clientExtras.putInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, videoState);
if (!callsManager.isSelfManaged(phoneAccountHandle,
(UserHandle) intent.getParcelableExtra(KEY_INITIATING_USER))) {
boolean fixedInitiatingUser = fixInitiatingUserIfNecessary(context, intent);
if (fixedInitiatingUser) {
Toast.makeText(context, R.string.toast_personal_call_msg, Toast.LENGTH_LONG).show();
}
} else {
Log.i(CallIntentProcessor.class,
"processOutgoingCallIntent: skip initiating user check");
}
UserHandle initiatingUser = intent.getParcelableExtra(KEY_INITIATING_USER);
TelecomManager telecomManager =
(TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
List<PhoneAccountHandle> mSubscriptionAccountHandles = telecomManager.getCallCapablePhoneAccounts();
boolean isDualCard = (mSubscriptionAccountHandles != null && mSubscriptionAccountHandles.size() > 1);
if(isDualCard) {
String phoneNumber = intent.getData().getSchemeSpecificPart();
phoneNumber = PhoneNumberUtils.stripSeparators(phoneNumber);
int simGroup = SimSelectDialog.getCardByNumber(context, phoneNumber) - 1;
if (simGroup >= 0) {
if (phoneAccountHandle == null) {
phoneAccountHandle = telecomManager.getUserSelectedOutgoingPhoneAccount();
}
if (phoneAccountHandle != null) {
if (!phoneAccountHandle.equals(mSubscriptionAccountHandles.get(simGroup))) {
......
}
/**end*/
// 創建一個call.startOutgoingCall 新建一個(或者重用) call, 將CallsManager綁定監聽該 call, 然後通知 callsManager 的監聽者, 添加了一個 call
// Send to CallsManager to ensure the InCallUI gets kicked off before the broadcast returns
Call call = callsManager
.startOutgoingCall(handle, phoneAccountHandle, clientExtras, initiatingUser,
intent);
//這是一個斷點,因爲下面將會詳細地深入分析 callsManager.startOutgoingCall 的流程,下面分析 startOutgoingCall 完成後, 再從 NewOutgoingCallIntentBroadcaster.processIntent()往下分析
if (call != null) {
sendNewOutgoingCallIntent(context, call, callsManager, intent);//拿到上面創建的call, 進行下一步
}
}
這裏重點看callsManager.startOutgoingCall(......);這裏就是調起InCallUi的入口,startOutgoingCall新建一個(或者重用)call,將CallManager綁定監聽該call,然後通知callsManager的監聽,添加了一個call
sendNewOutgoingCallIntent()方法
static void sendNewOutgoingCallIntent(Context context, Call call, CallsManager callsManager,
Intent intent) {
NewOutgoingCallIntentBroadcaster broadcaster = new NewOutgoingCallIntentBroadcaster(
context, callsManager, call, intent, callsManager.getPhoneNumberUtilsAdapter(),
isPrivilegedDialer);
final int result = broadcaster.processIntent(); //核心入口
......
}
NewOutgoingCallIntentBroadcaster.java
@VisibleForTesting
public int processIntent() {
Log.v(this, "Processing call intent in OutgoingCallIntentBroadcaster.");
Intent intent = mIntent;
String action = intent.getAction();
final Uri handle = intent.getData();
if (handle == null) {
Log.w(this, "Empty handle obtained from the call intent.");
return DisconnectCause.INVALID_NUMBER;
}
boolean isVoicemailNumber = PhoneAccount.SCHEME_VOICEMAIL.equals(handle.getScheme());
if (isVoicemailNumber) {
if (Intent.ACTION_CALL.equals(action)
|| Intent.ACTION_CALL_PRIVILEGED.equals(action)) {
// 語音郵件呼叫將由電話連接管理器直接處理
boolean speakerphoneOn = mIntent.getBooleanExtra(
TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, false);
placeOutgoingCallImmediately(mCall, handle, null, speakerphoneOn,
VideoProfile.STATE_AUDIO_ONLY);//核心入口
return DisconnectCause.NOT_DISCONNECTED;
} else {
Log.i(this, "Unhandled intent %s. Ignoring and not placing call.", intent);
return DisconnectCause.OUTGOING_CANCELED;
}
}
........
return DisconnectCause.NOT_DISCONNECTED;
}
private void placeOutgoingCallImmediately(Call call, Uri handle, GatewayInfo gatewayInfo,
boolean speakerphoneOn, int videoState) {
Log.i(this,
"Placing call immediately instead of waiting for OutgoingCallBroadcastReceiver");
// 準備開啓廣播進行呼叫
mCall.setNewOutgoingCallIntentBroadcastIsDone();
mCallsManager.placeOutgoingCall(call, handle, gatewayInfo, speakerphoneOn, videoState);//核心入口
}
CallManager.java
public void placeOutgoingCall(Call call, Uri handle, GatewayInfo gatewayInfo,
boolean speakerphoneOn, int videoState) {
if (call == null) {
// 如果不存在就退出
return;
}
final Uri uriHandle = (gatewayInfo == null) ? handle : gatewayInfo.getGatewayAddress();
if (gatewayInfo == null) {
} else {
call.setHandle(uriHandle);
call.setGatewayInfo(gatewayInfo);
final boolean useSpeakerWhenDocked = mContext.getResources().getBoolean(
R.bool.use_speaker_when_docked);
final boolean useSpeakerForDock = isSpeakerphoneEnabledForDock();
final boolean useSpeakerForVideoCall = isSpeakerphoneAutoEnabledForVideoCalls(videoState);
// Auto-enable speakerphone if the originating intent specified to do so, if the call
// is a video call, of if using speaker when docked
call.setStartWithSpeakerphoneOn(speakerphoneOn || useSpeakerForVideoCall
|| (useSpeakerWhenDocked && useSpeakerForDock));
call.setVideoState(videoState);
if (speakerphoneOn) {
Log.i(this, "%s Starting with speakerphone as requested", call);
} else if (useSpeakerWhenDocked && useSpeakerForDock) {
Log.i(this, "%s Starting with speakerphone because car is docked.", call);
} else if (useSpeakerForVideoCall) {
Log.i(this, "%s Starting with speakerphone because its a video call.", call);
}
if (call.isEmergencyCall()) {
new AsyncEmergencyContactNotifier(mContext).execute();
}
final boolean requireCallCapableAccountByHandle = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_requireCallCapableAccountForHandle);
final boolean isOutgoingCallPermitted = isOutgoingCallPermitted(call,
call.getTargetPhoneAccount());
final String callHandleScheme =
call.getHandle() == null ? null : call.getHandle().getScheme();
if (call.getTargetPhoneAccount() != null || call.isEmergencyCall()) {
// If the account has been set, proceed to place the outgoing call.
// Otherwise the connection will be initiated when the account is set by the user.
if (call.isSelfManaged() && !isOutgoingCallPermitted) {
notifyCreateConnectionFailed(call.getTargetPhoneAccount(), call);
} else {
if (call.isEmergencyCall()) {
// Drop any ongoing self-managed calls to make way for an emergency call
disconnectSelfManagedCalls("place emerg call" /* reason */);
}
if (mPendingMOEmerCall == null) {
//如果已設置帳戶,則繼續撥打呼出電話。否則,當用戶設置帳戶時將啓動連接。
call.startCreateConnection(mPhoneAccountRegistrar);//核心入口
}
}
} else if (mPhoneAccountRegistrar.getCallCapablePhoneAccounts(
requireCallCapableAccountByHandle ? callHandleScheme : null, false,
call.getInitiatingUser()).isEmpty()) {
markCallAsDisconnected(call, new DisconnectCause(DisconnectCause.CANCELED,
"No registered PhoneAccounts"));
markCallAsRemoved(call);
}
}
Call.java
//啓動創建連接序列。完成後,應該存在通過連接服務的活動連接(
void startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar) {
if (mCreateConnectionProcessor != null) {
return;
}
mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this,
phoneAccountRegistrar, mContext); //Call 由 CallsManager 維護的撥號,或者來電實例
mCreateConnectionProcessor.process();//核心入口
}
CreateConnectionProcessor.java
@VisibleForTesting
public void process() {
Log.v(this, "process");
clearTimeout();
mAttemptRecords = new ArrayList<>();
if (mCall.getTargetPhoneAccount() != null) {
mAttemptRecords.add(new CallAttemptRecord(
mCall.getTargetPhoneAccount(), mCall.getTargetPhoneAccount()));
}
if (!mCall.isSelfManaged()) {
adjustAttemptsForConnectionManager();
adjustAttemptsForEmergency(mCall.getTargetPhoneAccount());
}
mAttemptRecordIterator = mAttemptRecords.iterator();
attemptNextPhoneAccount();//核心入口
}
private void attemptNextPhoneAccount() {
......
if (mCallResponse != null && attempt != null) {
Log.i(this, "Trying attempt %s", attempt);
PhoneAccountHandle phoneAccount = attempt.connectionManagerPhoneAccount;
mService = mRepository.getService(phoneAccount.getComponentName(),
phoneAccount.getUserHandle());// 取到一個 ConnectionServiceWrapper (有緩存)
if (mService == null) {
Log.i(this, "Found no connection service for attempt %s", attempt);
attemptNextPhoneAccount();
} else {
mConnectionAttempt++;
mCall.setConnectionManagerPhoneAccount(attempt.connectionManagerPhoneAccount);
mCall.setTargetPhoneAccount(attempt.targetPhoneAccount);
mCall.setConnectionService(mService);
setTimeoutIfNeeded(mService, attempt);
if (mCall.isIncoming()) {
mService.createConnection(mCall, CreateConnectionProcessor.this); // 開始進行鏈接,核心過程
} else {
// Start to create the connection for outgoing call after the ConnectionService
// of the call has gained the focus.
mCall.getConnectionServiceFocusManager().requestFocus(
mCall,
new CallsManager.RequestCallback(new CallsManager.PendingAction() {
@Override
public void performAction() {
Log.d(this, "perform create connection");
mService.createConnection(
mCall,
CreateConnectionProcessor.this);
}
}));
}
}
} else {
DisconnectCause disconnectCause = mLastErrorDisconnectCause != null ?
mLastErrorDisconnectCause : new DisconnectCause(DisconnectCause.ERROR);
notifyCallConnectionFailure(disconnectCause);
}
}
ConnectionServiceWrapper.java
ConnectionServiceWrapper 負責和 Telephony 溝通 framework/base/telephony
創建鏈接就是一個綁定服務的過程
/**
* 爲新的傳出呼叫或附加到現有的傳入呼叫創建新連接。
*/
@VisibleForTesting
public void createConnection(final Call call, final CreateConnectionResponse response) {//創建鏈接就是一個綁定服務的過程
Log.d(this, "createConnection(%s) via %s.", call, getComponentName());
BindCallback callback = new BindCallback() {// 鏈接創建成功後 onSuccess 會被調用
@Override
public void onSuccess() {
.......
........
try {
mServiceInterface.createConnection( // 通過遠程接口創建鏈接 利用遠程接口創建一個通話鏈接
call.getConnectionManagerPhoneAccount(),
callId,
connectionRequest,
call.shouldAttachToExistingConnection(),
call.isUnknown(),
Log.getExternalSession());
} catch (RemoteException e) {
Log.e(this, e, "Failure to createConnection -- %s", getComponentName());
mPendingResponses.remove(callId).handleCreateConnectionFailure(
new DisconnectCause(DisconnectCause.ERROR, e.toString()));
}
}
@Override
public void onFailure() {
....
}
};
//綁定
mBinder.bind(callback, call);
}
進入mBinder.bind中 ConnectionSerivceWrapper.java是ServiceBinder的子類
bind是進行綁定服務,回調裏面的BindCallbak纔會創建
SerivceBinder.java
void bind(BindCallback callback, Call call) {
......
//在類裏面綁定成功後mBinder.bind(callback, call);在父類ServiceBinder類裏面mCallbacks.add(callback);吧回調用集合給裝載起來,然後進行便利數據
//成功獲取到aidl接口後將其賦值mServiceInterface,
mCallbacks.add(callback);
if (mServiceConnection == null) {
Intent serviceIntent = new Intent(mServiceAction).setComponent(mComponentName);
ServiceConnection connection = new ServiceBinderConnection(call);
final int bindingFlags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE;
final boolean isBound;
if (mUserHandle != null) {// 進行綁定
isBound = mContext.bindServiceAsUser(serviceIntent, connection, bindingFlags, //如果綁定成功調用connection方法 這裏綁定是綁定了framework/telepony
mUserHandle);
} else {
isBound = mContext.bindService(serviceIntent, connection, bindingFlags);
}
if (!isBound) {
handleFailedConnection();
return;
}
} else {
Log.d(ServiceBinder.this, "Service is already bound.");
Preconditions.checkNotNull(mBinder);
handleSuccessfulConnection();
}
}
}
這裏綁定的是ConnectionSerivce 如果綁定成功調用connection方法 這裏綁定的是framework/telecomm中的ConnectionSerivce
執行到bindSerivce就已經初始化ConnectionSerivce了 ,然後回調connection,connection繼承了SeviceConnection,所以連接成功後會回調回來。
private final class ServiceBinderConnection implements ServiceConnection {
/**
* 綁定服務的初始調用。
*/
private Call mCall;
ServiceBinderConnection(Call call) {
mCall = call;
}
@Override
public void onServiceConnected(ComponentName componentName, IBinder binder) {
try {
Log.startSession("SBC.oSC");
synchronized (mLock) {
Log.i(this, "Service bound %s", componentName);
Log.addEvent(mCall, LogUtils.Events.CS_BOUND, componentName);
mCall = null;
// 取消綁定請求被排隊,因此立即取消綁定。
if (mIsBindingAborted) {
clearAbort();
logServiceDisconnected("onServiceConnected");
mContext.unbindService(this);
handleFailedConnection();
return;
}
if (binder != null) {
mServiceDeathRecipient = new ServiceDeathRecipient(componentName);
try {
binder.linkToDeath(mServiceDeathRecipient, 0);
mServiceConnection = this;
// 設置 mServiceInterface
// 會觸發 ConnectionServiceWrapper.setServiceInterface ==> ConnectionServiceWrapper.addConnectionServiceAdapter
// 通過mServiceInterface,給綁定的服務,提供一個訪問自己的接口
setBinder(binder);// 觸發bind(BindCallback callback, Call call) 中 callback 的 onSuccess
handleSuccessfulConnection(); //回調進入子類
//整個綁定過程,只做2件事,一是給遠程服務提供訪問自己的接口,二是利用遠程接口創建一個通話鏈接。
// 這2件事都是跨進程進行的。遠程服務訪問自己的接口是
} catch (RemoteException e) {
Log.w(this, "onServiceConnected: %s died.");
if (mServiceDeathRecipient != null) {
mServiceDeathRecipient.binderDied();
}
}
}
}
} finally {
Log.endSession();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {//連接失敗
........
}
}
private void setBinder(IBinder binder) {
if (mBinder != binder) {
if (binder == null) {
......
} else {
mBinder = binder;
setServiceInterface(binder);//進入子類
}
}
}
private void handleSuccessfulConnection() {
for (BindCallback callback : mCallbacks) {
callback.onSuccess(); //回調傳入的callback
}
mCallbacks.clear();
}
這裏的setBinder();
這個方法主要是調用子類去與ConnectionService進行綁定 ,綁定成功後可以進行隨時改變InCallUI的狀態 。
這裏我們講解一下綁定的邏輯
綁定的入口在ConnectionServiceWrapper.java中開始綁入口,在哪createConnection方法中
先看流程圖 流程圖從ConnectionSerivceWrapper中的createConnection入口
這裏連接我會分開寫,單獨寫一篇文章已代碼方式進行處理
鏈接:https://blog.csdn.net/qq_35427437/article/details/101067804
流程圖:
ConnectionServiceWrapper.java
ConnectionServiceWrapper.java中的BindCallback裏面的onSuccess方法
public void onSuccess() {
.....
try {
mServiceInterface.createConnection( // 通過遠程接口創建鏈接 利用遠程接口創建一個通話鏈接
call.getConnectionManagerPhoneAccount(),
callId,
connectionRequest,
call.shouldAttachToExistingConnection(),
call.isUnknown(),
Log.getExternalSession());
} catch (RemoteException e) {
Log.e(this, e, "Failure to createConnection -- %s", getComponentName());
mPendingResponses.remove(callId).handleCreateConnectionFailure(
new DisconnectCause(DisconnectCause.ERROR, e.toString()));
}
}
利用遠程接口創建一個通話鏈接完成,進入createConnection中進行處理電話
第四部 Phone進程
ConnectionService.java
public void createConnection(
PhoneAccountHandle connectionManagerPhoneAccount,
String id,
ConnectionRequest request,
boolean isIncoming,
boolean isUnknown,
Session.Info sessionInfo) {
Log.startSession(sessionInfo, SESSION_CREATE_CONN);
try {
SomeArgs args = SomeArgs.obtain();
args.arg1 = connectionManagerPhoneAccount;
args.arg2 = id;
args.arg3 = request;
args.arg4 = Log.createSubsession();
args.argi1 = isIncoming ? 1 : 0;
args.argi2 = isUnknown ? 1 : 0;
mHandler.obtainMessage(MSG_CREATE_CONNECTION, args).sendToTarget();
} finally {
Log.endSession();
}
}
傳遞給Handler
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_CREATE_CONNECTION: { // 創建鏈接
SomeArgs args = (SomeArgs) msg.obj;
Log.continueSession((Session) args.arg4, SESSION_HANDLER + SESSION_CREATE_CONN);
try {
final PhoneAccountHandle connectionManagerPhoneAccount =
(PhoneAccountHandle) args.arg1;
final String id = (String) args.arg2;
final ConnectionRequest request = (ConnectionRequest) args.arg3;
final boolean isIncoming = args.argi1 == 1;
final boolean isUnknown = args.argi2 == 1;
if (!mAreAccountsInitialized) {
Log.d(this, "Enqueueing pre-init request %s", id);
mPreInitializationConnectionRequests.add(
new android.telecom.Logging.Runnable(
SESSION_HANDLER + SESSION_CREATE_CONN + ".pICR",
null /*lock*/) {
@Override
public void loggedRun() {
createConnection(
connectionManagerPhoneAccount,
id,
request,
isIncoming,
isUnknown);
}
}.prepare());
} else {
createConnection(
connectionManagerPhoneAccount,
id,
request,
isIncoming,
isUnknown);
}
} finally {
args.recycle();
Log.endSession();
}
break;
}
}
進入createConnection()方法
private void createConnection(
final PhoneAccountHandle callManagerAccount,
final String callId,
final ConnectionRequest request,
boolean isIncoming,
boolean isUnknown) {
boolean isLegacyHandover = request.getExtras() != null &&
request.getExtras().getBoolean(TelecomManager.EXTRA_IS_HANDOVER, false);
boolean isHandover = request.getExtras() != null && request.getExtras().getBoolean(
TelecomManager.EXTRA_IS_HANDOVER_CONNECTION, false);
Log.d(this, "createConnection, callManagerAccount: %s, callId: %s, request: %s, " +
"isIncoming: %b, isUnknown: %b, isLegacyHandover: %b, isHandover: %b",
callManagerAccount, callId, request, isIncoming, isUnknown, isLegacyHandover,
isHandover);
Connection connection = null;
if (isHandover) {
PhoneAccountHandle fromPhoneAccountHandle = request.getExtras() != null
? (PhoneAccountHandle) request.getExtras().getParcelable(
TelecomManager.EXTRA_HANDOVER_FROM_PHONE_ACCOUNT) : null;
if (!isIncoming) {
connection = onCreateOutgoingHandoverConnection(fromPhoneAccountHandle, request);
} else {
connection = onCreateIncomingHandoverConnection(fromPhoneAccountHandle, request);
}
} else {
connection = isUnknown ? onCreateUnknownConnection(callManagerAccount, request)
: isIncoming ? onCreateIncomingConnection(callManagerAccount, request)
: onCreateOutgoingConnection(callManagerAccount, request);//onCreateOutgoingConnection撥號入口
}
if (connection == null) {
Log.i(this, "createConnection, implementation returned null connection.");
connection = Connection.createFailedConnection(
new DisconnectCause(DisconnectCause.ERROR, "IMPL_RETURNED_NULL_CONNECTION"));
}
connection.setTelecomCallId(callId);
if (connection.getState() != Connection.STATE_DISCONNECTED) {
// 監聽 Connection 的狀態變化,通知 system 進程(CallsManager)更新
// 通過 system 進程提供的 adapter 實現
addConnection(request.getAccountHandle(), callId, connection);
}
Uri address = connection.getAddress();
String number = address == null ? "null" : address.getSchemeSpecificPart();
Log.d(this, "createConnection, calling handleCreateConnectionSuccessful %s", callId);
//mAdapter.handleCreateConnectionComplete 調用回到 ConnectionServiceWrapper 的 handleCreateConnectionComplete,
// 然後回到 Call 的 handleCreateConnectionSuccess
mAdapter.handleCreateConnectionComplete(// 通知 system 進程(即CallsManager) 電話鏈接已經創建完成
callId,
request,
new ParcelableConnection(
request.getAccountHandle(),
connection.getState(),
connection.getConnectionCapabilities(),
connection.getConnectionProperties(),
connection.getSupportedAudioRoutes(),
connection.getAddress(),
connection.getAddressPresentation(),
connection.getCallerDisplayName(),
connection.getCallerDisplayNamePresentation(),
connection.getVideoProvider() == null ?
null : connection.getVideoProvider().getInterface(),
connection.getVideoState(),
connection.isRingbackRequested(),
connection.getAudioModeIsVoip(),
connection.getConnectTimeMillis(),
connection.getConnectElapsedTimeMillis(),
connection.getStatusHints(),
connection.getDisconnectCause(),
createIdList(connection.getConferenceables()),
connection.getExtras()));
if (isIncoming && request.shouldShowIncomingCallUi() &&
(connection.getConnectionProperties() & Connection.PROPERTY_SELF_MANAGED) ==
Connection.PROPERTY_SELF_MANAGED) {
// Tell ConnectionService to show its incoming call UX.
connection.onShowIncomingCallUi();
}
if (isUnknown) {
triggerConferenceRecalculate();
}
}
進入onCreateOutgoingConnection()方法這個方法在子類實現,而子類在packeger/service/telephony裏面
TelephonyConnectionService.java
@Override
public Connection onCreateOutgoingConnection(
PhoneAccountHandle connectionManagerPhoneAccount,
final ConnectionRequest request) {
.....
....
if (resultConnection instanceof TelephonyConnection) {
if (request.getExtras() != null && request.getExtras().getBoolean(
TelecomManager.EXTRA_USE_ASSISTED_DIALING, false)) {
((TelephonyConnection) resultConnection).setIsUsingAssistedDialing(true);
}
placeOutgoingConnection((TelephonyConnection) resultConnection, phone, request);//進行撥號流程,主流程
return resultConnection;
}
}
private void placeOutgoingConnection(
TelephonyConnection connection, Phone phone, ConnectionRequest request) {
placeOutgoingConnection(connection, phone, request.getVideoState(), request.getExtras());
}
private void placeOutgoingConnection(
TelephonyConnection connection, Phone phone, int videoState, Bundle extras) {
String number = connection.getAddress().getSchemeSpecificPart();
boolean isAddParticipant = (extras != null) && extras
.getBoolean(TelephonyProperties.ADD_PARTICIPANT_KEY, false);
Log.d(this, "placeOutgoingConnection isAddParticipant = " + isAddParticipant);
updatePhoneAccount(connection, phone);
com.android.internal.telephony.Connection originalConnection = null;
try {
if (phone != null) {
if (isAddParticipant) {
phone.addParticipant(number);// 爲緊急號碼做一些處理
return;
} else {
originalConnection = phone.dial(number, new ImsPhone.ImsDialArgs.Builder() //呼叫
.setVideoState(videoState)
.setIntentExtras(extras)
.setRttTextStream(connection.getRttTextStream())
.build());
}
}
} catch (CallStateException e) { // 失敗處理
Log.e(this, e, "placeOutgoingConnection, phone.dial exception: " + e);
int cause = android.telephony.DisconnectCause.OUTGOING_FAILURE;
if (e.getError() == CallStateException.ERROR_OUT_OF_SERVICE) {
cause = android.telephony.DisconnectCause.OUT_OF_SERVICE;
} else if (e.getError() == CallStateException.ERROR_POWER_OFF) {
cause = android.telephony.DisconnectCause.POWER_OFF;
}
connection.setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
cause, e.getMessage(), phone.getPhoneId()));
connection.clearOriginalConnection();
connection.destroy();
return;
}
if (originalConnection == null) { // 失敗處理
int telephonyDisconnectCause = android.telephony.DisconnectCause.OUTGOING_FAILURE;
// On GSM phones, null connection means that we dialed an MMI code
if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM) {
Log.d(this, "dialed MMI code");
int subId = phone.getSubId();
Log.d(this, "subId: "+subId);
telephonyDisconnectCause = android.telephony.DisconnectCause.DIALED_MMI;
final Intent intent = new Intent(this, MMIDialogActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
if (SubscriptionManager.isValidSubscriptionId(subId)) {
intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
}
startActivity(intent);
}
Log.d(this, "placeOutgoingConnection, phone.dial returned null");
connection.setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
telephonyDisconnectCause, "Connection is null", phone.getPhoneId()));
connection.clearOriginalConnection();
connection.destroy();
} else {
connection.setOriginalConnection(originalConnection); // TelephonyConnection 監聽 phone, 更新時,從originalConnection裏面取。
//TelephonyConnection.setOriginalConnection 的實現, 目的主要是爲將 mHandler 註冊註冊到
//phone 的監聽者列表裏面, phone 變化, 會觸發 mHandler 裏面的handleMessage 被調用。
}
}
placeOutgoingConnection只做2件事,一是使用對應的phone創建對應的連接,二是讓TelephonyConnectijon和連接建立起關聯,一個TelephonyConnection維護一個連接,Original變化會引起TelephonyCnnnection變化。
進入了framewrok/opt/telephony
GsmCdmaPhone.java
現在流程回到 phone.dial 只分析GSM類型的phone, GSMPhone.dial 如下
@Override
public Connection dial(String dialString, @NonNull DialArgs dialArgs)
throws CallStateException {
if (!isPhoneTypeGsm() && dialArgs.uusInfo != null) {
throw new CallStateException("Sending UUS information NOT supported in CDMA!");
}
boolean isEmergency = isEmergencyNumber(dialString);
Phone imsPhone = mImsPhone;
CarrierConfigManager configManager =
(CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
boolean alwaysTryImsForEmergencyCarrierConfig = configManager.getConfigForSubId(getSubId())
.getBoolean(CarrierConfigManager.KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL);
boolean useImsForCall = isImsUseEnabled()
&& imsPhone != null
&& (imsPhone.isVolteEnabled() || imsPhone.isWifiCallingEnabled() ||
(imsPhone.isVideoEnabled() && VideoProfile.isVideo(dialArgs.videoState)))
&& (imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE)
&& !shallDialOnCircuitSwitch(dialArgs.intentExtras);
boolean useImsForEmergency = imsPhone != null
&& isEmergency
&& alwaysTryImsForEmergencyCarrierConfig
&& ImsManager.getInstance(mContext, mPhoneId).isNonTtyOrTtyOnVolteEnabled()
&& imsPhone.isImsAvailable();
String dialPart = PhoneNumberUtils.extractNetworkPortionAlt(PhoneNumberUtils.
stripSeparators(dialString));
boolean isUt = (dialPart.startsWith("*") || dialPart.startsWith("#"))
&& dialPart.endsWith("#");
boolean useImsForUt = imsPhone != null && imsPhone.isUtEnabled();
if (DBG) {
logd("useImsForCall=" + useImsForCall
+ ", useImsForEmergency=" + useImsForEmergency
+ ", useImsForUt=" + useImsForUt
+ ", isUt=" + isUt
+ ", imsPhone=" + imsPhone
+ ", imsPhone.isVolteEnabled()="
+ ((imsPhone != null) ? imsPhone.isVolteEnabled() : "N/A")
+ ", imsPhone.isVowifiEnabled()="
+ ((imsPhone != null) ? imsPhone.isWifiCallingEnabled() : "N/A")
+ ", imsPhone.isVideoEnabled()="
+ ((imsPhone != null) ? imsPhone.isVideoEnabled() : "N/A")
+ ", imsPhone.getServiceState().getState()="
+ ((imsPhone != null) ? imsPhone.getServiceState().getState() : "N/A"));
}
Phone.checkWfcWifiOnlyModeBeforeDial(mImsPhone, mPhoneId, mContext);
if ((useImsForCall && !isUt) || (isUt && useImsForUt) || useImsForEmergency) {
try {
if (DBG) logd("Trying IMS PS call");
return imsPhone.dial(dialString, dialArgs);
} catch (CallStateException e) {
if (DBG) logd("IMS PS call exception " + e +
"useImsForCall =" + useImsForCall + ", imsPhone =" + imsPhone);
// Do not throw a CallStateException and instead fall back to Circuit switch
// for emergency calls and MMI codes.
if (Phone.CS_FALLBACK.equals(e.getMessage()) || isEmergency) {
logi("IMS call failed with Exception: " + e.getMessage() + ". Falling back "
+ "to CS.");
} else {
CallStateException ce = new CallStateException(e.getMessage());
ce.setStackTrace(e.getStackTrace());
throw ce;
}
}
}
if (mSST != null && mSST.mSS.getState() == ServiceState.STATE_OUT_OF_SERVICE
&& mSST.mSS.getDataRegState() != ServiceState.STATE_IN_SERVICE && !isEmergency) {
throw new CallStateException("cannot dial in current state");
}
// Check non-emergency voice CS call - shouldn't dial when POWER_OFF
if (mSST != null && mSST.mSS.getState() == ServiceState.STATE_POWER_OFF /* CS POWER_OFF */
&& !VideoProfile.isVideo(dialArgs.videoState) /* voice call */
&& !isEmergency /* non-emergency call */
&& !(isUt && useImsForUt) /* not UT */) {
throw new CallStateException(
CallStateException.ERROR_POWER_OFF,
"cannot dial voice call in airplane mode");
}
// Check for service before placing non emergency CS voice call.
// Allow dial only if either CS is camped on any RAT (or) PS is in LTE service.
if (mSST != null
&& mSST.mSS.getState() == ServiceState.STATE_OUT_OF_SERVICE /* CS out of service */
&& !(mSST.mSS.getDataRegState() == ServiceState.STATE_IN_SERVICE
&& ServiceState.isLte(mSST.mSS.getRilDataRadioTechnology())) /* PS not in LTE */
&& !VideoProfile.isVideo(dialArgs.videoState) /* voice call */
&& !isEmergency /* non-emergency call */) {
throw new CallStateException(
CallStateException.ERROR_OUT_OF_SERVICE,
"cannot dial voice call in out of service");
}
if (DBG) logd("Trying (non-IMS) CS call");
if (isPhoneTypeGsm()) {
return dialInternal(dialString, new DialArgs.Builder<>()
.setIntentExtras(dialArgs.intentExtras)
.build());
} else {
return dialInternal(dialString, dialArgs);
}
}
進入dialInternal方法
@Override
protected Connection dialInternal(String dialString, DialArgs dialArgs)
throws CallStateException {
return dialInternal(dialString, dialArgs, null);
}
protected Connection dialInternal(String dialString, DialArgs dialArgs,
ResultReceiver wrappedCallback)
throws CallStateException {
// Need to make sure dialString gets parsed properly
String newDialString = PhoneNumberUtils.stripSeparators(dialString);
if (isPhoneTypeGsm()) {
// 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(), wrappedCallback);
if (DBG) logd("dialInternal: dialing w/ mmi '" + mmi + "'...");
if (mmi == null) { //return mCT.dial(newDialString, uusInfo, intentExtras);
return mCT.dial(newDialString, dialArgs.uusInfo, dialArgs.intentExtras); // 流程會進入這裏, mCT 是 GsmCdmaCallTracker
} else if (mmi.isTemporaryModeCLIR()) {
return mCT.dial(mmi.mDialingNumber, mmi.getCLIRMode(), dialArgs.uusInfo,
dialArgs.intentExtras);
} else { // MTK 爲了支持VT加入的代碼
mPendingMMIs.add(mmi);
mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));
mmi.processCode();
return null;
}
} else {
return mCT.dial(newDialString);
}
}
GsmCallTracker 調用CI 執行指令,返回結果,通知GSMPhone
GsmCallTracker 會藉助 mCi 發送指令,這裏面會將包含 mCi 執行結果的 Message 提前發送出去,等到 mCi 執行完畢後, Message 會被髮送到對應的 Handler 裏面去。
//GSM
/**
* clirMode is one of the CLIR_ constants 呼出入口
*/
public synchronized Connection dial(String dialString, int clirMode, UUSInfo uusInfo,
Bundle intentExtras)
throws CallStateException {
// note that this triggers call state changed notif
clearDisconnected();
if (!canDial()) {
throw new CallStateException("cannot dial in current state");
}
String origNumber = dialString;
dialString = convertNumberIfNecessary(mPhone, dialString);
// The new call must be assigned to the foreground call.
// That call must be idle, so place anything that's
// there on hold
if (mForegroundCall.getState() == GsmCdmaCall.State.ACTIVE) {
// this will probably be done by the radio anyway
// but the dial might fail before this happens
// and we need to make sure the foreground call is clear
// for the newly dialed connection
switchWaitingOrHoldingAndActive();
// This is a hack to delay DIAL so that it is sent out to RIL only after
// EVENT_SWITCH_RESULT is received. We've seen failures when adding a new call to
// multi-way conference calls due to DIAL being sent out before SWITCH is processed
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// do nothing
}
// Fake local state so that
// a) foregroundCall is empty for the newly dialed connection
// b) hasNonHangupStateChanged remains false in the
// next poll, so that we don't clear a failed dialing call
fakeHoldForegroundBeforeDial();
}
if (mForegroundCall.getState() != GsmCdmaCall.State.IDLE) {
//we should have failed in !canDial() above before we get here
throw new CallStateException("cannot dial in current state");
}
boolean isEmergencyCall = PhoneNumberUtils.isLocalEmergencyNumber(mPhone.getContext(),
dialString);
mPendingMO = new GsmCdmaConnection(mPhone, checkForTestEmergencyNumber(dialString),
this, mForegroundCall, isEmergencyCall);
mHangupPendingMO = false;
mMetrics.writeRilDial(mPhone.getPhoneId(), mPendingMO, clirMode, uusInfo);
if ( mPendingMO.getAddress() == null || mPendingMO.getAddress().length() == 0
|| mPendingMO.getAddress().indexOf(PhoneNumberUtils.WILD) >= 0) {
// Phone number is invalid 電話號碼無效
mPendingMO.mCause = DisconnectCause.INVALID_NUMBER;
// handlePollCalls() will notice this call not present
// and will mark it as dropped.
pollCallsWhenSafe();
} else {
// Always unmute when initiating a new call
setMute(false);// 撥號時設置不能靜音
boolean isPhoneInEcmMode = EcbmHandler.getInstance().isInEcm();
// In Ecm mode, if another emergency call is dialed, Ecm mode will not exit.
if(!isPhoneInEcmMode || (isPhoneInEcmMode && isEmergencyCall)) {
mCi.dial(mPendingMO.getAddress(), clirMode, uusInfo, obtainCompleteMessage());// 正常撥號// 需要注意, EVENT_DIAL_CALL_RESULT 是對應操作的一個句柄, 等到結果返回時, 會讓 Handler 進行處理
} else {
EcbmHandler emergencyHandler = EcbmHandler.getInstance();
try {
emergencyHandler.exitEmergencyCallbackMode();
} catch (Exception e) {
e.printStackTrace();
}
emergencyHandler.setOnEcbModeExitResponse(this,EVENT_EXIT_ECM_RESPONSE_CDMA, null);
mPendingCallClirMode = clirMode;
mPendingCallUusInfo = uusInfo;
mPendingCallInEcm = true;
}
}
if (mNumberConverted) {
mPendingMO.setConverted(origNumber);
mNumberConverted = false;
}
updatePhoneState();
mPhone.notifyPreciseCallStateChanged();
return mPendingMO;
}
GsmCallTracker 本身就是一個 Handler, 它的 handleMessage 將會處理 mCi 返回的結果。
public void handleMessage(Message msg) {
AsyncResult ar;
switch (msg.what) {
case EVENT_OPERATION_COMPLETE:
// 再次獲取最新信息
operationComplete();
break;
省略.......
}
}
mCi 獲取最新狀態, 返回到 handleMessage 的 EVENT_POLL_CALLS_RESULT
private void operationComplete() {
mPendingOperations--;
if (DBG_POLL) log("operationComplete: pendingOperations=" +
mPendingOperations + ", needsPoll=" + mNeedsPoll);
if (mPendingOperations == 0 && mNeedsPoll) {
mLastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT);//EVENT_POLL_CALLS_RESULT 在調用Handler
mCi.getCurrentCalls(mLastRelevantPoll);//獲取最新狀態
} else if (mPendingOperations < 0) {
// this should never happen
Rlog.e(LOG_TAG,"GsmCdmaCallTracker.pendingOperations < 0");
mPendingOperations = 0;
}
}
@Override
public void handleMessage(Message msg) {
AsyncResult ar;
switch (msg.what) {
case EVENT_POLL_CALLS_RESULT:
Rlog.d(LOG_TAG, "Event EVENT_POLL_CALLS_RESULT Received");
if (msg == mLastRelevantPoll) {
if (DBG_POLL) log(
"handle EVENT_POLL_CALL_RESULT: set needsPoll=F");
mNeedsPoll = false;
mLastRelevantPoll = null;
handlePollCalls((AsyncResult)msg.obj);//handlePollCalls 處理4種狀態後,通知 phone 更新狀態。
}
break;
}
}
handlePollCalls出來電話轉態太長 剪數據
@Override
protected synchronized void handlePollCalls(AsyncResult ar) {//handlePollCalls 處理4種狀態後,通知 phone 更新狀態。
List polledCalls;
.....
//CDMA
boolean noConnectionExists = true;
for (int i = 0, curDC = 0, dcSize = polledCalls.size()
; i < mConnections.length; i++) {
GsmCdmaConnection conn = mConnections[i];
DriverCall dc = null;
// polledCall list is sparse
if (curDC < dcSize) {
dc = (DriverCall) polledCalls.get(curDC);
if (dc.index == i+1) {
curDC++;
} else {
dc = null;
}
}
//CDMA
if (conn != null || dc != null) {// 新的電話
noConnectionExists = false;
}
if (DBG_POLL) log("poll: conn[i=" + i + "]=" +
conn+", dc=" + dc);
if (conn == null && dc != null) {
// Connection appeared in CLCC response that we don't know about
if (mPendingMO != null && mPendingMO.compareTo(dc)) {
......
if (mHangupPendingMO) {
......
}
} else {
.....
}
hasNonHangupStateChanged = true;
} else if (conn != null && dc == null) { // 舊的電話消失,可能時掛斷
if (isPhoneTypeGsm()) {
// Connection missing in CLCC response that we were
// tracking.
mDroppedDuringPoll.add(conn);
} else {
.....
}
// Dropped connections are removed from the CallTracker
// list but kept in the Call list
mConnections[i] = null;
} else if (conn != null && dc != null && !conn.compareTo(dc) && isPhoneTypeGsm()) {
// Connection in CLCC response does not match what
// we were tracking. Assume dropped call and new call
mDroppedDuringPoll.add(conn);
mConnections[i] = new GsmCdmaConnection (mPhone, dc, this, i);
if (mConnections[i].getCall() == mRingingCall) {
newRinging = mConnections[i];
} // else something strange happened
hasNonHangupStateChanged = true;
} else if (conn != null && dc != null) { /* implicit conn.compareTo(dc) */ // 舊的電話更新, 正在打的電話時,狀態更新
......
}
if (REPEAT_POLLING) {
......
}
}
if (!isPhoneTypeGsm() && noConnectionExists) {
checkAndEnableDataCallAfterEmergencyCallDropped();
}
if (mPendingMO != null) {
Rlog.d(LOG_TAG, "Pending MO dropped before poll fg state:"
+ mForegroundCall.getState());
mDroppedDuringPoll.add(mPendingMO);
mPendingMO = null;
mHangupPendingMO = false;
if (!isPhoneTypeGsm()) {
if( mPendingCallInEcm) {
mPendingCallInEcm = false;
}
checkAndEnableDataCallAfterEmergencyCallDropped();
}
}
if (newRinging != null) {
mPhone.notifyNewRingingConnection(newRinging);
}
// clear the "local hangup" and "missed/rejected call"
// cases from the "dropped during poll" list
// These cases need no "last call fail" reason
ArrayList<GsmCdmaConnection> locallyDisconnectedConnections = new ArrayList<>();
for (int i = mDroppedDuringPoll.size() - 1; i >= 0 ; i--) {
GsmCdmaConnection conn = mDroppedDuringPoll.get(i);
//CDMA
boolean wasDisconnected = false;
if (conn.isIncoming() && conn.getConnectTime() == 0) {
// Missed or rejected call
int cause;
if (conn.mCause == DisconnectCause.LOCAL) {
cause = DisconnectCause.INCOMING_REJECTED;
} else {
cause = DisconnectCause.INCOMING_MISSED;
}
if (Phone.DEBUG_PHONE) {
log("missed/rejected call, conn.cause=" + conn.mCause);
log("setting cause to " + cause);
}
mDroppedDuringPoll.remove(i);
hasAnyCallDisconnected |= conn.onDisconnect(cause);
wasDisconnected = true;
locallyDisconnectedConnections.add(conn);
} else if (conn.mCause == DisconnectCause.LOCAL
|| conn.mCause == DisconnectCause.INVALID_NUMBER) {
mDroppedDuringPoll.remove(i);
hasAnyCallDisconnected |= conn.onDisconnect(conn.mCause);
wasDisconnected = true;
locallyDisconnectedConnections.add(conn);
}
if (!isPhoneTypeGsm() && wasDisconnected && unknownConnectionAppeared
&& conn == newUnknownConnectionCdma) {
unknownConnectionAppeared = false;
newUnknownConnectionCdma = null;
}
}
if (locallyDisconnectedConnections.size() > 0) {
mMetrics.writeRilCallList(mPhone.getPhoneId(), locallyDisconnectedConnections);
}
/* Disconnect any pending Handover connections */
for (Iterator<Connection> it = mHandoverConnections.iterator();
it.hasNext();) {
.....
}
// Any non-local disconnects: determine cause
if (mDroppedDuringPoll.size() > 0) {
mCi.getLastCallFailCause(
obtainNoPollCompleteMessage(EVENT_GET_LAST_CALL_FAIL_CAUSE));
}
if (needsPollDelay) {// 過一會在獲取最新狀態
pollCallsAfterDelay();
}
.....
if (VDBG) log("handlePollCalls calling updatePhoneState()");
updatePhoneState(); // 更新 phone 的狀態
if (unknownConnectionAppeared) {
......
}
if (hasNonHangupStateChanged || newRinging != null || hasAnyCallDisconnected) {
mPhone.notifyPreciseCallStateChanged(); // 通知 GSMPhone 狀態更新
updateMetrics(mConnections);
}
}
更新Phone狀態
private void updatePhoneState() {
PhoneConstants.State oldState = mState;
if (mRingingCall.isRinging()) {
mState = PhoneConstants.State.RINGING;
} else if (mPendingMO != null ||
!(mForegroundCall.isIdle() && mBackgroundCall.isIdle())) {
mState = PhoneConstants.State.OFFHOOK;
} else {
Phone imsPhone = mPhone.getImsPhone();
if ( mState == PhoneConstants.State.OFFHOOK && (imsPhone != null)){
imsPhone.callEndCleanupHandOverCallIfAny();
}
mState = PhoneConstants.State.IDLE;
}
if (mState == PhoneConstants.State.IDLE && oldState != mState) {
mVoiceCallEndedRegistrants.notifyRegistrants(
new AsyncResult(null, null, null));
} else if (oldState == PhoneConstants.State.IDLE && oldState != mState) {
mVoiceCallStartedRegistrants.notifyRegistrants (
new AsyncResult(null, null, null));
}
if (Phone.DEBUG_PHONE) {
log("update phone state, old=" + oldState + " new="+ mState);
}
if (mState != oldState) {
mPhone.notifyPhoneStateChanged();// 更新 phone 的狀態
mMetrics.writePhoneState(mPhone.getPhoneId(), mState);
}
}
mPhone.notifyPhoneStateChanged => mNotifier.notifyPhoneState , mNotifier 是一個 DefaultPhoneNotifier , notifyPhoneState 會通知 telephony.registry 更新,telephony.registry 會通知所有調用了TelephonyManager.listen 的監聽者, 實際上,這是提供給第三方APP監聽Phone狀態的一個接口,當前沒用!!。
DefaultPhoneNotifier.java
@Override
public void notifyPhoneState(Phone sender) {
Call ringingCall = sender.getRingingCall();
int subId = sender.getSubId();
int phoneId = sender.getPhoneId();
String incomingNumber = "";
if (ringingCall != null && ringingCall.getEarliestConnection() != null) {
incomingNumber = ringingCall.getEarliestConnection().getAddress();
}
try {
if (mRegistry != null) {
// mRegistry 是一個遠程接口 (TelephonyRegistry)
// protected DefaultPhoneNotifier() {
//mRegistry
=ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
// "telephony.registry"));
mRegistry.notifyCallStateForPhoneId(phoneId, subId,
PhoneConstantConversions.convertCallState(
sender.getState()), incomingNumber);
}
} catch (RemoteException ex) {
// system process is dead
}
}
mPhone.notifyPreciseCallStateChanged() ==> PhoneBase.notifyPreciseCallStateChangedP() 這裏會通知 TelephonyConnection 更新。 觸發 TelephonyConnection 的 updateState()
轉態更新後返回Message 回調mCi.dial(mPendingMO.getAddress(), clirMode, uusInfo, obtainCompleteMessage()),接下來流程來到了 mCi.dial, mCi 是一個 CommandsInterface 接口, 由 GsmCallTracker 的構造可知, GsmCallTracker 的 mCi 來自於 GSMPhone 的 mCi, 即 RIL。
RIL.java
進入RIL.java
@Override
public void dial(String address, int clirMode, UUSInfo uusInfo, Message result) {
IRadio radioProxy = getRadioProxy(result);
Log.v("liwangjiangDialerRil","Ril address = "+address);
if (radioProxy != null) {
RILRequest rr = obtainRequest(RIL_REQUEST_DIAL, result,
mRILDefaultWorkSource);
Dial dialInfo = new Dial();
dialInfo.address = convertNullToEmptyString(address);
dialInfo.clir = clirMode;
if (uusInfo != null) {
UusInfo info = new UusInfo();
info.uusType = uusInfo.getType();
info.uusDcs = uusInfo.getDcs();
info.uusData = new String(uusInfo.getUserData());
dialInfo.uusInfo.add(info);
}
if (RILJ_LOGD) {
// Do not log function arg for privacy
riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
}
try {
radioProxy.dial(rr.mSerial, dialInfo);
} catch (RemoteException | RuntimeException e) {
handleRadioProxyExceptionForRR(rr, "dial", e);
}
}
}
RIL與通話模塊底層通訊過程
RIL通訊主要由RILSender和RILReceiver構成,用於通訊傳輸的載體的有 RILRequest,
Registrant(RegistrantList)。
- RILSender
是一個Handler, 通過 #send(RILRequest) 將請求發送到 mSenderThread線程中,handleMessage 將請求寫入mSocket中。 - RILReceiver
在run內無限輪詢,一旦讀取到通話底層返回的數據,交給 #processResponse(Parcel)處理。
其中應答分爲有請求的應答,無請求的應答(即狀態改變後的反饋)
1.RIL_REQUEST_xxxx 有請求
2.RIL_UNSOL_xxxx 無請求 - RILRequest
成員變量:
1.mSerial 請求序號,唯一,保證請求和反饋的一致。
2.mRequest 請求類型 即 RIL_xxxx。
3.mResult 用來發送請求的結果的句柄,由RIL請求的調用者提供。 - Registrant
在 RIL內部維護着一系列的RegistrantList(Registrant的集合),每個集合代表一種狀態改變的類型,對於有請求的應答,RIL是通過對應的mResult來發送結果的,但是對於無請求的應答,RIL是將反饋通知告訴對應的RegistrantList的(#notifyRegistrants),RegistrantList會通知每一個Registrant。在RIL的直接父類定義了這些RegistrantList,並提供了註冊到RegistrantList的方法(eg.#registerForCallStateChanged,#unregisterForCallStateChanged)所以,如果想要監聽通話狀態的變化,那麼就需要註冊監聽到對應的RegistrantList(監聽着需要提供一個handler和一個int和object,handler用來發送數據,int區分監聽的事件,object是額外的信息)。
最後到Log分析了
Log需要打開才能看,當前沒有編過,敬請期待!!
如果寫的可以 給個 贊 吧謝謝!!
?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????