Android电话通信机制之一——RIL类分析
来源:互联网 发布:淘宝店铺客服电话 编辑:程序博客网 时间:2024/06/05 20:33
在PhoneFactory.java内开始实例化RIL类
public static void makeDefaultPhone(Context context) { ... sCommandsInterfaces[i] = new RIL(context, networkModes[i], cdmaSubscription, i); ... }
因为RIL的继承关系如:class RIL extends BaseCommands implements CommandsInterface,上面的代码中sCommandsInterface类型是CommandsInterface。属于向上转型。
下面在进入到RIL中的构造方法:
public RIL(Context context, int preferredNetworkType, int cdmaSubscription, Integer instanceId) { //1、调用父类的构造方法 super(context); if (RILJ_LOGD) { riljLog("RIL(context, preferredNetworkType=" + preferredNetworkType + " cdmaSubscription=" + cdmaSubscription + ")"); } //2、初始化全局变量,分别表示:mContext>上下文对象; mCdmaSubscription>int型,接收到的CDMA用户(CDMA subscription received from PhoneFactory); //mPreferredNetworkType>偏好网络设置,就是建立一个与网络提供商相关的网络连接 ; mPhoneType>Phone的类型,GSM,CDMA,由GsmCdmaPhone类来设置 ; mInstanceId> 实例的编号也即是卡遭号. mContext = context; mCdmaSubscription = cdmaSubscription; mPreferredNetworkType = preferredNetworkType; mPhoneType = RILConstants.NO_PHONE; mInstanceId = instanceId; //3、获取电量管理对象,实现对屏幕唤醒的控制 PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, RILJ_LOG_TAG); mWakeLock.setReferenceCounted(false); mAckWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, RILJ_ACK_WAKELOCK_NAME); mAckWakeLock.setReferenceCounted(false); mWakeLockTimeout = SystemProperties.getInt(TelephonyProperties.PROPERTY_WAKE_LOCK_TIMEOUT, DEFAULT_WAKE_LOCK_TIMEOUT_MS); mAckWakeLockTimeout = SystemProperties.getInt( TelephonyProperties.PROPERTY_WAKE_LOCK_TIMEOUT, DEFAULT_ACK_WAKE_LOCK_TIMEOUT_MS); mWakeLockCount = 0; //4、开启新的线程 mSenderThread = new HandlerThread("RILSender" + mInstanceId); mSenderThread.start(); //4、获取新线程的Looper对象;Looper由于为了实现一直对消息队列的检查,处于阻塞状态,而处在HandlerThread线程中则不会影响消息的发送与接收。 Looper looper = mSenderThread.getLooper(); //5、RILSender消息的发送者 mSender = new RILSender(looper); //6、获取网络连接管理器 ConnectivityManager cm = (ConnectivityManager)context.getSystemService( Context.CONNECTIVITY_SERVICE); //7、判断该网络是否支持移动网络 if (cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE) == false) { riljLog("Not starting RILReceiver: wifi-only"); } else { riljLog("Starting RILReceiver" + mInstanceId); //8、实例化RILReceiver类 mReceiver = new RILReceiver(); mReceiverThread = new Thread(mReceiver, "RILReceiver" + mInstanceId); mReceiverThread.start(); //9、显示管理器 DisplayManager dm = (DisplayManager)context.getSystemService( Context.DISPLAY_SERVICE); mDefaultDisplay = dm.getDisplay(Display.DEFAULT_DISPLAY); dm.registerDisplayListener(mDisplayListener, null); mDefaultDisplayState = mDefaultDisplay.getState(); //10、注册广播 IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); Intent batteryStatus = context.registerReceiver(mBatteryStateListener, filter); if (batteryStatus != null) { // 0 means it's on battery mIsDevicePlugged = batteryStatus.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0; } } //11、获取TelephonyDevController的实例,并且为其注册RIL对象 TelephonyDevController tdc = TelephonyDevController.getInstance(); tdc.registerRIL(this); }
通过对RIL的构造方法分析,我们发现RIL的主要作用是实现数据的发送与接收,那么问题是数据来自哪里,从哪里接收,发送到哪里?那首先来看看以上的注释4:
//4、开启新的线程 mSenderThread = new HandlerThread("RILSender" + mInstanceId); mSenderThread.start(); //4、获取新线程的Looper对象;Looper由于为了实现一直对消息队列的检查,处于阻塞状态,而处在HandlerThread线程中则不会影响消息的发送与接收。 Looper looper = mSenderThread.getLooper(); //5、RILSender消息的发送者 mSender = new RILSender(looper);
创建HandlerThread线程时,其中的参数表示线程的名称,mInstance的值是Phone对象的索引编号,也就是说为每一个Phone对象创建一个Handler线程。而HandlerThread线程跟普通的线程的差异比较在下一篇详细讲解。
HandlerThread对象可为外界提供一个Looper对象,该Looper对象被传送到RILSender对象中。通过RILSender的名字可知它是消息的发送者。
class RILSender extends Handler implements Runnable { //1、构造方法 public RILSender(Looper looper) { super(looper); } //2、只分配一次Only allocated once byte[] dataLength = new byte[4]; //***** Runnable implementation //3、Runnable接口 @Override public void run() { //setup if needed } //***** Handler implementation //4、Handler接口 @Override public void handleMessage(Message msg) { //5、将传递过来的Message信息强制转化为RILRequest对象 RILRequest rr = (RILRequest)(msg.obj); RILRequest req = null; switch (msg.what) { case EVENT_SEND: case EVENT_SEND_ACK: try { LocalSocket s; //6、将全局变量赋值给LocalSocket s = mSocket; if (s == null) { //7、LocalSocket实例化对象s为空时,则对外发送错误信息 rr.onError(RADIO_NOT_AVAILABLE, null); decrementWakeLock(rr); rr.release(); return; } // Acks should not be stored in list before sending if (msg.what != EVENT_SEND_ACK) { synchronized (mRequestList) { rr.mStartTimeMs = SystemClock.elapsedRealtime(); //8、也即是说当msg.what为EVENT_SEND时,该RILRequest对象的实例被存储起来,以键值对的形式存储 mRequestList.append(rr.mSerial, rr); } } byte[] data; //9、获取数据包中的二进制数据,很显然这些数据原本是在RILRequest的mParcel属性中的。然后将该属性的空间回收。 data = rr.mParcel.marshall(); rr.mParcel.recycle(); rr.mParcel = null; //数据长度小于(8 * 1024)个字节 if (data.length > RIL_MAX_COMMAND_BYTES) { throw new RuntimeException( "Parcel larger than max bytes allowed! " + data.length); } // parcel length in big endian //10.保存数据包的长度,将其转为为高低两个字节进行存储 dataLength[0] = dataLength[1] = 0; dataLength[2] = (byte)((data.length >> 8) & 0xff); //高八位 dataLength[3] = (byte)((data.length) & 0xff); //低八位 //Rlog.v(RILJ_LOG_TAG, "writing packet: " + data.length + " bytes"); //11、获取套接字的输出流,然后将字节数据的长度和要传送的数据包进行输出 s.getOutputStream().write(dataLength); s.getOutputStream().write(data); if (msg.what == EVENT_SEND_ACK) { rr.release(); //释放资源,将RILRequest返回到数据池中 return; } } catch (IOException ex) { Rlog.e(RILJ_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) { rr.onError(RADIO_NOT_AVAILABLE, null); decrementWakeLock(rr); rr.release(); return; } } catch (RuntimeException exc) { Rlog.e(RILJ_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) { rr.onError(GENERIC_FAILURE, null); decrementWakeLock(rr); rr.release(); return; } } break; ... } } }
从以上可以看到,数据来源于数据池中的RILRequest的实例中,使用LocalSocket将RILRequest实例中的数据写入输出流(注释11),这样数据就发送出去了,之后RILRequest的实例被回收重新放置到
数据池中。其中使用到了一个比较重要的类>RILRequest,等下再作分析。总之在RILSender类通过LocalSocket将数据发送出去。
接下来我们再看看RIL构造函数中的注释6,7,8.如下:
public RIL(Context context, int preferredNetworkType, int cdmaSubscription, Integer instanceId) { ... //6、获取网络连接管理器 ConnectivityManager cm = (ConnectivityManager)context.getSystemService( Context.CONNECTIVITY_SERVICE); //7、判断该网络是否支持移动网络 if (cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE) == false) { riljLog("Not starting RILReceiver: wifi-only"); } else { riljLog("Starting RILReceiver" + mInstanceId); //8、实例化RILReceiver类 mReceiver = new RILReceiver(); mReceiverThread = new Thread(mReceiver, "RILReceiver" + mInstanceId); mReceiverThread.start(); ... } }
注释6.通过系统服务获取系统的网络连接管理器,该网络管理器可以进行网络状态的查询和发送网络状态改变的通知。在注释7中判断是否支持移动网络链接,在注释8中则可以进行接收数据。通过这里我们可以知道,
开启新的线程获取的网络数据通过RILReceiver这个类来接收。接下来要好好分析下RILReceiver类;
class RILReceiver implements Runnable { byte[] buffer; RILReceiver() { // 1、创建数据接收的缓冲区,显然在这里缓冲区大小为8*1024,因此在发送数据时,要小于这个缓冲区的大小。 buffer = new byte[RIL_MAX_COMMAND_BYTES]; } @Override public void run() { //2、尝试网络连接的次数 int retryCount = 0; String rilSocket = "rild"; //3、一个死循环,说明一直处于数据获取状态 try {for (;;) { LocalSocket s = null; LocalSocketAddress l; if (mInstanceId == null || mInstanceId == 0 ) { rilSocket = SOCKET_NAME_RIL[0]; } else { rilSocket = SOCKET_NAME_RIL[mInstanceId]; } //4、在这个try的模块里,如果没有连接上,尝试进行多次连接 try { //5、通过LocalSocket连接server s = new LocalSocket(); l = new LocalSocketAddress(rilSocket, LocalSocketAddress.Namespace.RESERVED); s.connect(l); } catch (IOException ex){ try { if (s != null) { s.close(); } } catch (IOException ex2) { //ignore failure to close after failure to connect } // don't print an error message after the the first time // or after the 8th time if (retryCount == 8) { Rlog.e (RILJ_LOG_TAG, "Couldn't find '" + rilSocket + "' socket after " + retryCount + " times, continuing to retry silently"); } else if (retryCount >= 0 && retryCount < 8) { Rlog.i (RILJ_LOG_TAG, "Couldn't find '" + rilSocket + "' socket; retrying after timeout"); } try { Thread.sleep(SOCKET_OPEN_RETRY_MILLIS); } catch (InterruptedException er) { } retryCount++; continue; } //连接成功重置为0 retryCount = 0; //6、将内部变量赋值给全局变量,该全局变量在RILSender同样被使用,说明数据的发送与数据的就收使用的是同一个通道。 mSocket = s; Rlog.i(RILJ_LOG_TAG, "(" + mInstanceId + ") Connected to '" + rilSocket + "' socket"); int length = 0; try { //7、获取Socket的输入流通道 InputStream is = mSocket.getInputStream(); //8、读取数据的一个循环,在该循环中不断的读取底层的数据 for (;;) { Parcel p; //9、从输入流中读取数据到buffer中 length = readRilMessage(is, buffer); if (length < 0) { // End-of-stream reached break; } //10、实例化数据包,Parcel对象可以实现跨进程传递数据,因此把数据包装到Parcel中,因此便于跨进程发送数据 p = Parcel.obtain(); p.unmarshall(buffer, 0, length); //为Parcel设置原始字节 p.setDataPosition(0); // 重新设置数据的读取起始端 //Rlog.v(RILJ_LOG_TAG, "Read packet: " + length + " bytes"); //11、处理响应 processResponse(p); p.recycle(); } } .... } }
从上面的代码中我们可以看到,接收数据的过程中:首先要做的就是连接socket,连接过程中尝试多次连接,连接成功后,读取输入流数据,其中是在一个死循环中一直读取,直到读取结束为止。
数据一旦读取到,就将数据包装到Parcel中,从而进行数据包的响应。
再从RIL的构造方法中可以看到关于TelephonyDevController类的实例化,该类属于单例模式,通过create()创建,这里通过getInstance再次获取:
public static void makeDefaultPhone(Context context) { ... //11、获取TelephonyDevController的实例,并且为其注册RIL对象 TelephonyDevController tdc = TelephonyDevController.getInstance(); tdc.registerRIL(this); ... }
总之在RIL类中,有关于RILSender类和RILReceiver类进行对数据的接收和发送。接下来讲讲其中需要发送的RILRequest类和Parcel两个类。
参考:
1、(M)SIM卡开机流程分析之RIL类分析
2、Android异步消息处理机制完全解析,带你从源码的角度彻底理解
3、android的消息机制——Handler机制
4、Android核心分析Android电话系统之RIL-Java
- Android电话通信机制之一——RIL类分析
- Android——RIL 机制源码分析
- Android——RIL 机制源码分析
- Android的电话功能介绍——整个RIL文件夹的分析
- android电话部分之ril分析
- Android电话系统RIL-Java分析
- Android之通信RIL模块分析
- Android通讯之RIL 机制分析
- RIL 机制源码分析
- Android学习——Android RIL结构分析与移植
- android-ril 分析 -radiooption
- Android RIL源码分析
- Android RIL源码分析
- Android-RIL流程分析
- Android Ril 分析
- Android Ril 分析
- android ril 调试分析
- Android Ril 分析
- 谈一谈分布式架构那点事
- 尚学堂freemarker教程
- FragmentPagerAdapter 更新数据遇到了坑啊。下拉刷新不会更新页面、有缓存。
- OKhttp简单封装
- POJ
- Android电话通信机制之一——RIL类分析
- HDU-寻找大富翁
- 读properties和写properties文件
- 工具
- 大、小端机器判断
- 用Scala模拟RPC通信
- 2017/7/27 离线赛
- Spark 2.0 DataFrame map操作中Unable to find encoder for type stored in a Dataset.问题的分析与解决
- 51nod 1189 阶乘分数