Android RIL java 详解---------command interface
2014-09-28 13:10
295 查看
概括
RIL java command interface 起到承上启下的作用,对上调用注册的notify, 对下(RIL C) 通信。在和RIL C 通信中主要使用使用socket 通信机制,主要有两个类RILSender,完成消息打包和发送的。RILReceiver 完成消息接收和处理。按照处力方式的不同可以把消息分成两大类,Solicited 消息,主动请求的消息,如sim 状态的查询,请求和应答一一对应。
Unsolicited 消息,主动上报的消息,如Radio on 消息,模块状态的改变等,只有应答。
知识要点
RIL 主要完成 RILSender RILReceiver 的构造还有一些常用消息的封装,//***** Constructors public RIL(Context context, int preferredNetworkType, int cdmaSubscription) { super(context); if (RILJ_LOGD) { riljLog("RIL(context, preferredNetworkType=" + preferredNetworkType + " cdmaSubscription=" + cdmaSubscription + ")"); } mCdmaSubscription = cdmaSubscription; mPreferredNetworkType = preferredNetworkType; mPhoneType = RILConstants.NO_PHONE; PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG); mWakeLock.setReferenceCounted(false); mWakeLockTimeout = SystemProperties.getInt(TelephonyProperties.PROPERTY_WAKE_LOCK_TIMEOUT, DEFAULT_WAKE_LOCK_TIMEOUT); mRequestMessagesPending = 0; mRequestMessagesWaiting = 0; //构造发送线程 mSenderThread = new HandlerThread("RILSender"); mSenderThread.start(); Looper looper = mSenderThread.getLooper(); mSender = new RILSender(looper); ConnectivityManager cm = (ConnectivityManager)context.getSystemService( Context.CONNECTIVITY_SERVICE); if (cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE) == false) { riljLog("Not starting RILReceiver: wifi-only"); } else { //构造接受线程 riljLog("Starting RILReceiver"); mReceiver = new RILReceiver(); mReceiverThread = new Thread(mReceiver, "RILReceiver"); mReceiverThread.start(); //注册屏幕开关广播接受 IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_SCREEN_ON); filter.addAction(Intent.ACTION_SCREEN_OFF); context.registerReceiver(mIntentReceiver, filter); } }
RILSender 这个类继承Handler ,使用android Handler 机制,先将消息经过序列化打包生成RILRequest,之后再发送。其中RILRequest 负责完成加入serial,和序列化,
主要包括两个函数obtain onError .
其关键属性:
//***** Instance Variables int mSerial;//表示消息,每发送自动++ int mRequest; // 表示发送消息类型 Message mResult; // 接受处理用 Parcel mp; //序列化变量其关键方法:
/** * Retrieves a new RILRequest instance from the pool. * * @param request RIL_REQUEST_* * @param result sent when operation completes * @return a RILRequest instance from the pool. */ static RILRequest obtain(int request, Message result) { RILRequest rr = null; synchronized(sPoolSync) {//暂时没用 if (sPool != null) { rr = sPool; sPool = rr.mNext; rr.mNext = null; sPoolSize--; } } if (rr == null) { rr = new RILRequest(); //申请一个消息单元 } synchronized(sSerialMonitor) { rr.mSerial = sNextSerial++;//标示唯一一个消息, } //下面是序列化 rr.mRequest = request; rr.mResult = result; rr.mp = Parcel.obtain(); if (result != null && result.getTarget() == null) { throw new NullPointerException("Message target must not be null"); } // first elements in any RIL Parcel rr.mp.writeInt(request); rr.mp.writeInt(rr.mSerial); return rr; }
RIL Sender ,负责发送消息到RIL C。
class RILSender extends Handler implements Runnable { public RILSender(Looper looper) { super(looper); } // Only allocated once byte[] dataLength = new byte[4]; //***** Runnable implementation public void run() { //setup if needed } //***** Handler implementation @Override public void handleMessage(Message msg) { RILRequest rr = (RILRequest)(msg.obj); RILRequest req = null; switch (msg.what) { case EVENT_SEND: /** * mRequestMessagePending++ already happened for every * EVENT_SEND, thus we must make sure * mRequestMessagePending-- happens once and only once */ boolean alreadySubtracted = false; try { LocalSocket s; s = mSocket;//这个是RILReceiver 中的获得socket 句柄 if (s == null) { rr.onError(RADIO_NOT_AVAILABLE, null); rr.release(); if (mRequestMessagesPending > 0) mRequestMessagesPending--; alreadySubtracted = true; return; } synchronized (mRequestsList) { mRequestsList.add(rr); mRequestMessagesWaiting++; } if (mRequestMessagesPending > 0) mRequestMessagesPending--; alreadySubtracted = true; byte[] data; data = rr.mp.marshall();//反序列化得到data, rr.mp.recycle(); rr.mp = null; if (data.length > RIL_MAX_COMMAND_BYTES) { throw new RuntimeException( "Parcel larger than max bytes allowed! " + data.length); } // parcel length in big endian dataLength[0] = dataLength[1] = 0; dataLength[2] = (byte)((data.length >> 8) & 0xff); dataLength[3] = (byte)((data.length) & 0xff); //Log.v(LOG_TAG, "writing packet: " + data.length + " bytes"); s.getOutputStream().write(dataLength); //发送数据长度 s.getOutputStream().write(data); //发送数据 } catch (IOException ex) { Log.e(LOG_TAG, "IOException", ex); req = findAndRemoveRequestFromList(rr.mSerial); // make sure this request has not already been handled, // eg, if RILReceiver cleared the list. if (req != null || !alreadySubtracted) { rr.onError(RADIO_NOT_AVAILABLE, null); rr.release(); } } catch (RuntimeException exc) { Log.e(LOG_TAG, "Uncaught exception ", exc); req = findAndRemoveRequestFromList(rr.mSerial); // make sure this request has not already been handled, // eg, if RILReceiver cleared the list. if (req != null || !alreadySubtracted) { rr.onError(GENERIC_FAILURE, null); rr.release(); } } finally { // Note: We are "Done" only if there are no outstanding // requests or replies. Thus this code path will only release // the wake lock on errors. releaseWakeLockIfDone(); } if (!alreadySubtracted && mRequestMessagesPending > 0) { mRequestMessagesPending--; } break; case EVENT_WAKE_LOCK_TIMEOUT: // Haven't heard back from the last request. Assume we're // not getting a response and release the wake lock. synchronized (mWakeLock) { if (mWakeLock.isHeld()) { // The timer of WAKE_LOCK_TIMEOUT is reset with each // new send request. So when WAKE_LOCK_TIMEOUT occurs // all requests in mRequestList already waited at // least DEFAULT_WAKE_LOCK_TIMEOUT but no response. // Reset mRequestMessagesWaiting to enable // releaseWakeLockIfDone(). // // Note: Keep mRequestList so that delayed response // can still be handled when response finally comes. if (mRequestMessagesWaiting != 0) { Log.d(LOG_TAG, "NOTE: mReqWaiting is NOT 0 but" + mRequestMessagesWaiting + " at TIMEOUT, reset!" + " There still msg waitng for response"); mRequestMessagesWaiting = 0; if (RILJ_LOGD) { synchronized (mRequestsList) { int count = mRequestsList.size(); Log.d(LOG_TAG, "WAKE_LOCK_TIMEOUT " + " mRequestList=" + count); for (int i = 0; i < count; i++) { rr = mRequestsList.get(i); Log.d(LOG_TAG, i + ": [" + rr.mSerial + "] " + requestToString(rr.mRequest)); } } } } // mRequestMessagesPending shows how many // requests are waiting to be sent (and before // to be added in request list) since star the // WAKE_LOCK_TIMEOUT timer. Since WAKE_LOCK_TIMEOUT // is the expected time to get response, all requests // should already sent out (i.e. // mRequestMessagesPending is 0 )while TIMEOUT occurs. if (mRequestMessagesPending != 0) { Log.e(LOG_TAG, "ERROR: mReqPending is NOT 0 but" + mRequestMessagesPending + " at TIMEOUT, reset!"); mRequestMessagesPending = 0; } mWakeLock.release(); } } break; } } }
RILReceiver 主要维护socket 链接,从RIL C 接受消息并处理。
class RILReceiver implements Runnable { byte[] buffer; RILReceiver() { buffer = new byte[RIL_MAX_COMMAND_BYTES]; } public void run() { int retryCount = 0; try {for (;;) { LocalSocket s = null; LocalSocketAddress l; try { s = new LocalSocket(); //构造LocalSocket l = new LocalSocketAddress(SOCKET_NAME_RIL, LocalSocketAddress.Namespace.RESERVED); //打包参数 s.connect(l);//链接socket } catch (IOException ex){ try { if (s != null) { s.close(); } } catch (IOException ex2) { //ignore failure to close after failure to connect } //连续尝试八此,每次间隔SOCKET_OPEN_RETRY_MILLIS // don't print an error message after the the first time // or after the 8th time if (retryCount == 8) { Log.e (LOG_TAG, "Couldn't find '" + SOCKET_NAME_RIL + "' socket after " + retryCount + " times, continuing to retry silently"); } else if (retryCount > 0 && retryCount < 8) { Log.i (LOG_TAG, "Couldn't find '" + SOCKET_NAME_RIL + "' socket; retrying after timeout"); } try { Thread.sleep(SOCKET_OPEN_RETRY_MILLIS); } catch (InterruptedException er) { } retryCount++; continue; } retryCount = 0; mSocket = s;//连接成功,把句柄保存到mSocket Log.i(LOG_TAG, "Connected to '" + SOCKET_NAME_RIL + "' socket"); int length = 0; try { InputStream is = mSocket.getInputStream();//阻塞获取输入流 for (;;) { Parcel p; length = readRilMessage(is, buffer); //读取消息并放到buffer中 if (length < 0) { // End-of-stream reached d66f break; } p = Parcel.obtain(); p.unmarshall(buffer, 0, length); //把消息序列化 p.setDataPosition(0); //Log.v(LOG_TAG, "Read packet: " + length + " bytes"); processResponse(p);// 处理消息 p.recycle();//释放序列化变量 } } catch (java.io.IOException ex) { Log.i(LOG_TAG, "'" + SOCKET_NAME_RIL + "' socket closed", ex); } catch (Throwable tr) { Log.e(LOG_TAG, "Uncaught exception read length=" + length + "Exception:" + tr.toString()); } Log.i(LOG_TAG, "Disconnected from '" + SOCKET_NAME_RIL + "' socket"); setRadioState (RadioState.RADIO_UNAVAILABLE); try { mSocket.close(); } catch (IOException ex) { } mSocket = null; RILRequest.resetSerial(); // Clear request list on close clearRequestsList(RADIO_NOT_AVAILABLE, false); }} catch (Throwable tr) { Log.e(LOG_TAG,"Uncaught exception", tr); } /* We're disconnected so we don't know the ril version */ notifyRegistrantsRilConnectionChanged(-1); } }
示例
下面以获取sim卡的状态为例子大概说明一下这个过程。在UiccController.java中EVENT_ICC_STATUS_CHANGED 调用RIL Java 中的getIccCardStatus 参数是当前handler 和 EVENT_GET_ICC_STATUS_DONE,通过RILSender发送RIL_REQUEST_GET_SIM_STATUS消息到RIL C,并保存RILRequest . 经过相应处理,可以得到RIL C中的响应类型为 RIL_REQUEST_GET_SIM_STATUS,通过serial
找到之前发送的RILRequest,信息。在通过responseIccCardStatus 对消息格式化,回掉之前handler消息类型还是EVENT_GET_ICC_STATUS_DONE,这样就可以处理了。
相关函数
public void handleMessage (Message msg) { synchronized (mLock) { switch (msg.what) { case EVENT_ICC_STATUS_CHANGED: if (DBG) log("Received EVENT_ICC_STATUS_CHANGED, calling getIccCardStatus"); Log.i(LOG_TAG, "Kwan : **************************** via EVENT_ICC_STATUS_CHANGED"); mCi.getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE)); // mCi 是RIL Java 实例的句柄 break; case EVENT_GET_ICC_STATUS_DONE: if (DBG) log("Received EVENT_GET_ICC_STATUS_DONE"); Log.i(LOG_TAG, "Kwan : **************************** via EVENT_GET_ICC_STATUS_DONE" ); AsyncResult ar = (AsyncResult)msg.obj; onGetIccCardStatusDone(ar); break; default: Log.e(LOG_TAG, " Unknown Event " + msg.what); } } } //发送部分 public void getIccCardStatus(Message result) { //Note: This RIL request has not been renamed to ICC, // but this request is also valid for SIM and RUIM RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_SIM_STATUS, 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; } //发送到RILSender 中的handleMessage msg = mSender.obtainMessage(EVENT_SEND, rr); acquireWakeLock(); msg.sendToTarget(); } //接受部分 private void processSolicited (Parcel p) { int serial, error; boolean found = false; serial = p.readInt(); error = p.readInt(); RILRequest rr; rr = findAndRemoveRequestFromList(serial); // 根据获取RILRequest if (rr == null) { Log.w(LOG_TAG, "Unexpected solicited response! sn: " + serial + " error: " + error); return; } 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; //数据格式化 ****************************************************************************** default: throw new RuntimeException("Unrecognized solicited response: " + rr.mRequest); //break; }} catch (Throwable tr) { // Exceptions here usually mean invalid RIL responses Log.w(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(); } rr.release(); return; } } if (RILJ_LOGD) riljLog(rr.serialString() + "< " + requestToString(rr.mRequest) + " " + retToString(rr.mRequest, ret)); if (rr.mResult != null) { //调用发送时出册的handler AsyncResult.forMessage(rr.mResult, ret, null); rr.mResult.sendToTarget(); } rr.release(); } private Object responseIccCardStatus(Parcel p) { IccCardApplicationStatus appStatus; IccCardStatus cardStatus = new IccCardStatus(); cardStatus.setCardState(p.readInt()); cardStatus.setUniversalPinState(p.readInt()); cardStatus.mGsmUmtsSubscriptionAppIndex = p.readInt(); cardStatus.mCdmaSubscriptionAppIndex = p.readInt(); cardStatus.mImsSubscriptionAppIndex = p.readInt(); int numApplications = p.readInt(); // limit to maximum allowed applications if (numApplications > IccCardStatus.CARD_MAX_APPS) { numApplications = IccCardStatus.CARD_MAX_APPS; } cardStatus.mApplications = new IccCardApplicationStatus[numApplications]; for (int i = 0 ; i < numApplications ; i++) { appStatus = new IccCardApplicationStatus(); appStatus.app_type = appStatus.AppTypeFromRILInt(p.readInt()); appStatus.app_state = appStatus.AppStateFromRILInt(p.readInt()); appStatus.perso_substate = appStatus.PersoSubstateFromRILInt(p.readInt()); appStatus.aid = p.readString(); appStatus.app_label = p.readString(); appStatus.pin1_replaced = p.readInt(); appStatus.pin1 = appStatus.PinStateFromRILInt(p.readInt()); appStatus.pin2 = appStatus.PinStateFromRILInt(p.readInt()); cardStatus.mApplications[i] = appStatus; } return cardStatus; }
总结
RIL Java command interface 主要是完成:1、和RIL C 通信,命令的收发
2、对RIL java 中的其它提供接口
相关文章推荐
- android RIL Java 详解 ---- 入口 Phone factory
- android RIL Java 详解 -------- RegistrantList
- Android RIL Java 详解 -------- CDMALTEPhone
- android-WebView详解实例(JavaScript调用Java方法,Java调用JavaScript方法)
- Android RIL的java框架
- android froyo framework内RIL.java类分析
- Android RIL Java
- 【贪吃蛇—Java程序员写Android游戏】系列 3. 用J2ME实现Android的Snake Sample详解
- Win7环境下的Java和Android环境变量配置详解
- Win7环境下的Java和Android环境变量配置详解
- Android核心分析之(18)Android电话系统之RIL-Java
- Android RIL 本地代码(c/c++) 和 Java代码解析
- Android核心分析(18)-----Android电话系统之RIL-Java
- win7中java或android开发环境配置详解
- android4.0网络服务状态或者信号量强度上报过程(RIL Framework Java 部分)
- Android RIL的java框架
- 【贪吃蛇—Java程序员写Android游戏】系列 1.Android SDK Sample-Snake详解
- 【贪吃蛇—Java程序员写Android游戏】系列 1.Android SDK Sample-Snake详解
- Android资源和R.java文件详解《一》
- android 横屏竖屏设置的详解(最好不要用Java代码实现,用xml配置文件吧)