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

原创粉丝点击