1.1 Dialer拨号
拨打电话需要开启Dialer(拨号盘),当用户触发onClick事件,DialtactsActivity的onClick()方法被触发:
public void onClick(View view) { switch (view.getId()) {//对触发的buttonID进行判断 case R.id.floating_action_button://此处可认为是点击“拨号”按钮 if (mListsFragment.getCurrentTabIndex()//通过电话簿拨号 == ListsFragment.TAB_INDEX_ALL_CONTACTS && !mInRegularSearch) { DialerUtils.startActivityWithErrorToast( this, IntentUtil.getNewContactIntent(), R.string.add_contact_not_available); }else if (!mIsDialpadShown) {//拨号界面未显示,显示拨号界面 mInCallDialpadUp = false; showDialpadFragment(true);//这里实际上是交给了DialPadFrament去处理 } break; case R.id.voice_search_button://声控搜索 try { startActivityForResult(new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH), ACTIVITY_REQUEST_CODE_VOICE_SEARCH); } catch (ActivityNotFoundException e) { Toast.makeText(DialtactsActivity.this, R.string.voice_search_not_available, Toast.LENGTH_SHORT).show(); } break; case R.id.dialtacts_options_menu_button://设置按钮 mOverflowMenu.show(); break; default: { Log.wtf(TAG, "Unexpected onClick event from " + view); break; } } } |
注意到上面的程序中并没有对搜索按钮进行处理,因为压根就没有也不需要这个按钮,Android在输入内容的同时自动搜索。
进入到DialPadFragment,注意到Android6.0中其实有两个DialPadFragment.java文件,一个当然是拨号时显示,另一个是来电时显示的。找到拨号的那一个DialPadFragment.java,此类主要负责与键盘拨号相关的动作的处理、输入校验和输入结果提交等逻辑。数字按键事件捕获:
@Override public boolean onKey(View view, int keyCode, KeyEvent event) { switch (view.getId()) { case R.id.digits: if (keyCode == KeyEvent.KEYCODE_ENTER) {//如果是KEYCODE_ENTER,开始拨打 handleDialButtonPressed(); return true;//自己处理了 } break; } return false;//返回false由系统进行处理 } |
本人对Android的应用开发不太了解,百度得知onKey()如果返回 false会交由系统处理,本人猜测处理函数就是本类的OnPressed()。不知是否正确,希望指正。
/** * When a key is pressed, we start playing DTMF tone, do vibration, and enter the digit * immediately. When a key is released, we stop the tone. Note that the "key press" event will * be delivered by the system with certain amount of delay, it won't be synced with user's * actual "touch-down" behavior. */ @Override public void onPressed(View view, boolean pressed) { if (DEBUG) Log.d(TAG, "onPressed(). view: " + view + ", pressed: " + pressed); if (pressed) { switch (view.getId()) { case R.id.one: { keyPressed(KeyEvent.KEYCODE_1);//这里根据设置可能会有tone音,并触发//EditText的onKeyDown方法,下类似 break; } case R.id.two: { keyPressed(KeyEvent.KEYCODE_2); break; } case R.id.three: { keyPressed(KeyEvent.KEYCODE_3); break; } case R.id.four: { keyPressed(KeyEvent.KEYCODE_4); break; } case R.id.five: { keyPressed(KeyEvent.KEYCODE_5); break; } case R.id.six: { keyPressed(KeyEvent.KEYCODE_6); break; } case R.id.seven: { keyPressed(KeyEvent.KEYCODE_7); break; } case R.id.eight: { keyPressed(KeyEvent.KEYCODE_8); break; } case R.id.nine: { keyPressed(KeyEvent.KEYCODE_9); break; } case R.id.zero: { keyPressed(KeyEvent.KEYCODE_0); break; } case R.id.pound: { keyPressed(KeyEvent.KEYCODE_POUND); break; } case R.id.star: { keyPressed(KeyEvent.KEYCODE_STAR); break; } default: { Log.wtf(TAG, "Unexpected onTouch(ACTION_DOWN) event from: " + view); break; } } mPressedDialpadKeys.add(view); } else { mPressedDialpadKeys.remove(view); if (mPressedDialpadKeys.isEmpty()) { stopTone(); } } } |
此时假设号码已经输入完毕,DialPadFragment类内部的onKey()和onClick()方法都将触发handleDialButtonPressed(),以onClick()为例;
public void onClick(View view) { switch (view.getId()) { case R.id.dialpad_floating_action_button://拨打按钮 mHaptic.vibrate(); handleDialButtonPressed();//负责处理button pressed消息 break; case R.id.deleteButton: {//del按钮 keyPressed(KeyEvent.KEYCODE_DEL); break; } case R.id.digits: {//数字显示框,聚焦 if (!isDigitsEmpty()) { mDigits.setCursorVisible(true); } break; } case R.id.dialpad_overflow: {//滑动拨号盘? mOverflowPopupMenu.show(); break; } default: { Log.wtf(TAG, "Unexpected onClick() event from: " + view); return; } } } |
值得一说的是,DialPadFragment还处理了一类DTMF(双音多频)按键事件,这种信令方式是在拨打一些特殊的服务台电话时候使用的,比如拨打10086,接着按0进入人工服务。
接着之前的Onclick(),进入handleDialButtonPressed()
private void handleDialButtonPressed() { if (isDigitsEmpty()) { //如果没有数字输入 handleDialButtonClickWithEmptyDigits(); } else { final String number = mDigits.getText().toString();//获取输入的电话号码 if (number != null && !TextUtils.isEmpty(mProhibitedPhoneNumberRegexp) && number.matches(mProhibitedPhoneNumberRegexp)) {//对number过滤 Log.i(TAG, "The phone number is prohibited explicitly by a rule."); if (getActivity() != null) { DialogFragment dialogFragment = ErrorDialogFragment.newInstance( R.string.dialog_phone_call_prohibited_message); dialogFragment.show(getFragmentManager(), "phone_prohibited_dialog"); } clearDialpad();//清除数字 } else {//如果number通过了检查 //取得一个Callintent final Intent intent = CallUtil.getCallIntent(number, (getActivity() instanceof DialtactsActivity ? ((DialtactsActivity) getActivity()).getCallOrigin() : null)); //交给DialerUtils去处理 DialerUtils.startActivityWithErrorToast(getActivity(), intent); hideAndClearDialpad(false); } } } |
进入DialerUtils.startActivityWithErrorToast()法,此方法有多个重载方法,满参数实现方法如下:
public static void startActivityWithErrorToast(Context context, Intent intent, int msgId) { try {//判断Intent是否是ACTION_CALL,这里当然是 if (Intent.ACTION_CALL.equals(intent.getAction())) { // All dialer-initiated calls should pass the touch point to the InCallUI Point touchPoint = TouchPointManager.getInstance().getPoint(); if (touchPoint.x != 0 || touchPoint.y != 0) { Bundle extras = new Bundle(); extras.putParcelable(TouchPointManager.TOUCH_POINT, touchPoint); intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, extras); } final TelecomManager tm = (TelecomManager)//获取TelecomManager服务 context.getSystemService(Context.TELECOM_SERVICE); //开启Activity,此处和5.0 tm.placeCall(intent.getData(), intent.getExtras());//不一样 } else { context.startActivity(intent); } } catch (ActivityNotFoundException e) { Toast.makeText(context, msgId, Toast.LENGTH_SHORT).show(); } } |
此时的Context其实是一个CallActivity。此处比5.0更直接的进入了TelecomService(当然是通过Binder)。进入TelecomManager.placeCall()
public void placeCall(Uri address, Bundle extras) { ITelecomService service = getTelecomService();//获取TelecomService服务 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());//交给Service去处理,进入Server端 } catch (RemoteException e) { Log.e(TAG, "Error calling ITelecomService#placeCall", e); } } } |
1.2 TelecomService
TelecommService的placeCall()方法在TelecommServiceImpl中找到。代码如下:
@Override public void placeCall(Uri handle, Bundle extras, String callingPackage) { enforceCallingPackage(callingPackage); 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;//定是否可以拨打非紧急电话
synchronized (mLock) { final UserHandle userHandle = Binder.getCallingUserHandle(); long token = Binder.clearCallingIdentity(); try { final Intent intent = new Intent(Intent.ACTION_CALL, handle); intent.putExtras(extras); new UserCallIntentProcessor(mContext,//交给UserCallIntentProcessor userHandle).processIntent(intent, callingPackage, hasCallAppOp && hasCallPermission); } finally { Binder.restoreCallingIdentity(token); } } } |
进入UserCallIntentProcessor的processIntent();
public void processIntent(Intent intent, String callingPackageName, boolean canCallNonEmergency) { if (!isVoiceCapable()) { return; } String action = intent.getAction();//得到intent的Action if (Intent.ACTION_CALL.equals(action) || Intent.ACTION_CALL_PRIVILEGED.equals(action) || Intent.ACTION_CALL_EMERGENCY.equals(action)) {//如果是这三种类型的Call processOutgoingCallIntent(intent, callingPackageName,//程序进入这里canCallNonEmergency); } } |
进processOutgoingCallIntent():
private void processOutgoingCallIntent(Intent intent, String callingPackageName, boolean canCallNonEmergency) { Uri handle = intent.getData(); String scheme = handle.getScheme(); String uriString = handle.getSchemeSpecificPart();
if (!PhoneAccount.SCHEME_VOICEMAIL.equals(scheme)) { handle = Uri.fromParts(PhoneNumberUtils.isUriNumber(uriString) ? PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL, uriString, null); }
final UserManager userManager =//再次获取服务,此时是userManager (UserManager) mContext.getSystemService(Context.USER_SERVICE); if (userManager.hasUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS, mUserHandle) && !TelephonyUtil.shouldProcessAsEmergency(mContext, handle)) { //被应用程序级别的权限拦截处理 showErrorDialogForRestrictedOutgoingCall(mContext, R.string.outgoing_call_not_allowed_user_restriction); Log.w(this, "Rejecting non-emergency phone call due to DISALLOW_OUTGOING_CALLS " + "restriction"); return; }
if (!canCallNonEmergency && !TelephonyUtil.shouldProcessAsEmergency(mContext, handle)) {//被系统级别的权限拦截处理 showErrorDialogForRestrictedOutgoingCall(mContext, R.string.outgoing_call_not_allowed_no_permission); Log.w(this, "Rejecting non-emergency phone call because " + android.Manifest.permission.CALL_PHONE + " permission is not granted."); return; } //权限检查OK int videoState = intent.getIntExtra( TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, VideoProfile.STATE_AUDIO_ONLY); Log.d(this, "processOutgoingCallIntent videoState = " + videoState);
if (VideoProfile.isVideo(videoState)//是视频?且需紧急处理 && TelephonyUtil.shouldProcessAsEmergency(mContext, handle)) { Log.d(this, "Emergency call...Converting video call to voice..."); videoState = VideoProfile.STATE_AUDIO_ONLY; intent.putExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, videoState); }
if (VideoProfile.isVideo(videoState) && isTtyModeEnabled()) {//是视频且TTY Toast.makeText(mContext, mContext.getResources().getString(R.string. video_call_not_allowed_if_tty_enabled), Toast.LENGTH_SHORT).show(); Log.d(this, "Rejecting video calls as tty is enabled"); return; } //对于非残疾人(非TTY),在普通情况下的处理 intent.putExtra(CallIntentProcessor.KEY_IS_PRIVILEGED_DIALER, isDefaultOrSystemDialer(callingPackageName)); sendBroadcastToReceiver(intent);//广播拨号Intent } |
在初级Receiver:PrimaryCallReceiver的onReceive()中被接收。
public void onReceive(Context context, Intent intent) { synchronized (getTelecomSystem().getLock()) { getTelecomSystem().getCallIntentProcessor().processIntent(intent); } } |
接着CallIntentProcessor. processIntent(intent)
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 {//通过Unknown检测 processOutgoingCallIntent(mContext, mCallsManager, intent);//进入这里 } Trace.endSection(); } |
接着processOutgoingCallIntent()
static void processOutgoingCallIntent( Context context, CallsManager callsManager, Intent intent) { if (shouldPreventDuplicateVideoCall(context, callsManager, intent)) { return; }
Uri handle = intent.getData(); String scheme = handle.getScheme(); String uriString = handle.getSchemeSpecificPart();
if (!PhoneAccount.SCHEME_VOICEMAIL.equals(scheme)) { handle = Uri.fromParts(PhoneNumberUtils.isUriNumber(uriString) ? PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL, 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(); }
final boolean isPrivilegedDialer = intent.getBooleanExtra(KEY_IS_PRIVILEGED_DIALER, false);
//这里新建了一个Call,这是个很重要的入口,开启了InCallUI Call call = callsManager.startOutgoingCall(handle, phoneAccountHandle, clientExtras);//下一章在这里扩展!! if (call != null) {//获得的Call不为空 //新建一个NewOutgoingCallIntentBroadcaster的实例,并调用其processIntent() NewOutgoingCallIntentBroadcaster broadcaster = newNewOutgoingCallIntentBroadcaster( context, callsManager, call, intent, isPrivilegedDialer); final int result = broadcaster.processIntent(); final boolean success = result == DisconnectCause.NOT_DISCONNECTED;
if (!success && call != null) { disconnectCallAndShowErrorDialog(context, call, result); } } } |
进入broadcaster.processIntent(),此方法将处理3类电话权限,分别为第三方Call,系统Call,和紧急Call。这三类Call的区别:首先可以想到,拨打Emergency call是不需要卡的(当然,现在基本上的手机都已经不支持无卡紧急拨号了,主要原因在运营商)。应该走的是一种特殊的链路,或者更直白的将应该是一条更快捷的链路。Emergency call 可以被两种Intent所触发:
Intent.ACTION_CALL_EMERGENCY
Intent.ACTION_CALL_PRIVILEGED
CALL_PRIVILEGED是被系统级的应用所发起的,而CALL_EMERGENCY Intent 是从紧急呼叫的拨号盘里发起的,就是手机在没有SIM卡的模式下显示的拨号盘。CALL_PRIVILEGED这个intent 接到后会检查号码是否是emergencynumber, 如果是紧急电话的号码就会把intent的action 替换为ACTION_CALL_EMERGENCY, 如果不是就替换成ACTION_CALL。由此可看,虽然我们有三种call的intent action, 但是我们实际有效果的就是上述两种
CALL_PRIVILEGED这种action的call的作用的是能把紧急电话的号码带给phone app去处理,而普通的call action的intent是不可以的。
processIntent实现里用一个flag变量callImmediately来控制是否拨打电话,callImmediately在满足拨打emengency call的时候被置为true。
3rd App是没有权限拨打Emergency call的,如果3rd App对应的Intent.ACTION_CALL里带有Emergency number,会被阻止。
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)) { // Voicemail calls will be handled directly by the telephony connection manager Log.i(this, "Placing call immediately instead of waiting for " + " OutgoingCallBroadcastReceiver: %s", intent);
boolean speakerphoneOn = mIntent.getBooleanExtra( TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, false); mCallsManager.placeOutgoingCall(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; } }
String number = PhoneNumberUtils.getNumberFromIntent(intent, mContext); if (TextUtils.isEmpty(number)) { Log.w(this, "Empty number obtained from the call intent."); return DisconnectCause.NO_PHONE_NUMBER_SUPPLIED; }
boolean isUriNumber = PhoneNumberUtils.isUriNumber(number); if (!isUriNumber) { number = PhoneNumberUtils.convertKeypadLettersToDigits(number); number = PhoneNumberUtils.stripSeparators(number); } //只有CALL_PRIVILEGED和CALL_EMERGENCY才有可能成为紧急号码 final boolean isPotentialEmergencyNumber = isPotentialEmergencyNumber(number); Log.v(this, "isPotentialEmergencyNumber = %s", isPotentialEmergencyNumber); //根据是否为紧急号码,重写intent以便后续做判断 //此后,只存在两种类型的电话了,一个是普通电话,另一个为紧急电话ALL_PRIVILEGED //被转化成了这两种电话的一种(根据number) rewriteCallIntentAction(intent, isPotentialEmergencyNumber); action = intent.getAction(); // True for certain types of numbers that are not intended to be intercepted or // modified by third parties (e.g. emergency nmbers). boolean callImmediately = false; //通过第三方来源检查,且为紧急号后设置为true,需//立即处理 if (Intent.ACTION_CALL.equals(action)) {//对Action_Call进行处理 if (isPotentialEmergencyNumber) { if (!mIsDefaultOrSystemPhoneApp) {//普通拨号也可能是紧急电话,但是如果是紧//急电话,第三方软件无权拨打 Log.w(this, "Cannot call potential emergency number %s with CALL Intent %s " + "unless caller is system or default dialer.", number, intent); launchSystemDialer(intent.getData());//弹出系统拨号盘供拨打紧急电话 return DisconnectCause.OUTGOING_CANCELED; } else { callImmediately = true; //如果是紧急电话设置紧急电话标志 } } } else if (Intent.ACTION_CALL_EMERGENCY.equals(action)) {//对紧急拨号的处理 if (!isPotentialEmergencyNumber) {//紧急拨号盘拨打的并不是紧急电话 Log.w(this, "Cannot call non-potential-emergency number %s with EMERGENCY_CALL " + "Intent %s.", number, intent); return DisconnectCause.OUTGOING_CANCELED; } callImmediately = true; //设置紧急电话标志 } else { Log.w(this, "Unhandled Intent %s. Ignoring and not placing call.", intent); return DisconnectCause.INVALID_NUMBER; }
if (callImmediately) {//开始处理紧急号码 Log.i(this, "Placing call immediately instead of waiting for " + " OutgoingCallBroadcastReceiver: %s", intent); String scheme = isUriNumber ? PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL; boolean speakerphoneOn = mIntent.getBooleanExtra( TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, false); int videoState = mIntent.getIntExtra( TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, VideoProfile.STATE_AUDIO_ONLY); mCallsManager.placeOutgoingCall(mCall, Uri.fromParts(scheme, number, null), null, speakerphoneOn, videoState);//紧急电话快速处理
// Don't return but instead continue and send the ACTION_NEW_OUTGOING_CALL broadcast // so that third parties can still inspect (but not intercept) the outgoing call. When // the broadcast finally reaches the OutgoingCallBroadcastReceiver, we'll know not to // initiate the call again because of the presence of the EXTRA_ALREADY_CALLED extra. } Log.i(this, "Sending NewOutgoingCallBroadcast for %s", mCall); //对普通号码的处理 broadcastIntent(intent, number, !callImmediately);//广播ACTION_NEW_OUTGOING_CALL return DisconnectCause.NOT_DISCONNECTED; } |
从上可以看出,对于这三类电话,broadcaster()做了甄别处理,对number和号码来源进行分析,将PRIVILEGED CALL转化成普通拨号或紧急拨号,并屏蔽第三方APP的紧急拨号,接着将紧急号码直接交给mCallsManagerd的placeOutgoingCall()方法去做进一步处理,而普通号码为了可以通知给发起拨号的第三方APP需要进行广播。内容为ACTION_NEW_OUTGOING_CALL型的 Intent。此广播将在NewOutgoingCallIntentBroadcaster(其实就是自己的一个内部类)中接收。onReceive()对广播的内容作了一定的甄别判断后,将处理权交给了mCallsManager的placeOutgoingCall()。对比之前对紧急电话的处理也是交给了这个方法,可以看出此方法为处理去电的一个关口方法。
void placeOutgoingCall(Call call, Uri handle, GatewayInfo gatewayInfo, boolean speakerphoneOn,int videoState) { …… call.setHandle(uriHandle);//设置一些信息 call.setGatewayInfo(gatewayInfo); call.setStartWithSpeakerphoneOn(speakerphoneOn); call.setVideoState(videoState); …… if (call.getTargetPhoneAccount() != null || isEmergencyCall) { call.startCreateConnection(mPhoneAccountRegistrar);//创建并开启连接 } } |
进入Call的startCreateConnection(),此后都是围绕Connection展开的,因为这里可以认为是客户端,而真正的Connection由Android后台服务控制,所以Connection对于客户端来说就是获取远程服务,在Android中这明显会用Binder机制获取服务的远程代理对象。(注意android文件系统中有多个不同的Call.java):
void startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar) { Preconditions.checkState(mCreateConnectionProcessor == null); //新建一个创建连接的processor mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this, phoneAccountRegistrar, mContext); mCreateConnectionProcessor.process();//将连接任务交给preocessor去处理 } |
进入CreateConnectionProcessor.process():
void process() { Log.v(this, "process"); mAttemptRecords = new ArrayList<>(); if (mCall.getTargetPhoneAccount() != null) { mAttemptRecords.add(new CallAttemptRecord( mCall.getTargetPhoneAccount(), mCall.getTargetPhoneAccount())); } adjustAttemptsForConnectionManager(); adjustAttemptsForEmergency(); mAttemptRecordIterator = mAttemptRecords.iterator(); attemptNextPhoneAccount();//这里通过包装类调用了Service的createConnection方法,//而这个包装类是IConnectionService的子类 } |
进入attempNextPhoneAccount:
private void attemptNextPhoneAccount() { …… if (mResponse != null && attempt != null) { Log.i(this, "Trying attempt %s", attempt); PhoneAccountHandle phoneAccount = attempt.connectionManagerPhoneAccount; ConnectionServiceWrapper service =//再次获取服务,这次是ConnectionService mRepository.getService( phoneAccount.getComponentName(), phoneAccount.getUserHandle()); if (service == null) {//有效性判断 Log.i(this, "Found no connection service for attempt %s", attempt); attemptNextPhoneAccount(); } else { mCall.setConnectionManagerPhoneAccount(attempt.connectionManagerPhoneAccount); mCall.setTargetPhoneAccount(attempt.targetPhoneAccount); mCall.setConnectionService(service); setTimeoutIfNeeded(service, attempt);
service.createConnection(mCall, new Response(service));//进入这里 } …… } } |
进入ConnectionServiceWrapper.createConnection();
void createConnection(final Call call, final CreateConnectionResponse response) { Log.d(this, "createConnection(%s) via %s.", call, getComponentName()); //这里new了一个Callback对 BindCallback callback = new BindCallback() { @Override public void onSuccess() {//连接成功时的回调方法 String callId = mCallIdMapper.getCallId(call); mPendingResponses.put(callId, response);
GatewayInfo gatewayInfo = call.getGatewayInfo(); Bundle extras = call.getExtras(); if (gatewayInfo != null && gatewayInfo.getGatewayProviderPackageName() != null && gatewayInfo.getOriginalAddress() != null) { extras = (Bundle) extras.clone(); extras.putString( TelecomManager.GATEWAY_PROVIDER_PACKAGE, gatewayInfo.getGatewayProviderPackageName()); extras.putParcelable( TelecomManager.GATEWAY_ORIGINAL_ADDRESS, gatewayInfo.getOriginalAddress()); } Log.event(call, Log.Events.START_CONNECTION, Log.piiHandle(call.getHandle())); try { mServiceInterface.createConnection(//利用远程对象调用Service服务 call.getConnectionManagerPhoneAccount(), callId, new ConnectionRequest( call.getTargetPhoneAccount(), call.getHandle(), extras, call.getVideoState()), call.isIncoming(), call.isUnknown()); } 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() { Log.e(this, new Exception(), "Failure to call %s", getComponentName()); response.handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.ERROR)); } }; //绑定callback到call mBinder.bind(callback, call); } |
此方法实际上是建立了一个callBack,然后进行了bind。bind()在ServiceBinder中实现:
void bind(BindCallback callback, Call call) { Log.d(ServiceBinder.this, "bind()"); // Reset any abort request if we're asked to bind again. clearAbort(); if (!mCallbacks.isEmpty()) { // Binding already in progress, append to the list of callbacks and bail out. mCallbacks.add(callback); return; } mCallbacks.add(callback); if (mServiceConnection == null) {//如果还没有获取mServiceConnection,获取它 Intent serviceIntent = new //首先构造Intent以便后续的绑定检测Intent(mServiceAction).setComponent(mComponentName); //以call为参数新建ServiceConnection实例 ServiceConnection connection = new ServiceBinderConnection(call);
Log.event(call, Log.Events.BIND_CS, mComponentName); final int bindingFlags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE; final boolean isBound; if (mUserHandle != null) {//带mUserHandle的绑定 isBound = mContext.bindServiceAsUser(serviceIntent, connection, bindingFlags, mUserHandle); } else {//不带mUserHandle的绑定 isBound = mContext.bindService(serviceIntent, connection, bindingFlags); } //绑定失败则返回isBound=false,绑定成功暂时不返回,回调//onServiceConnected方法,这部分逻辑很奇怪,这里也仅仅是我的推测 if (!isBound) {//绑定失败,个人觉得绑定失败也应该弄个回调,好理解好维护 handleFailedConnection(); return; } } else {//老早之前就绑定了,意味着后续的不少处理可以直接跳过,直接处理 Log.d(ServiceBinder.this, "Service is already bound."); Preconditions.checkNotNull(mBinder); handleSuccessfulConnection();//这里一定要看清楚else所对应的if } } |
ServiceBinderConnection是一个内部类,其内部并没有变量域。如果绑定成功的话,在ServiceBinder的内部类ServiceBinderConnection的onServiceConnected()方法就被触发调用。
public void onServiceConnected(ComponentName componentName, IBinder binder) { synchronized (mLock) { Log.i(this, "Service bound %s", componentName); Log.event(mCall, Log.Events.CS_BOUND, componentName); mCall = null;
// 如果Unbind请求在队列中,立即Unbind if (mIsBindingAborted) { clearAbort(); logServiceDisconnected("onServiceConnected"); mContext.unbindService(this); handleFailedConnection(); return; }
mServiceConnection = this; setBinder(binder);①//主要逻辑就是获得远程服务代理:mServiceInterface =//IConnectionService.Stub.asInterface(binder); handleSuccessfulConnection();//②连接成功处理,回调Callback的OnSuccess() } } |
① 进入setBinder
private void setBinder(IBinder binder) { if (mBinder != binder) { mBinder = binder; setServiceInterface(binder);//此方法为抽象方法 if (binder == null) { for (Listener l : mListeners) { l.onUnbind(this); } } } } |
进入setServiceIntenterface(),回调的是ConnectionServiceWrapper的实现。
protected void setServiceInterface(IBinder binder) { if (binder == null) { handleConnectionServiceDeath(); CallsManager.getInstance().handleConnectionServiceDeath(this); mServiceInterface = null; } else {//获得所需Service的远程引用 mServiceInterface = IConnectionService.Stub.asInterface(binder);//服务代理 addConnectionServiceAdapter(mAdapter); } } |
② 进入handleSuccessfuconnection()
private void handleSuccessfulConnection() { for (BindCallback callback : mCallbacks) { callback.onSuccess(); } mCallbacks.clear(); } |
这里很明显的意图是处理connection成功状态,回调了之前设立的callback()(查看之前代码)这里之前的分析错了,应该是处理Binder获取服务的结果,如果服务获取成功,就在callback的OnSuccess中开始connection。于是程序又回到ConnectionServiceWrapper的createConnection()方法。由此进入TelecommFramework。
1.3 TelecommFramework
ConnectionServiceWrapper从名字上来看应该是ConnectionService的一个包装,此类的createConnection()方法最终调用了mServiceInterface.createConnection(),通过Android的Binder通信机制,这里实际上可以理解为RPC,如果了解Binder机制和Looper机制的话,就知道它最终调用的肯定是Service进程(当然也可能是线程)端的同名方法,这里调用的远程服务方法为ConnectionService.java的createConnection()方法,如下。。
private void createConnection( final PhoneAccountHandle callManagerAccount, final String callId, final ConnectionRequest request, boolean isIncoming, boolean isUnknown) { Log.d(this, "createConnection, callManagerAccount: %s, callId: %s, request: %s, " + "isIncoming: %b, isUnknown: %b", callManagerAccount, callId, request, isIncoming, isUnknown);
Connection connection = isUnknown ? onCreateUnknownConnection(callManagerAccount, request) : isIncoming ? onCreateIncomingConnection(callManagerAccount, request) : onCreateOutgoingConnection(callManagerAccount, request);//去电在这里 Log.d(this, "createConnection, connection: %s", connection); if (connection == null) {//创建connection失败 connection = Connection.createFailedConnection( new DisconnectCause(DisconnectCause.ERROR)); }
if (connection.getState() != Connection.STATE_DISCONNECTED) {//connection成功 addConnection(callId, connection);//添加到连接队列 }
Uri address = connection.getAddress(); String number = address == null ? "null" : address.getSchemeSpecificPart(); Log.v(this, "createConnection, number: %s, state: %s, capabilities: %s", Connection.toLogSafePhoneNumber(number), Connection.stateToString(connection.getState()), Connection.capabilitiesToString(connection.getConnectionCapabilities()));
Log.d(this, "createConnection, calling handleCreateConnectionSuccessful %s", callId); mAdapter.handleCreateConnectionComplete( callId, request, new ParcelableConnection( request.getAccountHandle(), connection.getState(), connection.getConnectionCapabilities(), connection.getAddress(), connection.getAddressPresentation(), connection.getCallerDisplayName(), connection.getCallerDisplayNamePresentation(), connection.getVideoProvider() == null ? null : connection.getVideoProvider().getInterface(), connection.getVideoState(), connection.isRingbackRequested(), connection.getAudioModeIsVoip(), connection.getConnectTimeMillis(), connection.getStatusHints(), connection.getDisconnectCause(), createIdList(connection.getConferenceables()), connection.getExtras())); } |
onCreateOutgoingConnection()在ConnectionService类中为空方法,它将在TelephonyConnectionService中被重写,因此进入TelephonyConnectionService的同名方法中。
1.4 TelephonyService
TelephonyConnectionService做了一定的处理之后交给了本类的placeOutgoingConnection()进行进一步的处理:
private void placeOutgoingConnection( TelephonyConnection connection, Phone phone, ConnectionRequest request) { String number = connection.getAddress().getSchemeSpecificPart();
com.android.internal.telephony.Connection originalConnection; try {//由此进入TelephonyFramework,后续的内容围绕phone展开 originalConnection =phone.dial(number, null, request.getVideoState(), request.getExtras()); } catch (CallStateException e) { Log.e(this, e, "placeOutgoingConnection, phone.dial exception: " + e); int cause = android.telephony.DisconnectCause.OUTGOING_FAILURE; if (e.getError() == CallStateException.ERROR_DISCONNECTED) { cause = android.telephony.DisconnectCause.OUT_OF_SERVICE; } connection.setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause( cause, e.getMessage())); return; }
if (originalConnection == null) { int telephonyDisconnectCause = android.telephony.DisconnectCause.OUTGOING_FAILURE; // 在GSMPhone,null connection意味着拨打MMI code if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM) { Log.d(this, "dialed MMI code"); 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); startActivity(intent); } Log.d(this, "placeOutgoingConnection, phone.dial returned null"); connection.setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause( telephonyDisconnectCause, "Connection is null")); } else { connection.setOriginalConnection(originalConnection); } } |
由此进入TelephonyFramework层
1.5 TelephonyFramework
在TelephonyFramework层所涉及到的类相对来说比较少,以CDMA为例,主要包括CDMAPhone,CDMACallTracker和与RIL层交互的RIL.java。
接着前上节的分析,程序进入TelephonyFramework层首先执行的是dial(),这里需要假定之前通过phoneFactory获得的是一个CDMAPhone,那么此处调用的是CDMAPhone.dial()。
public Connection dial(String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras) throws CallStateException { if (uusInfo != null) { throw new CallStateException("Sending UUS information NOT supported in CDMA!"); }
boolean isEmergency = PhoneNumberUtils.isEmergencyNumber(dialString); ImsPhone imsPhone = mImsPhone;
boolean imsUseEnabled = isImsUseEnabled() && imsPhone != null && (imsPhone.isVolteEnabled() || imsPhone.isVowifiEnabled()) && (imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE);
boolean useImsForEmergency = ImsManager.isVolteEnabledByPlatform(mContext) && imsPhone != null && isEmergency && mContext.getResources().getBoolean( com.android.internal.R.bool.useImsAlwaysForEmergencyCall) && ImsManager.isNonTtyOrTtyOnVolteEnabled(mContext) && (imsPhone.getServiceState().getState() != ServiceState.STATE_POWER_OFF);
if (DBG) { Rlog.d(LOG_TAG, "imsUseEnabled=" + imsUseEnabled + ", useImsForEmergency=" + useImsForEmergency + ", imsPhone=" + imsPhone + ", imsPhone.isVolteEnabled()=" + ((imsPhone != null) ? imsPhone.isVolteEnabled() : "N/A") + ", imsPhone.isVowifiEnabled()=" + ((imsPhone != null) ? imsPhone.isVowifiEnabled() : "N/A") + ", imsPhone.getServiceState().getState()=" + ((imsPhone != null) ? imsPhone.getServiceState().getState() : "N/A")); }
ImsPhone.checkWfcWifiOnlyModeBeforeDial(mImsPhone, mContext);
if (imsUseEnabled || useImsForEmergency) { try { if (DBG) Rlog.d(LOG_TAG, "Trying IMS PS call"); return imsPhone.dial(dialString, uusInfo, videoState, intentExtras); } catch (CallStateException e) { if (DBG) Rlog.d(LOG_TAG, "IMS PS call exception " + e + "imsUseEnabled =" + imsUseEnabled + ", imsPhone =" + imsPhone); if (!ImsPhone.CS_FALLBACK.equals(e.getMessage())) { 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"); } if (DBG) Rlog.d(LOG_TAG, "Trying (non-IMS) CS call"); return dialInternal(dialString, null, videoState, intentExtras); } |
接着进入dialInternal()
protected Connection dialInternal (String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras) throws CallStateException { // 对电话号码作解析 String newDialString = PhoneNumberUtils.stripSeparators(dialString); return mCT.dial(newDialString); //注意这里的mCT的类型为CallTracker |
由于是CDMA,调用的自然是CDMACallTracker的dial(),此类有两个dial(),满参实现为:
Connection dial (String dialString, int clirMode) throws CallStateException { // 清除掉disconnected状态 clearDisconnected(); if (!canDial()) { throw new CallStateException("cannot dial in current state"); }
TelephonyManager tm =//又一次获取服务,这次是TelephonyManager (TelephonyManager) mPhone.getContext().getSystemService(Context.TELEPHONY_SERVICE); String origNumber = dialString; String operatorIsoContry = tm.getNetworkCountryIsoForPhone(mPhone.getPhoneId()); String simIsoContry = tm.getSimCountryIsoForPhone(mPhone.getPhoneId()); boolean internationalRoaming = !TextUtils.isEmpty(operatorIsoContry) && !TextUtils.isEmpty(simIsoContry) && !simIsoContry.equals(operatorIsoContry); if (internationalRoaming) { if ("us".equals(simIsoContry)) { internationalRoaming = internationalRoaming && !"vi".equals(operatorIsoContry); } else if ("vi".equals(simIsoContry)) { internationalRoaming = internationalRoaming && !"us".equals(operatorIsoContry); } } if (internationalRoaming) { dialString = convertNumberIfNecessary(mPhone, dialString); }
String inEcm=SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE, "false"); boolean isPhoneInEcmMode = inEcm.equals("true"); boolean isEmergencyCall = PhoneNumberUtils.isLocalEmergencyNumber(mPhone.getContext(), dialString);
// Cancel Ecm timer if a second emergency call is originating in Ecm mode if (isPhoneInEcmMode && isEmergencyCall) { handleEcmTimer(CDMAPhone.CANCEL_ECM_TIMER); }
// We are initiating a call therefore even if we previously // didn't know the state (i.e. Generic was true) we now know // and therefore can set Generic to false. mForegroundCall.setGeneric(false);
// 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() == CdmaCall.State.ACTIVE) { return dialThreeWay(dialString); }
mPendingMO = new CdmaConnection(mPhone.getContext(),//建立连接,默认为前景CallcheckForTestEmergencyNumber(dialString), this, mForegroundCall); mHangupPendingMO = false;
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);
// Check data call disableDataCallInEmergencyCall(dialString);
// In Ecm mode, if another emergency call is dialed, Ecm mode will not exit. if(!isPhoneInEcmMode || (isPhoneInEcmMode && isEmergencyCall)) { //mCi的类型为CommandsInterface,在创建phone的时候作为参数,实际上执行的是//RIL.dial() mCi.dial(mPendingMO.getAddress(), clirMode, obtainCompleteMessage()); } else { mPhone.exitEmergencyCallbackMode(); mPhone.setOnEcbModeExitResponse(this,EVENT_EXIT_ECM_RESPONSE_CDMA, null); mPendingCallClirMode=clirMode; mPendingCallInEcm=true; } }
if (mNumberConverted) { mPendingMO.setConverted(origNumber); mNumberConverted = false; } //更新phone状态 updatePhoneState(); //发起phone状态变化通知 mPhone.notifyPreciseCallStateChanged(); //返回通话连接 return mPendingMO; } |
标红且背景为金黄色的地方,以默认参数EVENT_OPERATION_COMPLETE进入obtainCompeleteMessage()的满参实现,它返回了一个消息。
private Message obtainCompleteMessage(int what) {//默认为EVENT_OPERATION_COMPLETE mPendingOperations++; mLastRelevantPoll = null; mNeedsPoll = true;
if (DBG_POLL) log("obtainCompleteMessage: pendingOperations=" + mPendingOperations + ", needsPoll=" + mNeedsPoll);
return obtainMessage(what); } |
仔细分析mCi的来龙去脉,可以锁定,其调用的dial方法就是RIL.java的dial方法。此方法同样有两个重载方法,其中满参重载方法如下:
public void dial(String address, int clirMode, UUSInfo uusInfo, Message result) { //创建了一个RILRequest对象rr,这里的result为之前发送的EVENT_OPERATION_COMPLETE RILRequest rr = RILRequest.obtain(RIL_REQUEST_DIAL, result);
rr.mParcel.writeString(address); rr.mParcel.writeInt(clirMode);
if (uusInfo == null) { rr.mParcel.writeInt(0); // UUS information is absent } else { rr.mParcel.writeInt(1); // UUS information is present rr.mParcel.writeInt(uusInfo.getType()); rr.mParcel.writeInt(uusInfo.getDcs()); rr.mParcel.writeByteArray(uusInfo.getUserData()); } //打印日志 if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); //将rr发送给RILC,此时的rr内部为RIL_REQUEST_DIAL //RILC层处理完成后会调用RILJ的processSolicited方法 send(rr); } |
RILJ从名字可以看出他是RILC在java层的实现,负责了Framework层与RIL层的交互,上层发送rr到底层处理,而底层通过RILJ将处理结果上报。RILJ的processSolicited(),此方法负责接收并处理RILC层回传上来的消息。Android6.0的phone的底层结构并没有多大变化,下面是一个老版本的图,Android6.0可能小有不同,不过大体应该一致:
图 phone底层结构图
在RIL层中的数据交互使用基于Socket网络连接的方式,当然这种socket并不是网络socket而是localSocket。消息数据按其处理方式的不同可以分为两类:
1,Solicited消息。这类消息如dial拨号,answer接听电话等主动操作请求。Solicited请求类的RIL消息,根据其动作行为,可再细分为SolicitedRequest和Solicited Response。正常情况下这两类消息成对出现,请求和应答一一对应。
2,UnSolicited消息。这类消息为底层主动上报的消息,如来电,接受短信等等。此消息由于是底层Modem主动上报,因此没有Request只有Response。
以下函数充分证明了这一点:
private void processResponse (Parcel p) { int type; type = p.readInt(); if (type == RESPONSE_UNSOLICITED) { processUnsolicited (p);//处理Unsolicited消息 } else if (type == RESPONSE_SOLICITED) { RILRequest rr = processSolicited (p);//处理Unsolicited消息 if (rr != null) { rr.release(); decrementWakeLock(); } } } |
RIL.dial()最后send(rr),很明显,上层下发了一个rr,底层处理接受并处理之后还会回发一个Respose以表明处理结果,其处理结果根据上面的介绍,将在processSolicited()中进行处理:
private RILRequest processSolicited (Parcel p) { int serial, error; boolean found = false;
serial = p.readInt(); error = p.readInt();
RILRequest rr;
rr = findAndRemoveRequestFromList(serial);
if (rr == null) { Rlog.w(RILJ_LOG_TAG, "Unexpected solicited response! sn: " + serial + " error: " + error); return null; }
Object ret = null;
if (error == 0 || p.dataAvail() > 0) { // either command succeeds or command fails but with data payload try {switch (rr.mRequest) { /* cat libs/telephony/ril_commands.h \ | egrep "^ *{RIL_" \ | sed -re 's/\{([^,]+),[^,]+,([^}]+).+/case \1: ret = \2(p); break;/' */ case RIL_REQUEST_GET_SIM_STATUS: ret = responseIccCardStatus(p); break; case RIL_REQUEST_ENTER_SIM_PIN: ret = responseInts(p); break; case RIL_REQUEST_ENTER_SIM_PUK: ret = responseInts(p); break; case RIL_REQUEST_ENTER_SIM_PIN2: ret = responseInts(p); break; case RIL_REQUEST_ENTER_SIM_PUK2: ret = responseInts(p); break; case RIL_REQUEST_CHANGE_SIM_PIN: ret = responseInts(p); break; case RIL_REQUEST_CHANGE_SIM_PIN2: ret = responseInts(p); break; case RIL_REQUEST_ENTER_NETWORK_DEPERSONALIZATION: ret = responseInts(p); break; case RIL_REQUEST_GET_CURRENT_CALLS: ret = responseCallList(p); break; case RIL_REQUEST_DIAL: ret = responseVoid(p); break;//这里 case RIL_REQUEST_GET_IMSI: ret = responseString(p); break; case RIL_REQUEST_HANGUP: ret = responseVoid(p); break; case RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND: ret = responseVoid(p); break; case RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND: { if (mTestingEmergencyCall.getAndSet(false)) { if (mEmergencyCallbackModeRegistrant != null) { riljLog("testing emergency call, notify ECM Registrants"); mEmergencyCallbackModeRegistrant.notifyRegistrant(); } } ret = responseVoid(p); break; } case RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE: ret = responseVoid(p); break; case RIL_REQUEST_CONFERENCE: ret = responseVoid(p); break; case RIL_REQUEST_UDUB: ret = responseVoid(p); break; case RIL_REQUEST_LAST_CALL_FAIL_CAUSE: ret = responseFailCause(p); break; case RIL_REQUEST_SIGNAL_STRENGTH: ret = responseSignalStrength(p); break; case RIL_REQUEST_VOICE_REGISTRATION_STATE: ret = responseStrings(p); break; case RIL_REQUEST_DATA_REGISTRATION_STATE: ret = responseStrings(p); break; case RIL_REQUEST_OPERATOR: ret = responseStrings(p); break; case RIL_REQUEST_RADIO_POWER: ret = responseVoid(p); break; case RIL_REQUEST_DTMF: ret = responseVoid(p); break; case RIL_REQUEST_SEND_SMS: ret = responseSMS(p); break; case RIL_REQUEST_SEND_SMS_EXPECT_MORE: ret = responseSMS(p); break; case RIL_REQUEST_SETUP_DATA_CALL: ret = responseSetupDataCall(p); break; case RIL_REQUEST_SIM_IO: ret = responseICC_IO(p); break; case RIL_REQUEST_SEND_USSD: ret = responseVoid(p); break; case RIL_REQUEST_CANCEL_USSD: ret = responseVoid(p); break; case RIL_REQUEST_GET_CLIR: ret = responseInts(p); break; case RIL_REQUEST_SET_CLIR: ret = responseVoid(p); break; case RIL_REQUEST_QUERY_CALL_FORWARD_STATUS: ret = responseCallForward(p); break; case RIL_REQUEST_SET_CALL_FORWARD: ret = responseVoid(p); break; case RIL_REQUEST_QUERY_CALL_WAITING: ret = responseInts(p); break; case RIL_REQUEST_SET_CALL_WAITING: ret = responseVoid(p); break; case RIL_REQUEST_SMS_ACKNOWLEDGE: ret = responseVoid(p); break; case RIL_REQUEST_GET_IMEI: ret = responseString(p); break; case RIL_REQUEST_GET_IMEISV: ret = responseString(p); break; case RIL_REQUEST_ANSWER: ret = responseVoid(p); break; case RIL_REQUEST_DEACTIVATE_DATA_CALL: ret = responseVoid(p); break; case RIL_REQUEST_QUERY_FACILITY_LOCK: ret = responseInts(p); break; case RIL_REQUEST_SET_FACILITY_LOCK: ret = responseInts(p); break; case RIL_REQUEST_CHANGE_BARRING_PASSWORD: ret = responseVoid(p); break; case RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE: ret = responseInts(p); break; case RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC: ret = responseVoid(p); break; case RIL_REQUEST_SET_NETWORK_SELECTION_MANUAL: ret = responseVoid(p); break; case RIL_REQUEST_QUERY_AVAILABLE_NETWORKS : ret = responseOperatorInfos(p); break; case RIL_REQUEST_DTMF_START: ret = responseVoid(p); break; case RIL_REQUEST_DTMF_STOP: ret = responseVoid(p); break; case RIL_REQUEST_BASEBAND_VERSION: ret = responseString(p); break; case RIL_REQUEST_SEPARATE_CONNECTION: ret = responseVoid(p); break; case RIL_REQUEST_SET_MUTE: ret = responseVoid(p); break; case RIL_REQUEST_GET_MUTE: ret = responseInts(p); break; case RIL_REQUEST_QUERY_CLIP: ret = responseInts(p); break; case RIL_REQUEST_LAST_DATA_CALL_FAIL_CAUSE: ret = responseInts(p); break; case RIL_REQUEST_DATA_CALL_LIST: ret = responseDataCallList(p); break; case RIL_REQUEST_RESET_RADIO: ret = responseVoid(p); break; case RIL_REQUEST_OEM_HOOK_RAW: ret = responseRaw(p); break; case RIL_REQUEST_OEM_HOOK_STRINGS: ret = responseStrings(p); break; case RIL_REQUEST_SCREEN_STATE: ret = responseVoid(p); break; case RIL_REQUEST_SET_SUPP_SVC_NOTIFICATION: ret = responseVoid(p); break; case RIL_REQUEST_WRITE_SMS_TO_SIM: ret = responseInts(p); break; case RIL_REQUEST_DELETE_SMS_ON_SIM: ret = responseVoid(p); break; case RIL_REQUEST_SET_BAND_MODE: ret = responseVoid(p); break; case RIL_REQUEST_QUERY_AVAILABLE_BAND_MODE: ret = responseInts(p); break; case RIL_REQUEST_STK_GET_PROFILE: ret = responseString(p); break; case RIL_REQUEST_STK_SET_PROFILE: ret = responseVoid(p); break; case RIL_REQUEST_STK_SEND_ENVELOPE_COMMAND: ret = responseString(p); break; case RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE: ret = responseVoid(p); break; case RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM: ret = responseInts(p); break; case RIL_REQUEST_EXPLICIT_CALL_TRANSFER: ret = responseVoid(p); break; case RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE: ret = responseVoid(p); break; case RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE: ret = responseGetPreferredNetworkType(p); break; case RIL_REQUEST_GET_NEIGHBORING_CELL_IDS: ret = responseCellList(p); break; case RIL_REQUEST_SET_LOCATION_UPDATES: ret = responseVoid(p); break; case RIL_REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE: ret = responseVoid(p); break; case RIL_REQUEST_CDMA_SET_ROAMING_PREFERENCE: ret = responseVoid(p); break; case RIL_REQUEST_CDMA_QUERY_ROAMING_PREFERENCE: ret = responseInts(p); break; case RIL_REQUEST_SET_TTY_MODE: ret = responseVoid(p); break; case RIL_REQUEST_QUERY_TTY_MODE: ret = responseInts(p); break; case RIL_REQUEST_CDMA_SET_PREFERRED_VOICE_PRIVACY_MODE: ret = responseVoid(p); break; case RIL_REQUEST_CDMA_QUERY_PREFERRED_VOICE_PRIVACY_MODE: ret = responseInts(p); break; case RIL_REQUEST_CDMA_FLASH: ret = responseVoid(p); break; case RIL_REQUEST_CDMA_BURST_DTMF: ret = responseVoid(p); break; case RIL_REQUEST_CDMA_SEND_SMS: ret = responseSMS(p); break; case RIL_REQUEST_CDMA_SMS_ACKNOWLEDGE: ret = responseVoid(p); break; case RIL_REQUEST_GSM_GET_BROADCAST_CONFIG: ret = responseGmsBroadcastConfig(p); break; case RIL_REQUEST_GSM_SET_BROADCAST_CONFIG: ret = responseVoid(p); break; case RIL_REQUEST_GSM_BROADCAST_ACTIVATION: ret = responseVoid(p); break; case RIL_REQUEST_CDMA_GET_BROADCAST_CONFIG: ret = responseCdmaBroadcastConfig(p); break; case RIL_REQUEST_CDMA_SET_BROADCAST_CONFIG: ret = responseVoid(p); break; case RIL_REQUEST_CDMA_BROADCAST_ACTIVATION: ret = responseVoid(p); break; case RIL_REQUEST_CDMA_VALIDATE_AND_WRITE_AKEY: ret = responseVoid(p); break; case RIL_REQUEST_CDMA_SUBSCRIPTION: ret = responseStrings(p); break; case RIL_REQUEST_CDMA_WRITE_SMS_TO_RUIM: ret = responseInts(p); break; case RIL_REQUEST_CDMA_DELETE_SMS_ON_RUIM: ret = responseVoid(p); break; case RIL_REQUEST_DEVICE_IDENTITY: ret = responseStrings(p); break; case RIL_REQUEST_GET_SMSC_ADDRESS: ret = responseString(p); break; case RIL_REQUEST_SET_SMSC_ADDRESS: ret = responseVoid(p); break; case RIL_REQUEST_EXIT_EMERGENCY_CALLBACK_MODE: ret = responseVoid(p); break; case RIL_REQUEST_REPORT_SMS_MEMORY_STATUS: ret = responseVoid(p); break; case RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING: ret = responseVoid(p); break; case RIL_REQUEST_CDMA_GET_SUBSCRIPTION_SOURCE: ret = responseInts(p); break; case RIL_REQUEST_ISIM_AUTHENTICATION: ret = responseString(p); break; case RIL_REQUEST_ACKNOWLEDGE_INCOMING_GSM_SMS_WITH_PDU: ret = responseVoid(p); break; case RIL_REQUEST_STK_SEND_ENVELOPE_WITH_STATUS: ret = responseICC_IO(p); break; case RIL_REQUEST_VOICE_RADIO_TECH: ret = responseInts(p); break; case RIL_REQUEST_GET_CELL_INFO_LIST: ret = responseCellInfoList(p); break; case RIL_REQUEST_SET_UNSOL_CELL_INFO_LIST_RATE: ret = responseVoid(p); break; case RIL_REQUEST_SET_INITIAL_ATTACH_APN: ret = responseVoid(p); break; case RIL_REQUEST_SET_DATA_PROFILE: ret = responseVoid(p); break; case RIL_REQUEST_IMS_REGISTRATION_STATE: ret = responseInts(p); break; case RIL_REQUEST_IMS_SEND_SMS: ret = responseSMS(p); break; case RIL_REQUEST_SIM_TRANSMIT_APDU_BASIC: ret = responseICC_IO(p); break; case RIL_REQUEST_SIM_OPEN_CHANNEL: ret = responseInts(p); break; case RIL_REQUEST_SIM_CLOSE_CHANNEL: ret = responseVoid(p); break; case RIL_REQUEST_SIM_TRANSMIT_APDU_CHANNEL: ret = responseICC_IO(p); break; case RIL_REQUEST_NV_READ_ITEM: ret = responseString(p); break; case RIL_REQUEST_NV_WRITE_ITEM: ret = responseVoid(p); break; case RIL_REQUEST_NV_WRITE_CDMA_PRL: ret = responseVoid(p); break; case RIL_REQUEST_NV_RESET_CONFIG: ret = responseVoid(p); break; case RIL_REQUEST_SET_UICC_SUBSCRIPTION: ret = responseVoid(p); break; case RIL_REQUEST_ALLOW_DATA: ret = responseVoid(p); break; case RIL_REQUEST_GET_HARDWARE_CONFIG: ret = responseHardwareConfig(p); break; case RIL_REQUEST_SIM_AUTHENTICATION: ret = responseICC_IOBase64(p); break; case RIL_REQUEST_SHUTDOWN: ret = responseVoid(p); break; case RIL_REQUEST_GET_RADIO_CAPABILITY: ret = responseRadioCapability(p); break; case RIL_REQUEST_SET_RADIO_CAPABILITY: ret = responseRadioCapability(p); break; case RIL_REQUEST_START_LCE: ret = responseLceStatus(p); break; case RIL_REQUEST_STOP_LCE: ret = responseLceStatus(p); break; case RIL_REQUEST_PULL_LCEDATA: ret = responseLceData(p); break; case RIL_REQUEST_GET_ACTIVITY_INFO: ret = responseActivityData(p); break; default: throw new RuntimeException("Unrecognized solicited response: " + rr.mRequest); //break; }} catch (Throwable tr) { // Exceptions here usually mean invalid RIL responses
Rlog.w(RILJ_LOG_TAG, rr.serialString() + "< " + requestToString(rr.mRequest) + " exception, possible invalid RIL response", tr);
if (rr.mResult != null) { AsyncResult.forMessage(rr.mResult, null, tr); rr.mResult.sendToTarget(); } return rr; } }
if (rr.mRequest == RIL_REQUEST_SHUTDOWN) { // Set RADIO_STATE to RADIO_UNAVAILABLE to continue shutdown process // regardless of error code to continue shutdown procedure. riljLog("Response to RIL_REQUEST_SHUTDOWN received. Error is " + error + " Setting Radio State to Unavailable regardless of error."); setRadioState(RadioState.RADIO_UNAVAILABLE); }
// Here and below fake RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED, see b/7255789. // This is needed otherwise we don't automatically transition to the main lock // screen when the pin or puk is entered incorrectly. switch (rr.mRequest) { case RIL_REQUEST_ENTER_SIM_PUK: case RIL_REQUEST_ENTER_SIM_PUK2: if (mIccStatusChangedRegistrants != null) { if (RILJ_LOGD) { riljLog("ON enter sim puk fakeSimStatusChanged: reg count=" + mIccStatusChangedRegistrants.size()); } mIccStatusChangedRegistrants.notifyRegistrants(); } break; }
if (error != 0) { switch (rr.mRequest) { case RIL_REQUEST_ENTER_SIM_PIN: case RIL_REQUEST_ENTER_SIM_PIN2: case RIL_REQUEST_CHANGE_SIM_PIN: case RIL_REQUEST_CHANGE_SIM_PIN2: case RIL_REQUEST_SET_FACILITY_LOCK: if (mIccStatusChangedRegistrants != null) { if (RILJ_LOGD) { riljLog("ON some errors fakeSimStatusChanged: reg count=" + mIccStatusChangedRegistrants.size()); } mIccStatusChangedRegistrants.notifyRegistrants(); } break; case RIL_REQUEST_GET_RADIO_CAPABILITY: { // Ideally RIL's would support this or at least give NOT_SUPPORTED // but the hammerhead RIL reports GENERIC :( // TODO - remove GENERIC_FAILURE catching: b/21079604 if (REQUEST_NOT_SUPPORTED == error || GENERIC_FAILURE == error) { // we should construct the RAF bitmask the radio // supports based on preferred network bitmasks ret = makeStaticRadioCapability(); error = 0; } break; } case RIL_REQUEST_GET_ACTIVITY_INFO: ret = new ModemActivityInfo(0, 0, 0, new int [ModemActivityInfo.TX_POWER_LEVELS], 0, 0); error = 0; break; }
if (error != 0) rr.onError(error, ret); } if (error == 0) {
if (RILJ_LOGD) riljLog(rr.serialString() + "< " + requestToString(rr.mRequest) + " " + retToString(rr.mRequest, ret));
if (rr.mResult != null) { AsyncResult.forMessage(rr.mResult, ret, null); //异步,因为后面要销毁rr rr.mResult.sendToTarget();//发送Handler消息通知,此消息将在//CDMACallTracker中处理 } } return rr; } |
进入CdmaCallTracker的handleMessage()
public void handleMessage (Message msg) { AsyncResult ar;
if (!mPhone.mIsTheCurrentActivePhone) { Rlog.w(LOG_TAG, "Ignoring events received on inactive CdmaPhone"); return; } switch (msg.what) { case EVENT_POLL_CALLS_RESULT:{ Rlog.d(LOG_TAG, "Event EVENT_POLL_CALLS_RESULT Received"); ar = (AsyncResult)msg.obj;
if(msg == mLastRelevantPoll) { if(DBG_POLL) log( "handle EVENT_POLL_CALL_RESULT: set needsPoll=F"); mNeedsPoll = false; mLastRelevantPoll = null; handlePollCalls((AsyncResult)msg.obj); } } break;
case EVENT_OPERATION_COMPLETE: //完成事件,此消息之前在本类中创建,最后返回给//自己处理 operationComplete();//完成事件处理 break;
case EVENT_SWITCH_RESULT: // In GSM call operationComplete() here which gets the // current call list. But in CDMA there is no list so // there is nothing to do. break;
case EVENT_GET_LAST_CALL_FAIL_CAUSE: int causeCode; String vendorCause = null; ar = (AsyncResult)msg.obj;
operationComplete();
if (ar.exception != null) { // An exception occurred...just treat the disconnect // cause as "normal" causeCode = CallFailCause.NORMAL_CLEARING; Rlog.i(LOG_TAG, "Exception during getLastCallFailCause, assuming normal disconnect"); } else { LastCallFailCause failCause = (LastCallFailCause)ar.result; causeCode = failCause.causeCode; vendorCause = failCause.vendorCause; }
for (int i = 0, s = mDroppedDuringPoll.size() ; i < s ; i++ ) { CdmaConnection conn = mDroppedDuringPoll.get(i);
conn.onRemoteDisconnect(causeCode, vendorCause); }
updatePhoneState();
mPhone.notifyPreciseCallStateChanged(); mDroppedDuringPoll.clear(); break;
case EVENT_REPOLL_AFTER_DELAY: case EVENT_CALL_STATE_CHANGE: pollCallsWhenSafe(); break;
case EVENT_RADIO_AVAILABLE: handleRadioAvailable(); break;
case EVENT_RADIO_NOT_AVAILABLE: handleRadioNotAvailable(); break;
case EVENT_EXIT_ECM_RESPONSE_CDMA: // no matter the result, we still do the same here if (mPendingCallInEcm) { mCi.dial(mPendingMO.getAddress(), mPendingCallClirMode, obtainCompleteMessage()); mPendingCallInEcm = false; } mPhone.unsetOnEcbModeExitResponse(this); break;
case EVENT_CALL_WAITING_INFO_CDMA: ar = (AsyncResult)msg.obj; if (ar.exception == null) { handleCallWaitingInfo((CdmaCallWaitingNotification)ar.result); Rlog.d(LOG_TAG, "Event EVENT_CALL_WAITING_INFO_CDMA Received"); } break;
case EVENT_THREE_WAY_DIAL_L2_RESULT_CDMA: ar = (AsyncResult)msg.obj; if (ar.exception == null) { // Assume 3 way call is connected mPendingMO.onConnectedInOrOut(); mPendingMO = null; } break;
case EVENT_THREE_WAY_DIAL_BLANK_FLASH: ar = (AsyncResult) msg.obj; if (ar.exception == null) { postDelayed( new Runnable() { public void run() { if (mPendingMO != null) { mCi.sendCDMAFeatureCode(mPendingMO.getAddress(), obtainMessage(EVENT_THREE_WAY_DIAL_L2_RESULT_CDMA)); } } }, m3WayCallFlashDelay); } else { mPendingMO = null; Rlog.w(LOG_TAG, "exception happened on Blank Flash for 3-way call"); } break;
default:{ throw new RuntimeException("unexpected event not handled"); } } } |
进入operationComplete()
private void operationComplete() { mPendingOperations--;
if (DBG_POLL) log("operationComplete: pendingOperations=" + mPendingOperations + ", needsPoll=" + mNeedsPoll);
if (mPendingOperations == 0 && mNeedsPoll) { mLastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT);//创建一个消息 mCi.getCurrentCalls(mLastRelevantPoll);//将消息传递给mCi,从而再次进入RILJ } else if (mPendingOperations < 0) { // this should never happen Rlog.e(LOG_TAG,"CdmaCallTracker.pendingOperations < 0"); mPendingOperations = 0; } } |
进入RIL.java的getCurrentCalls()
public void getCurrentCalls (Message result) { RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_CURRENT_CALLS, result); if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); } |
可以看到这里和之前的处理基本类似,首先获取一个RILRequest对象,再send给RILC。底层查询Call_list状态列表,RILC处理完后,仍然返回到RILJ,同样在processSolicited()中处理,由此可见processSolicited()基本负责了对RILC层返回消息的处理。同样进入CdmaCallTracker中的handleMessage()的Case EVENT_POLL_CALLS_RESULT。(请查看之前代码),由handlePollCalls()处理
protected void handlePollCalls(AsyncResult ar) { List polledCalls;
if (ar.exception == null) { polledCalls = (List)ar.result; } else if (isCommandExceptionRadioNotAvailable(ar.exception)) { // just a dummy empty ArrayList to cause the loop // to hang up all the calls polledCalls = new ArrayList(); } else { // Radio probably wasn't ready--try again in a bit // But don't keep polling if the channel is closed pollCallsAfterDelay(); return; }
Connection newRinging = null; //or waiting Connection newUnknown = null; boolean hasNonHangupStateChanged = false; // Any change besides // a dropped connection boolean hasAnyCallDisconnected = false; boolean needsPollDelay = false; boolean unknownConnectionAppeared = false;
for (int i = 0, curDC = 0, dcSize = polledCalls.size() ; i < mConnections.length; i++) { CdmaConnection 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; } }
if (DBG_POLL) log("poll: conn[i=" + i + "]=" + conn+", dc=" + dc); //conn代表旧连接地基本信息,dc代表新连接 if (conn == null && dc != null) {//拨打第一通电话进入这里 // Connection appeared in CLCC response that we don't know about if (mPendingMO != null && mPendingMO.compareTo(dc)) {
if (DBG_POLL) log("poll: pendingMO=" + mPendingMO);
// It's our pending mobile originating call mConnections[i] = mPendingMO; mPendingMO.mIndex = i; mPendingMO.update(dc); mPendingMO = null;
// Someone has already asked to hangup this call if (mHangupPendingMO) { mHangupPendingMO = false; // Re-start Ecm timer when an uncompleted emergency call ends if (mIsEcmTimerCanceled) { handleEcmTimer(CDMAPhone.RESTART_ECM_TIMER); }
try { if (Phone.DEBUG_PHONE) log( "poll: hangupPendingMO, hangup conn " + i); hangup(mConnections[i]); } catch (CallStateException ex) { Rlog.e(LOG_TAG, "unexpected error on hangup"); }
// Do not continue processing this poll // Wait for hangup and repoll return; } } else { if (Phone.DEBUG_PHONE) { log("pendingMo=" + mPendingMO + ", dc=" + dc); } mConnections[i] = new CdmaConnection(mPhone.getContext(), dc, this, i);
Connection hoConnection = getHoConnection(dc); if (hoConnection != null) { // Single Radio Voice Call Continuity (SRVCC) completed mConnections[i].migrateFrom(hoConnection); mHandoverConnections.remove(hoConnection); mPhone.notifyHandoverStateChanged(mConnections[i]); } else { // 检测MT call是不是个新来电或者Unknwon connection newRinging = checkMtFindNewRinging(dc,i); if (newRinging == null) { unknownConnectionAppeared = true; newUnknown = mConnections[i]; } } checkAndEnableDataCallAfterEmergencyCallDropped(); } hasNonHangupStateChanged = true; } else if (conn != null && dc == null) { // This case means the RIL has no more active call anymore and // we need to clean up the foregroundCall and ringingCall. // Loop through foreground call connections as // it contains the known logical connections. int count = mForegroundCall.mConnections.size(); for (int n = 0; n < count; n++) { if (Phone.DEBUG_PHONE) log("adding fgCall cn " + n + " to droppedDuringPoll"); CdmaConnection cn = (CdmaConnection)mForegroundCall.mConnections.get(n); mDroppedDuringPoll.add(cn); } count = mRingingCall.mConnections.size(); // Loop through ringing call connections as // it may contain the known logical connections. for (int n = 0; n < count; n++) { if (Phone.DEBUG_PHONE) log("adding rgCall cn " + n + " to droppedDuringPoll"); CdmaConnection cn = (CdmaConnection)mRingingCall.mConnections.get(n); mDroppedDuringPoll.add(cn); } mForegroundCall.setGeneric(false); mRingingCall.setGeneric(false);
// Re-start Ecm timer when the connected emergency call ends if (mIsEcmTimerCanceled) { handleEcmTimer(CDMAPhone.RESTART_ECM_TIMER); } // If emergency call is not going through while dialing checkAndEnableDataCallAfterEmergencyCallDropped();
// Dropped connections are removed from the CallTracker // list but kept in the Call list mConnections[i] = null; } else if (conn != null && dc != null) { /* implicit conn.compareTo(dc) */ // 来去电冲突处理 if (conn.isIncoming() != dc.isMT) { if (dc.isMT == true){ // 来电优先,此时并不会出现双方都放弃连接的问题,因为MO还没有发至RIL mDroppedDuringPoll.add(conn); // 检测MT call是不是新来电或者unknown connection newRinging = checkMtFindNewRinging(dc,i); if (newRinging == null) { unknownConnectionAppeared = true; newUnknown = conn; } checkAndEnableDataCallAfterEmergencyCallDropped(); } else { // Call info stored in conn is not consistent with the call info from dc. // We should follow the rule of MT calls taking precedence over MO calls // when there is conflict, so here we drop the call info from dc and // continue to use the call info from conn, and only take a log. Rlog.e(LOG_TAG,"Error in RIL, Phantom call appeared " + dc); } } else { boolean changed; changed = conn.update(dc); hasNonHangupStateChanged = hasNonHangupStateChanged || changed; } }
if (REPEAT_POLLING) { if (dc != null) { // FIXME with RIL, we should not need this anymore if ((dc.state == DriverCall.State.DIALING /*&& cm.getOption(cm.OPTION_POLL_DIALING)*/) || (dc.state == DriverCall.State.ALERTING /*&& cm.getOption(cm.OPTION_POLL_ALERTING)*/) || (dc.state == DriverCall.State.INCOMING /*&& cm.getOption(cm.OPTION_POLL_INCOMING)*/) || (dc.state == DriverCall.State.WAITING /*&& cm.getOption(cm.OPTION_POLL_WAITING)*/) ) { // Sometimes there's no unsolicited notification // for state transitions needsPollDelay = true; } } } }
// This is the first poll after an ATD. // We expect the pending call to appear in the list // If it does not, we land here if (mPendingMO != null) { Rlog.d(LOG_TAG,"Pending MO dropped before poll fg state:" + mForegroundCall.getState());
mDroppedDuringPoll.add(mPendingMO); mPendingMO = null; mHangupPendingMO = false; 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 for (int i = mDroppedDuringPoll.size() - 1; i >= 0 ; i--) { CdmaConnection conn = mDroppedDuringPoll.get(i);
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); } else if (conn.mCause == DisconnectCause.LOCAL || conn.mCause == DisconnectCause.INVALID_NUMBER) { mDroppedDuringPoll.remove(i); hasAnyCallDisconnected |= conn.onDisconnect(conn.mCause); } }
/* Disconnect any pending Handover connections */ for (Connection hoConnection : mHandoverConnections) { log("handlePollCalls - disconnect hoConn= " + hoConnection.toString()); ((ImsPhoneConnection)hoConnection).onDisconnect(DisconnectCause.NOT_VALID); mHandoverConnections.remove(hoConnection); }
// Any non-local disconnects: determine cause if (mDroppedDuringPoll.size() > 0) { mCi.getLastCallFailCause( obtainNoPollCompleteMessage(EVENT_GET_LAST_CALL_FAIL_CAUSE)); }
if (needsPollDelay) { pollCallsAfterDelay(); }
// Cases when we can no longer keep disconnected Connection's // with their previous calls // 1) the phone has started to ring // 2) A Call/Connection object has changed state... // we may have switched or held or answered (but not hung up) if (newRinging != null || hasNonHangupStateChanged || hasAnyCallDisconnected) { internalClearDisconnected(); }
updatePhoneState();
if (unknownConnectionAppeared) { mPhone.notifyUnknownConnection(newUnknown); }
if (hasNonHangupStateChanged || newRinging != null || hasAnyCallDisconnected) { mPhone.notifyPreciseCallStateChanged(); }
//dumpState(); } |
接着是RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED消息的处理,同样的过程,其交
由RIL.processUnsolicited()处理。可以看到此方法与之前的processSolicited很类似。
private void processUnsolicited (Parcel p) { int response; Object ret;
response = p.readInt();
try {switch(response) { /* cat libs/telephony/ril_unsol_commands.h \ | egrep "^ *{RIL_" \ | sed -re 's/\{([^,]+),[^,]+,([^}]+).+/case \1: \2(rr, p); break;/' */
case RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED: ret = responseVoid(p); break; case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED: ret = responseVoid(p); break; case RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED: ret = responseVoid(p); break; case RIL_UNSOL_RESPONSE_NEW_SMS: ret = responseString(p); break; case RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT: ret = responseString(p); break; case RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM: ret = responseInts(p); break; case RIL_UNSOL_ON_USSD: ret = responseStrings(p); break; case RIL_UNSOL_NITZ_TIME_RECEIVED: ret = responseString(p); break; case RIL_UNSOL_SIGNAL_STRENGTH: ret = responseSignalStrength(p); break; case RIL_UNSOL_DATA_CALL_LIST_CHANGED: ret = responseDataCallList(p);break; case RIL_UNSOL_SUPP_SVC_NOTIFICATION: ret = responseSuppServiceNotification(p); break; case RIL_UNSOL_STK_SESSION_END: ret = responseVoid(p); break; case RIL_UNSOL_STK_PROACTIVE_COMMAND: ret = responseString(p); break; case RIL_UNSOL_STK_EVENT_NOTIFY: ret = responseString(p); break; case RIL_UNSOL_STK_CALL_SETUP: ret = responseInts(p); break; case RIL_UNSOL_SIM_SMS_STORAGE_FULL: ret = responseVoid(p); break; case RIL_UNSOL_SIM_REFRESH: ret = responseSimRefresh(p); break; case RIL_UNSOL_CALL_RING: ret = responseCallRing(p); break; case RIL_UNSOL_RESTRICTED_STATE_CHANGED: ret = responseInts(p); break; case RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED: ret = responseVoid(p); break; case RIL_UNSOL_RESPONSE_CDMA_NEW_SMS: ret = responseCdmaSms(p); break; case RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS: ret = responseRaw(p); break; case RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL: ret = responseVoid(p); break; case RIL_UNSOL_ENTER_EMERGENCY_CALLBACK_MODE: ret = responseVoid(p); break; case RIL_UNSOL_CDMA_CALL_WAITING: ret = responseCdmaCallWaiting(p); break; case RIL_UNSOL_CDMA_OTA_PROVISION_STATUS: ret = responseInts(p); break; case RIL_UNSOL_CDMA_INFO_REC: ret = responseCdmaInformationRecord(p); break; case RIL_UNSOL_OEM_HOOK_RAW: ret = responseRaw(p); break; case RIL_UNSOL_RINGBACK_TONE: ret = responseInts(p); break; case RIL_UNSOL_RESEND_INCALL_MUTE: ret = responseVoid(p); break; case RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED: ret = responseInts(p); break; case RIL_UNSOl_CDMA_PRL_CHANGED: ret = responseInts(p); break; case RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE: ret = responseVoid(p); break; case RIL_UNSOL_RIL_CONNECTED: ret = responseInts(p); break; case RIL_UNSOL_VOICE_RADIO_TECH_CHANGED: ret = responseInts(p); break; case RIL_UNSOL_CELL_INFO_LIST: ret = responseCellInfoList(p); break; case RIL_UNSOL_RESPONSE_IMS_NETWORK_STATE_CHANGED: ret = responseVoid(p); break; case RIL_UNSOL_UICC_SUBSCRIPTION_STATUS_CHANGED: ret = responseInts(p); break; case RIL_UNSOL_SRVCC_STATE_NOTIFY: ret = responseInts(p); break; case RIL_UNSOL_HARDWARE_CONFIG_CHANGED: ret = responseHardwareConfig(p); break; case RIL_UNSOL_RADIO_CAPABILITY: ret = responseRadioCapability(p); break; case RIL_UNSOL_ON_SS: ret = responseSsData(p); break; case RIL_UNSOL_STK_CC_ALPHA_NOTIFY: ret = responseString(p); break; case RIL_UNSOL_LCEDATA_RECV: ret = responseLceData(p); break;
default: throw new RuntimeException("Unrecognized unsol response: " + response); //break; (implied) }} catch (Throwable tr) { Rlog.e(RILJ_LOG_TAG, "Exception processing unsol response: " + response + "Exception:" + tr.toString()); return; }
switch(response) { case RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED: /* has bonus radio state int */ RadioState newState = getRadioStateFromInt(p.readInt()); if (RILJ_LOGD) unsljLogMore(response, newState.toString());
switchToRadioState(newState); break; case RIL_UNSOL_RESPONSE_IMS_NETWORK_STATE_CHANGED: if (RILJ_LOGD) unsljLog(response);
mImsNetworkStateChangedRegistrants .notifyRegistrants(new AsyncResult(null, null, null)); break; case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED: if (RILJ_LOGD) unsljLog(response);
mCallStateRegistrants//发出通知(RegistrantList消息处理机制) .notifyRegistrants(new AsyncResult(null, null, null)); break; case RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED: if (RILJ_LOGD) unsljLog(response);
mVoiceNetworkStateRegistrants .notifyRegistrants(new AsyncResult(null, null, null)); break; case RIL_UNSOL_RESPONSE_NEW_SMS: { if (RILJ_LOGD) unsljLog(response);
// FIXME this should move up a layer String a[] = new String[2];
a[1] = (String)ret;
SmsMessage sms;
sms = SmsMessage.newFromCMT(a); if (mGsmSmsRegistrant != null) { mGsmSmsRegistrant .notifyRegistrant(new AsyncResult(null, sms, null)); } break; } case RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT: if (RILJ_LOGD) unsljLogRet(response, ret);
if (mSmsStatusRegistrant != null) { mSmsStatusRegistrant.notifyRegistrant( new AsyncResult(null, ret, null)); } break; case RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM: if (RILJ_LOGD) unsljLogRet(response, ret);
int[] smsIndex = (int[])ret;
if(smsIndex.length == 1) { if (mSmsOnSimRegistrant != null) { mSmsOnSimRegistrant. notifyRegistrant(new AsyncResult(null, smsIndex, null)); } } else { if (RILJ_LOGD) riljLog(" NEW_SMS_ON_SIM ERROR with wrong length " + smsIndex.length); } break; case RIL_UNSOL_ON_USSD: String[] resp = (String[])ret;
if (resp.length < 2) { resp = new String[2]; resp[0] = ((String[])ret)[0]; resp[1] = null; } if (RILJ_LOGD) unsljLogMore(response, resp[0]); if (mUSSDRegistrant != null) { mUSSDRegistrant.notifyRegistrant( new AsyncResult (null, resp, null)); } break; case RIL_UNSOL_NITZ_TIME_RECEIVED: if (RILJ_LOGD) unsljLogRet(response, ret);
// has bonus long containing milliseconds since boot that the NITZ // time was received long nitzReceiveTime = p.readLong();
Object[] result = new Object[2];
result[0] = ret; result[1] = Long.valueOf(nitzReceiveTime);
boolean ignoreNitz = SystemProperties.getBoolean( TelephonyProperties.PROPERTY_IGNORE_NITZ, false);
if (ignoreNitz) { if (RILJ_LOGD) riljLog("ignoring UNSOL_NITZ_TIME_RECEIVED"); } else { if (mNITZTimeRegistrant != null) {
mNITZTimeRegistrant .notifyRegistrant(new AsyncResult (null, result, null)); } // in case NITZ time registrant isn't registered yet, or a new registrant // registers later mLastNITZTimeInfo = result; } break;
case RIL_UNSOL_SIGNAL_STRENGTH: // Note this is set to "verbose" because it happens // frequently if (RILJ_LOGV) unsljLogvRet(response, ret);
if (mSignalStrengthRegistrant != null) { mSignalStrengthRegistrant.notifyRegistrant( new AsyncResult (null, ret, null)); } break; case RIL_UNSOL_DATA_CALL_LIST_CHANGED: if (RILJ_LOGD) unsljLogRet(response, ret);
mDataNetworkStateRegistrants.notifyRegistrants(new AsyncResult(null, ret, null)); break;
case RIL_UNSOL_SUPP_SVC_NOTIFICATION: if (RILJ_LOGD) unsljLogRet(response, ret);
if (mSsnRegistrant != null) { mSsnRegistrant.notifyRegistrant( new AsyncResult (null, ret, null)); } break;
case RIL_UNSOL_STK_SESSION_END: if (RILJ_LOGD) unsljLog(response);
if (mCatSessionEndRegistrant != null) { mCatSessionEndRegistrant.notifyRegistrant( new AsyncResult (null, ret, null)); } break;
case RIL_UNSOL_STK_PROACTIVE_COMMAND: if (RILJ_LOGD) unsljLog(response);
if (mCatProCmdRegistrant != null) { mCatProCmdRegistrant.notifyRegistrant( new AsyncResult (null, ret, null)); } break;
case RIL_UNSOL_STK_EVENT_NOTIFY: if (RILJ_LOGD) unsljLog(response);
if (mCatEventRegistrant != null) { mCatEventRegistrant.notifyRegistrant( new AsyncResult (null, ret, null)); } break;
case RIL_UNSOL_STK_CALL_SETUP: if (RILJ_LOGD) unsljLogRet(response, ret);
if (mCatCallSetUpRegistrant != null) { mCatCallSetUpRegistrant.notifyRegistrant( new AsyncResult (null, ret, null)); } break;
case RIL_UNSOL_SIM_SMS_STORAGE_FULL: if (RILJ_LOGD) unsljLog(response);
if (mIccSmsFullRegistrant != null) { mIccSmsFullRegistrant.notifyRegistrant(); } break;
case RIL_UNSOL_SIM_REFRESH: if (RILJ_LOGD) unsljLogRet(response, ret);
if (mIccRefreshRegistrants != null) { mIccRefreshRegistrants.notifyRegistrants( new AsyncResult (null, ret, null)); } break;
case RIL_UNSOL_CALL_RING: if (RILJ_LOGD) unsljLogRet(response, ret);
if (mRingRegistrant != null) { mRingRegistrant.notifyRegistrant( new AsyncResult (null, ret, null)); } break;
case RIL_UNSOL_RESTRICTED_STATE_CHANGED: if (RILJ_LOGD) unsljLogvRet(response, ret); if (mRestrictedStateRegistrant != null) { mRestrictedStateRegistrant.notifyRegistrant( new AsyncResult (null, ret, null)); } break;
case RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED: if (RILJ_LOGD) unsljLog(response);
if (mIccStatusChangedRegistrants != null) { mIccStatusChangedRegistrants.notifyRegistrants(); } break;
case RIL_UNSOL_RESPONSE_CDMA_NEW_SMS: if (RILJ_LOGD) unsljLog(response);
SmsMessage sms = (SmsMessage) ret;
if (mCdmaSmsRegistrant != null) { mCdmaSmsRegistrant .notifyRegistrant(new AsyncResult(null, sms, null)); } break;
case RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS: if (RILJ_LOGD) unsljLogvRet(response, IccUtils.bytesToHexString((byte[])ret));
if (mGsmBroadcastSmsRegistrant != null) { mGsmBroadcastSmsRegistrant .notifyRegistrant(new AsyncResult(null, ret, null)); } break;
case RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL: if (RILJ_LOGD) unsljLog(response);
if (mIccSmsFullRegistrant != null) { mIccSmsFullRegistrant.notifyRegistrant(); } break;
case RIL_UNSOL_ENTER_EMERGENCY_CALLBACK_MODE: if (RILJ_LOGD) unsljLog(response);
if (mEmergencyCallbackModeRegistrant != null) { mEmergencyCallbackModeRegistrant.notifyRegistrant(); } break;
case RIL_UNSOL_CDMA_CALL_WAITING: if (RILJ_LOGD) unsljLogRet(response, ret);
if (mCallWaitingInfoRegistrants != null) { mCallWaitingInfoRegistrants.notifyRegistrants( new AsyncResult (null, ret, null)); } break;
case RIL_UNSOL_CDMA_OTA_PROVISION_STATUS: if (RILJ_LOGD) unsljLogRet(response, ret);
if (mOtaProvisionRegistrants != null) { mOtaProvisionRegistrants.notifyRegistrants( new AsyncResult (null, ret, null)); } break;
case RIL_UNSOL_CDMA_INFO_REC: ArrayList<CdmaInformationRecords> listInfoRecs;
try { listInfoRecs = (ArrayList<CdmaInformationRecords>)ret; } catch (ClassCastException e) { Rlog.e(RILJ_LOG_TAG, "Unexpected exception casting to listInfoRecs", e); break; }
for (CdmaInformationRecords rec : listInfoRecs) { if (RILJ_LOGD) unsljLogRet(response, rec); notifyRegistrantsCdmaInfoRec(rec); } break;
case RIL_UNSOL_OEM_HOOK_RAW: if (RILJ_LOGD) unsljLogvRet(response, IccUtils.bytesToHexString((byte[]) ret)); if (mUnsolOemHookRawRegistrant != null) { mUnsolOemHookRawRegistrant.notifyRegistrant(new AsyncResult(null, ret, null)); } break;
case RIL_UNSOL_RINGBACK_TONE: if (RILJ_LOGD) unsljLogvRet(response, ret); if (mRingbackToneRegistrants != null) { boolean playtone = (((int[])ret)[0] == 1); mRingbackToneRegistrants.notifyRegistrants( new AsyncResult (null, playtone, null)); } break;
case RIL_UNSOL_RESEND_INCALL_MUTE: if (RILJ_LOGD) unsljLogRet(response, ret);
if (mResendIncallMuteRegistrants != null) { mResendIncallMuteRegistrants.notifyRegistrants( new AsyncResult (null, ret, null)); } break;
case RIL_UNSOL_VOICE_RADIO_TECH_CHANGED: if (RILJ_LOGD) unsljLogRet(response, ret);
if (mVoiceRadioTechChangedRegistrants != null) { mVoiceRadioTechChangedRegistrants.notifyRegistrants( new AsyncResult(null, ret, null)); } break;
case RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED: if (RILJ_LOGD) unsljLogRet(response, ret);
if (mCdmaSubscriptionChangedRegistrants != null) { mCdmaSubscriptionChangedRegistrants.notifyRegistrants( new AsyncResult (null, ret, null)); } break;
case RIL_UNSOl_CDMA_PRL_CHANGED: if (RILJ_LOGD) unsljLogRet(response, ret);
if (mCdmaPrlChangedRegistrants != null) { mCdmaPrlChangedRegistrants.notifyRegistrants( new AsyncResult (null, ret, null)); } break;
case RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE: if (RILJ_LOGD) unsljLogRet(response, ret);
if (mExitEmergencyCallbackModeRegistrants != null) { mExitEmergencyCallbackModeRegistrants.notifyRegistrants( new AsyncResult (null, null, null)); } break;
case RIL_UNSOL_RIL_CONNECTED: { if (RILJ_LOGD) unsljLogRet(response, ret);
// Initial conditions setRadioPower(false, null); setCdmaSubscriptionSource(mCdmaSubscription, null); setCellInfoListRate(Integer.MAX_VALUE, null); notifyRegistrantsRilConnectionChanged(((int[])ret)[0]); break; } case RIL_UNSOL_CELL_INFO_LIST: { if (RILJ_LOGD) unsljLogRet(response, ret);
if (mRilCellInfoListRegistrants != null) { mRilCellInfoListRegistrants.notifyRegistrants( new AsyncResult (null, ret, null)); } break; } case RIL_UNSOL_UICC_SUBSCRIPTION_STATUS_CHANGED: { if (RILJ_LOGD) unsljLogRet(response, ret);
if (mSubscriptionStatusRegistrants != null) { mSubscriptionStatusRegistrants.notifyRegistrants( new AsyncResult (null, ret, null)); } break; } case RIL_UNSOL_SRVCC_STATE_NOTIFY: { if (RILJ_LOGD) unsljLogRet(response, ret);
if (mSrvccStateRegistrants != null) { mSrvccStateRegistrants .notifyRegistrants(new AsyncResult(null, ret, null)); } break; } case RIL_UNSOL_HARDWARE_CONFIG_CHANGED: if (RILJ_LOGD) unsljLogRet(response, ret);
if (mHardwareConfigChangeRegistrants != null) { mHardwareConfigChangeRegistrants.notifyRegistrants( new AsyncResult (null, ret, null)); } break; case RIL_UNSOL_RADIO_CAPABILITY: if (RILJ_LOGD) unsljLogRet(response, ret);
if (mPhoneRadioCapabilityChangedRegistrants != null) { mPhoneRadioCapabilityChangedRegistrants.notifyRegistrants( new AsyncResult(null, ret, null)); } break; case RIL_UNSOL_ON_SS: if (RILJ_LOGD) unsljLogRet(response, ret);
if (mSsRegistrant != null) { mSsRegistrant.notifyRegistrant( new AsyncResult (null, ret, null)); } break; case RIL_UNSOL_STK_CC_ALPHA_NOTIFY: if (RILJ_LOGD) unsljLogRet(response, ret);
if (mCatCcAlphaRegistrant != null) { mCatCcAlphaRegistrant.notifyRegistrant( new AsyncResult (null, ret, null)); } break; case RIL_UNSOL_LCEDATA_RECV: if (RILJ_LOGD) unsljLogRet(response, ret);
if (mLceInfoRegistrant != null) { mLceInfoRegistrant.notifyRegistrant(new AsyncResult(null, ret, null)); } break; } } |
经过分析,EVENT_CALL_STATE_CHANGE消息将在CdmaCallTracker.java文件中的handleMessage()方法中被处理,这和之前的消息处理都类似。(请查看之前CdmaCallTracker代码)。发现最终的处理函数为pollCallsWhenSafe()。如下:
protected void pollCallsWhenSafe() { mNeedsPoll = true;
if (checkNoOperationsPending()) { mLastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT); mCi.getCurrentCalls(mLastRelevantPoll);//与之前一样的方法调用 } } |
可看到之前基本类似的流程。之后的很多消息都是通过类似的过程进行处理的。在此就不予赘述。
1.6 RIL
请看本博客另一篇关于RIL的博文http://blog.csdn.net/a34140974/article/details/50578324
1.7 小结
大概的调用路径如下图所示(中间省略很多):
有个小错误(左下大框应应该为TeleComFrameWork)
应该注意到的是这些箭头只代表了逻辑的走向,并不一定是程序的真正流向。尤其是对于android系统的中很多系统服务都是以C/S、RPC方式提供的,进程或线程空间的切换分析对于理解整个应用的流程变得尤为重要。以下作图并不严谨,仅供参考。
总结一下就是Dialer→TeleComService→TeleComFramework→TeleponyService→TelephonyFramework→RIL