Android4.0(Phone)拨号启动过程分析(三)与Framework层通信
由于Android几乎所有的代码都是公开的,如果要对Framework层分析就必需先拿到Framework层的代码,我在前面已经搭建好了ubuntu14.04的环境,下载好了Android4.0的源码,其中也包括了Framework层和Package的代码,导出到宿主机Windows XP中用Source Insight 3.5工具来查看源码,Package中的代码可以导入到Eclipse下查看,我是把frameworks\base整个目录都导入到Source Insight 3.5工程中,可以选择我们需要的目录导入,如frameworks\base\media\tests\contents\media_api目录下有很多音、视频文件,可以只导入frameworks\base\core、frameworks\base\telephony、frameworks\base\services、frameworks\base\include等目录。需要代码的可以到这下载:http://pan.baidu.com/s/1i3KczeX在PhoneApp初始化时,有以下代码
@Override public void onCreate() { //........... if (phone == null) { // 初始化phone frameworks层 PhoneFactory.makeDefaultPhones(this); // 获取默认的phone对象 phone = PhoneFactory.getDefaultPhone(); mCM = CallManager.getInstance(); mCM.registerPhone(phone); //............. } //............... }在应用层的PhoneApp中调用PhoneFactory的静态方法makeDefaultPhones创建一个默认的Phone对象,而framework中采用的是代理模式和工厂模式实现,在makedefaultPhone中
//***** Class Methods public static void makeDefaultPhones(Context context) { makeDefaultPhone(context); } /** * FIXME replace this with some other way of making these * instances */ public static void makeDefaultPhone(Context context) { synchronized(Phone.class) { if (!sMadeDefaults) { sLooper = Looper.myLooper(); sContext = context; if (sLooper == null) { throw new RuntimeException( "PhoneFactory.makeDefaultPhone must be called from Looper thread"); } int retryCount = 0; for(;;) { boolean hasException = false; retryCount ++; try { // use UNIX domain socket to // prevent subsequent initialization new LocalServerSocket("com.android.internal.telephony"); } catch (java.io.IOException ex) { hasException = true; } if ( !hasException ) { break; } else if (retryCount > SOCKET_OPEN_MAX_RETRY) { throw new RuntimeException("PhoneFactory probably already running"); } else { try { Thread.sleep(SOCKET_OPEN_RETRY_MILLIS); } catch (InterruptedException er) { } } } sPhoneNotifier = new DefaultPhoneNotifier(); // Get preferred network mode int preferredNetworkMode = RILConstants.PREFERRED_NETWORK_MODE; if (BaseCommands.getLteOnCdmaModeStatic() == Phone.LTE_ON_CDMA_TRUE) { preferredNetworkMode = Phone.NT_MODE_GLOBAL; } int networkMode = Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.PREFERRED_NETWORK_MODE, preferredNetworkMode); Log.i(LOG_TAG, "Network Mode set to " + Integer.toString(networkMode)); // Get cdmaSubscription // TODO: Change when the ril will provides a way to know at runtime // the configuration, bug 4202572. And the ril issues the // RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED, bug 4295439. int cdmaSubscription; int lteOnCdma = BaseCommands.getLteOnCdmaModeStatic(); switch (lteOnCdma) { case Phone.LTE_ON_CDMA_FALSE: cdmaSubscription = RILConstants.SUBSCRIPTION_FROM_NV; Log.i(LOG_TAG, "lteOnCdma is 0 use SUBSCRIPTION_FROM_NV"); break; case Phone.LTE_ON_CDMA_TRUE: cdmaSubscription = RILConstants.SUBSCRIPTION_FROM_RUIM; Log.i(LOG_TAG, "lteOnCdma is 1 use SUBSCRIPTION_FROM_RUIM"); break; case Phone.LTE_ON_CDMA_UNKNOWN: default: //Get cdmaSubscription mode from Settings.System cdmaSubscription = Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.PREFERRED_CDMA_SUBSCRIPTION, preferredCdmaSubscription); Log.i(LOG_TAG, "lteOnCdma not set, using PREFERRED_CDMA_SUBSCRIPTION"); break; } Log.i(LOG_TAG, "Cdma Subscription set to " + cdmaSubscription); //reads the system properties and makes commandsinterface sCommandsInterface = new RIL(context, networkMode, cdmaSubscription); int phoneType = getPhoneType(networkMode); if (phoneType == Phone.PHONE_TYPE_GSM) { Log.i(LOG_TAG, "Creating GSMPhone"); sProxyPhone = new PhoneProxy(new GSMPhone(context, sCommandsInterface, sPhoneNotifier)); } else if (phoneType == Phone.PHONE_TYPE_CDMA) { switch (BaseCommands.getLteOnCdmaModeStatic()) { case Phone.LTE_ON_CDMA_TRUE: Log.i(LOG_TAG, "Creating CDMALTEPhone"); sProxyPhone = new PhoneProxy(new CDMALTEPhone(context, sCommandsInterface, sPhoneNotifier)); break; case Phone.LTE_ON_CDMA_FALSE: default: Log.i(LOG_TAG, "Creating CDMAPhone"); sProxyPhone = new PhoneProxy(new CDMAPhone(context, sCommandsInterface, sPhoneNotifier)); break; } } sMadeDefaults = true; } } }在PhoneFactory.java类中定义了
static private CommandsInterface sCommandsInterface = null; //reads the system properties and makes commandsinterface sCommandsInterface = new RIL(context, networkMode, cdmaSubscription);所以后面用到的CommandsInterface对象都是RIL类,由于RIL实现了CommandsInterface
public final class RIL extends BaseCommands implements CommandsInterface {}
对于什么是RIL,百度上的解释如下,后面有时间再去分析RIL类。
Android上的RIL
Android是目前最流行智能手机操作系统之一,Android的RIL位于应用程序框架与内核之间,分成了两个部分,一个部分是rild,它负责socket与应用程序框架进行通信。另外一个部分是Vendor RIL,这个部分负责向下是通过两种方式与radio进行通信,它们是直接与radio通信的AT指令通道和用于传输包数据的通道,数据通道用于手机的上网功能。对于RIL的java框架部分,也被分成了两个部分,一个是RIL模块,这个模块主要用于与下层的rild进行通信,另外一个是Phone模块,这个模块直接暴露电话功能接口给应用开发用户,供他们调用以进行电话功能的实现。
在以下代码中根据不同的类型创建Phone,如GSM(2G中国移动和联通)、CDMA(中国电信)等,采用了向上转型,向上转型是安全的。
int phoneType = getPhoneType(networkMode); if (phoneType == Phone.PHONE_TYPE_GSM) { Log.i(LOG_TAG, "Creating GSMPhone"); sProxyPhone = new PhoneProxy(new GSMPhone(context, sCommandsInterface, sPhoneNotifier)); } else if (phoneType == Phone.PHONE_TYPE_CDMA) { switch (BaseCommands.getLteOnCdmaModeStatic()) { case Phone.LTE_ON_CDMA_TRUE: Log.i(LOG_TAG, "Creating CDMALTEPhone"); sProxyPhone = new PhoneProxy(new CDMALTEPhone(context, sCommandsInterface, sPhoneNotifier)); break; case Phone.LTE_ON_CDMA_FALSE: default: Log.i(LOG_TAG, "Creating CDMAPhone"); sProxyPhone = new PhoneProxy(new CDMAPhone(context, sCommandsInterface, sPhoneNotifier)); break; } }GSMPhone和CDMAPhone都继承了PhoneBase,以下分析默认创建的是GSMPhone;在GSMPhone.java的构造函数中
// Constructors public GSMPhone (Context context, CommandsInterface ci, PhoneNotifier notifier) { this(context,ci,notifier, false); } public GSMPhone (Context context, CommandsInterface ci, PhoneNotifier notifier, boolean unitTestMode) { super(notifier, context, ci, unitTestMode); if (ci instanceof SimulatedRadioControl) { mSimulatedRadioControl = (SimulatedRadioControl) ci; } mCM.setPhoneType(Phone.PHONE_TYPE_GSM); mCT = new GsmCallTracker(this); //通话管理 mSST = new GsmServiceStateTracker (this); mSMS = new GsmSMSDispatcher(this, mSmsStorageMonitor, mSmsUsageMonitor); mIccFileHandler = new SIMFileHandler(this); mIccRecords = new SIMRecords(this); mDataConnectionTracker = new GsmDataConnectionTracker (this); mIccCard = new SimCard(this); if (!unitTestMode) { mSimPhoneBookIntManager = new SimPhoneBookInterfaceManager(this); mSimSmsIntManager = new SimSmsInterfaceManager(this, mSMS); mSubInfo = new PhoneSubInfo(this); } mStkService = CatService.getInstance(mCM, mIccRecords, mContext, mIccFileHandler, mIccCard); mCM.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null); mIccRecords.registerForRecordsLoaded(this, EVENT_SIM_RECORDS_LOADED, null); mCM.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null); mCM.registerForOn(this, EVENT_RADIO_ON, null); mCM.setOnUSSD(this, EVENT_USSD, null); mCM.setOnSuppServiceNotification(this, EVENT_SSN, null); mSST.registerForNetworkAttached(this, EVENT_REGISTERED_TO_NETWORK, null); if (false) { try { //debugSocket = new LocalServerSocket("com.android.internal.telephony.debug"); debugSocket = new ServerSocket(); debugSocket.setReuseAddress(true); debugSocket.bind (new InetSocketAddress("127.0.0.1", 6666)); debugPortThread = new Thread( new Runnable() { public void run() { for(;;) { try { Socket sock; sock = debugSocket.accept(); Log.i(LOG_TAG, "New connection; resetting radio"); mCM.resetRadio(null); sock.close(); } catch (IOException ex) { Log.w(LOG_TAG, "Exception accepting socket", ex); } } } }, "GSMPhone debug"); debugPortThread.start(); } catch (IOException ex) { Log.w(LOG_TAG, "Failure to open com.android.internal.telephony.debug socket", ex); } } //Change the system property SystemProperties.set(TelephonyProperties.CURRENT_ACTIVE_PHONE, new Integer(Phone.PHONE_TYPE_GSM).toString()); }在这里创建了通话管理类GsmCallTracker mCT = new GsmCallTracker(this); //通话管理
在构造时,监听EVENT_CALL_STATE_CHANGE标记
//***** Constructors GsmCallTracker (GSMPhone phone) { this.phone = phone; cm = phone.mCM; cm.registerForCallStateChanged(this, EVENT_CALL_STATE_CHANGE, null); cm.registerForOn(this, EVENT_RADIO_AVAILABLE, null); cm.registerForNotAvailable(this, EVENT_RADIO_NOT_AVAILABLE, null); }由于GsmCallTracker的父类继承了Handler,有以下方法
//****** Overridden from Handler public void handleMessage (Message msg) { AsyncResult ar; switch (msg.what) { case EVENT_POLL_CALLS_RESULT: ar = (AsyncResult)msg.obj; if (msg == lastRelevantPoll) { if (DBG_POLL) log( "handle EVENT_POLL_CALL_RESULT: set needsPoll=F"); needsPoll = false; lastRelevantPoll = null; handlePollCalls((AsyncResult)msg.obj); } break; case EVENT_OPERATION_COMPLETE: ar = (AsyncResult)msg.obj; operationComplete(); break; case EVENT_SWITCH_RESULT: case EVENT_CONFERENCE_RESULT: case EVENT_SEPARATE_RESULT: case EVENT_ECT_RESULT: ar = (AsyncResult)msg.obj; if (ar.exception != null) { phone.notifySuppServiceFailed(getFailedService(msg.what)); } operationComplete(); break; case EVENT_GET_LAST_CALL_FAIL_CAUSE: int causeCode; ar = (AsyncResult)msg.obj; operationComplete(); if (ar.exception != null) { // An exception occurred...just treat the disconnect // cause as "normal" causeCode = CallFailCause.NORMAL_CLEARING; Log.i(LOG_TAG, "Exception during getLastCallFailCause, assuming normal disconnect"); } else { causeCode = ((int[])ar.result)[0]; } // Log the causeCode if its not normal if (causeCode == CallFailCause.NO_CIRCUIT_AVAIL || causeCode == CallFailCause.TEMPORARY_FAILURE || causeCode == CallFailCause.SWITCHING_CONGESTION || causeCode == CallFailCause.CHANNEL_NOT_AVAIL || causeCode == CallFailCause.QOS_NOT_AVAIL || causeCode == CallFailCause.BEARER_NOT_AVAIL || causeCode == CallFailCause.ERROR_UNSPECIFIED) { GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation()); EventLog.writeEvent(EventLogTags.CALL_DROP, causeCode, loc != null ? loc.getCid() : -1, TelephonyManager.getDefault().getNetworkType()); } for (int i = 0, s = droppedDuringPoll.size() ; i < s ; i++ ) { GsmConnection conn = droppedDuringPoll.get(i); conn.onRemoteDisconnect(causeCode); } updatePhoneState(); phone.notifyPreciseCallStateChanged(); droppedDuringPoll.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_CALL_STATE_CHANGE: 去获取当前的状态
protected void pollCallsWhenSafe() { needsPoll = true; if (checkNoOperationsPending()) { lastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT); cm.getCurrentCalls(lastRelevantPoll); } }在RIL.java类中的实现
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); } private void send(RILRequest rr) { Message msg; if (mSocket == null) { rr.onError(RADIO_NOT_AVAILABLE, null); rr.release(); return; } msg = mSender.obtainMessage(EVENT_SEND, rr); acquireWakeLock(); msg.sendToTarget(); }在这里通知应用层改变状态
事件通知流程
为了加深理解,我也自己写了一个例子,在GsmCallTracker开一个线程去随机模拟电话状态的改变,程序相当简单
程序目录结构
在PhoneApp中做一些全局的初始化工作
package com.dzt.phonemsg; import android.app.Application; import android.util.Log; import com.dzt.phonemsg.framework.CallManager; import com.dzt.phonemsg.framework.Phone; /** * 演示Phone程序中的事件传递,由于Phone应用程序在代码跟踪时不是很方便, * 并且Phone的消息通讯也比较复杂,就自己把部分代码拿出来模拟Handler的消息传递,用到了觀察者模式 * * @author Administrator * @date 2014.08.01 */ public class PhoneApp extends Application { private static final String TAG = "PhoneApp_dzt"; private static final boolean mIsShowLog = true; static PhoneApp instance = null; Phone phone = new Phone(); CallManager mCM; @Override public void onCreate() { // TODO Auto-generated method stub super.onCreate(); instance = this; mCM = CallManager.getInstance(); mCM.registerPhone(phone); print_i("PhoneApp", "onCreate"); } /** * Returns the singleton instance of the PhoneApp. */ static PhoneApp getInstance() { return instance; } static boolean isRunning() { if (instance != null) return true; return false; } /** * 打印消息 * * @param pkg * @param msg */ public static void print_i(String pkg, String msg) { if (mIsShowLog) Log.i(TAG, "[" + pkg + "]-------------------->" + msg); } }在InCallScreen类中注册需要处理的消息,并根据不同的状态使用一个TextView来更新
package com.dzt.phonemsg; import com.dzt.phonemsg.framework.CallManager; import com.dzt.phonemsg.framework.Phone; import com.dzt.phonemsg.os.AsyncResult; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.widget.TextView; public class InCallScreen extends Activity { private static final int PHONE_STATE_CHANGED = 101; private static final int REQUEST_UPDATE_SCREEN = 122; private TextView mText = null; private Phone.State mState = Phone.State.IDLE; private CallManager mCM; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mText = (TextView) findViewById(R.id.tv_text); mCM = PhoneApp.getInstance().mCM; registerForPhoneStates(); } @Override protected void onDestroy() { // TODO Auto-generated method stub if (PhoneApp.isRunning()) { PhoneApp.getInstance().phone.exitPhone(); } super.onDestroy(); } /** * Something has changed in the phone's state. Update the UI. */ private void onPhoneStateChanged(AsyncResult r) { mState = mCM.getState(); requestUpdateScreen(); } /** * 注册PHONE_STATE_CHANGED标记,跟CallManager通讯 */ private void registerForPhoneStates() { mCM.registerForPreciseCallStateChanged(mHandler, PHONE_STATE_CHANGED, null); } /* package */void requestUpdateScreen() { PhoneApp.print_i("MainActivity", "requestUpdateScreen()..."); mHandler.removeMessages(REQUEST_UPDATE_SCREEN); mHandler.sendEmptyMessage(REQUEST_UPDATE_SCREEN); } /** * 处理UI的操作,如更新通话状态和时间 */ private void updateScreen() { mText.setText(mState.toString()); } private Handler mHandler = new Handler() { public void handleMessage(Message msg) { switch (msg.what) { case PHONE_STATE_CHANGED: onPhoneStateChanged((AsyncResult) msg.obj); break; case REQUEST_UPDATE_SCREEN: updateScreen(); break; default: break; } }; }; }GsmCallTracker这个类去改变不同的状态,用一个线程来获取随机来改变
package com.dzt.phonemsg.framework; import java.util.Random; /** * 在这里随机改变状态,模拟通话管理 * * @author Administrator * */ public class GsmCallTracker { Phone.State state = Phone.State.IDLE; boolean mIsRunning = false; Phone mPhone = null; GsmCallTracker(Phone phone) { // TODO Auto-generated constructor stub mIsRunning = true; mPhone = phone; new CallTrackerThread().start(); } public void exitGsmCallTracker() { mIsRunning = false; } class CallTrackerThread extends Thread { @Override public void run() { // TODO Auto-generated method stub while (mIsRunning) { Random random = new Random(); int data = random.nextInt(3); switch (data) { case 0: state = Phone.State.IDLE; break; case 1: state = Phone.State.RINGING; break; case 2: state = Phone.State.OFFHOOK; break; default: state = Phone.State.IDLE; break; } mPhone.notifyPreciseCallStateChanged(); // PhoneApp.print_i("GsmCallTracker", // "CallTrackerThread data = " // + data + "----" + state.toString()); try { sleep(2000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } }
Demo下载:http://download.csdn.net/detail/deng0zhaotai/7706739
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。