今天讲解一下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处理的单个电话呼叫。
GsmCmdaConnection.java 7.0升级后管理GSM,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() {@Overridepublic 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

    @Overridepublic 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() {@Overridepublic 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();...........// 构建CallsManagermCallsManager = 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() {@Overridepublic 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

        @Overridepublic 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); // //开启广播 最后进入到 PrimaryCallReceiverif (isLocalInvocation) {synchronized (TelecomSystem.getInstance().getLock()) {TelecomSystem.getInstance().getCallIntentProcessor().processIntent(intent);}} else {mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM); //发送广播}return true;}

接下来进入关播PrimaryCallReceiver中  ,PrimaryCallReceiver作为拨号的单一入口,其收到消息后就转给TelecomSystem中的CallIntentProcessor进行处理

PrimartCallReceiver.java

@Overridepublic 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 returnsCall 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

@VisibleForTestingpublic 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 dockedcall.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 calldisconnectSelfManagedCalls("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

    @VisibleForTestingpublic 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() {@Overridepublic 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

创建链接就是一个绑定服务的过程

/*** 为新的传出呼叫或附加到现有的传入呼叫创建新连接。*/@VisibleForTestingpublic void createConnection(final Call call, final CreateConnectionResponse response) {//创建链接就是一个绑定服务的过程Log.d(this, "createConnection(%s) via %s.", call, getComponentName());BindCallback callback = new BindCallback() {// 链接创建成功后 onSuccess 会被调用@Overridepublic 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()));}}@Overridepublic 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/teleponymUserHandle);} 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;}@Overridepublic 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 的 onSuccesshandleSuccessfulConnection();  //回调进入子类//整个绑定过程,只做2件事,一是给远程服务提供访问自己的接口,二是利用远程接口创建一个通话链接。// 这2件事都是跨进程进行的。远程服务访问自己的接口是} catch (RemoteException e) {Log.w(this, "onServiceConnected: %s died.");if (mServiceDeathRecipient != null) {mServiceDeathRecipient.binderDied();}}}}} finally {Log.endSession();}}@Overridepublic 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()) {@Overridepublic 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*/) {@Overridepublic 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 的 handleCreateConnectionSuccessmAdapter.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

 @Overridepublic 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 codeif (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 如下

 @Overridepublic 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_OFFif (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方法

  @Overrideprotected 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 properlyString newDialString = PhoneNumberUtils.stripSeparators(dialString);if (isPhoneTypeGsm()) {// handle in-call MMI first if applicableif (handleInCallMmiCommands(newDialString)) {return null;}// Only look at the Network portion for mmiString 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 notifclearDisconnected();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 holdif (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 connectionswitchWaitingOrHoldingAndActive();// 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 processedtry {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 callfakeHoldForegroundBeforeDial();}if (mForegroundCall.getState() != GsmCdmaCall.State.IDLE) {//we should have failed in !canDial() above before we get herethrow 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 callsetMute(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 在调用HandlermCi.getCurrentCalls(mLastRelevantPoll);//获取最新状态} else if (mPendingOperations < 0) {// this should never happenRlog.e(LOG_TAG,"GsmCdmaCallTracker.pendingOperations < 0");mPendingOperations = 0;}}
   @Overridepublic 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出来电话转态太长 剪数据

   @Overrideprotected synchronized void handlePollCalls(AsyncResult ar) {//handlePollCalls 处理4种状态后,通知 phone 更新状态。List polledCalls;.....//CDMAboolean 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 sparseif (curDC < dcSize) {dc = (DriverCall) polledCalls.get(curDC);if (dc.index == i+1) {curDC++;} else {dc = null;}}//CDMAif (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 aboutif (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 listmConnections[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 callmDroppedDuringPoll.add(conn);mConnections[i] = new GsmCdmaConnection (mPhone, dc, this, i);if (mConnections[i].getCall() == mRingingCall) {newRinging = mConnections[i];} // else something strange happenedhasNonHangupStateChanged = 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" reasonArrayList<GsmCdmaConnection> locallyDisconnectedConnections = new ArrayList<>();for (int i = mDroppedDuringPoll.size() - 1; i >= 0 ; i--) {GsmCdmaConnection conn = mDroppedDuringPoll.get(i);//CDMAboolean wasDisconnected = false;if (conn.isIncoming() && conn.getConnectTime() == 0) {// Missed or rejected callint 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 causeif (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

 @Overridepublic 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

@Overridepublic 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 privacyriljLog(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)。

  1. RILSender
    是一个Handler, 通过 #send(RILRequest) 将请求发送到 mSenderThread线程中,handleMessage 将请求写入mSocket中。
  2. RILReceiver
    在run内无限轮询,一旦读取到通话底层返回的数据,交给 #processResponse(Parcel)处理。
    其中应答分为有请求的应答,无请求的应答(即状态改变后的反馈)
    1.RIL_REQUEST_xxxx 有请求
    2.RIL_UNSOL_xxxx 无请求
  3. RILRequest
    成员变量:
    1.mSerial 请求序号,唯一,保证请求和反馈的一致。
    2.mRequest 请求类型 即 RIL_xxxx。
    3.mResult 用来发送请求的结果的句柄,由RIL请求的调用者提供。
  4. 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需要打开才能看,当前没有编过,敬请期待!!

如果写的可以  给个 赞 吧谢谢!!

?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????

Phone拨号流程(Android9.0 Phone拨号)相关推荐

  1. 中兴wcdma模块 linux拨号流程,Linux下ppp拨号+3G模块(evdo 中兴MC8630模块)

    物联网的发展,嵌入式节点具备3G无线上网功能是个必不可少的功能,奈何网上的资料多数以WCDMA为主,电信的evdo资料不是很多,笔者经过一番实验,搜寻了一些网站,终于在开发板上,将EVDO模块(中兴M ...

  2. android9.0紧急号码拨号流程

    https://blog.csdn.net/liuchao329282455/article/details/82928086#%E7%B4%A7%E6%80%A5%E7%94%B5%E8%AF%9D ...

  3. ec200t 拨号_移远EC20 R2.0 AT指令拨号流程

    使用AT指令进行拨号,可快速的集成EC20 4G模块,只需要给EC20供电和接上通信UART串口,就能实现网络连接. EC20管脚分配 最小系统电路图 TCP/IP AT命令拨号流程 AT指令拨号流程 ...

  4. Android 7.0 APN 拨号上网流程分析

    1.前言 在前段时间的项目中遇到客户的设备出现APN断开的情况没有自动连接,后来折腾了一段时间解决了这个问题.现在用这篇博客记录一下APN的选择和连接流程. 2.名词解析 APN:APN指一种网络接入 ...

  5. Telephony 拨号流程

    一.进入Dialer App 使用adb logcat -vtime -b events命令查看手机的运行日志,最后操作手机,点击dialer进入拨号界面. 05-26 19:20:41.633 I/ ...

  6. Android7.0 数据拨号前的准备工作

    背景  在介绍PhoneApp的创建过程时,我们知道为了支持双卡手机,PhoneFactory创建了两个Phone对象.  然而由于通信制式.功耗等的限制,目前底层的芯片厂商规定modem工作于DSD ...

  7. Android7.0 数据拨号前的准备工作

    背景 在介绍PhoneApp的创建过程时,我们知道为了支持双卡手机,PhoneFactory创建了两个Phone对象. 然而由于通信制式.功耗等的限制,目前底层的芯片厂商规定modem工作于DSDS模 ...

  8. 移远AT指令拨号流程

    使用AT指令进行拨号,可快速的集成EC20 4G模块,只需要给EC20供电和接上通信UART串口,就能实现网络连接. EC20管脚分配 最小系统电路图 TCP/IP AT命令拨号流程 AT指令拨号流程 ...

  9. 安卓10拨号流程梳理

    电话应用框架 Android电话模块是一个典型的分层结构设计,如下: 电话框架分为4个层次,分别为:应用层.框架层(framework层,简称fw).RIL(Radio Interface Layer ...

最新文章

  1. C#实现动态生成Word
  2. php是做前端还是后端,在后端准备数据还是在前端操作? - php
  3. CentOS 安装go client调用Kubernetes API
  4. 影响信息业发展的飞鸽传书2007关键
  5. 微软版UnityVs横空出世,究竟是谁成就了谁?
  6. 深入大数据安全分析(1):为什么需要大数据安全分析?
  7. 什么是OpenStack
  8. sqlite数据库文件的下载编译和VS中数据库创建demo
  9. 项目中常用的各国语言字典表分享
  10. 如何轻松集成VARCHART XGantt
  11. 求助!KeyError:None of [Index(['2017-01-01, ...\n dtype='object', length=365)] are in the [columns]
  12. 基于MK802 MiniPC的扩展开发应用-系统自制
  13. ardruino控制继电器_用 Arduino 实现带继电器的拨动开关
  14. 淘金网UCskype即时通讯软件定制
  15. Photoshop创意设计手法14点(转)
  16. 2012腾讯实习招聘笔试附加题1求解方法
  17. dos格式和linux文本文件,怎么判断是Windows-dos文本文件还是Linux-Unix文本文件?
  18. android免root读写u盘最新方法,支持安卓Q+
  19. 简单又详细的网页爬虫案例
  20. 谷歌地图下载器中“地图艺术照”

热门文章

  1. 软件工程_东师站_课堂笔记
  2. 魔兽世界怀旧服务器最新,魔兽世界怀旧服9月13日免费转服-11个转服新服务器一览...
  3. 华为发布台式电脑,可能给PC老大联想带来压力
  4. 软件工程就业前景怎么样?大家对这个职业是否有什么误区?
  5. 转载:latex中.bb not found问题
  6. AndroidIOS 实现接触NFC自动跳转到App,如果未安装App,则跳转到应用市场
  7. 虚拟局域网软件开源_老牌沙盒虚拟软件免费开源,可以不用虚拟机了
  8. Vue 预览word,excel,ppt等office文档-内网访问(基于onlyoffice,后端返回文件流)
  9. 技术一旦被用来作恶,究竟会有多可怕?
  10. 为什么需要运营商级NAT设备?