Android Ethernet从上至下解析一

最近遇到不少框架问题,比如关于网口的,开机后拔掉有线网,状态栏和设置项中有线网显示图标不会更新,还有双网口的需求,下面就带着这个问题,以跟踪网络状态问题为引线,本篇将贯穿分析Ethernet从上至下的框架结构。因能力和时间有限,文中有分析不到位的地方,十分欢迎大侠们拍砖。

首先看下应用层网络监听相关的app

网络监听一:设置

packages/apps/Settings/src/com/android/settings/ethernet/EthernetEnabler.java

设置项网络按钮类定义

网络监听二:statusbar

frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java

NetworkController本身是个BroadcastReceiver,其中关于网络状态变化的监听消息为EthernetManager.NETWORK_STATE_CHANGED_ACTION,可以猜测这个消息是framework发出来的,往下看。

网络服务框架层

通过整理,网络框架管理器和服务相关代码和基本解释如下:

frameworks/base/ethernet/java/com/android/internal/ethernet/

    EthernetStateMachine.java     -> 网络状态机,用于管理网络状态变化及动作逻辑
    EthernetManager.java          -> 网络管理器,是app和EthernetService信息交互的桥梁
    EthernetInfo.java             -> 网络状态参数类,是Parcelable的一个实现
    EthernetInfo.aidl             -> aidl文件,Manager和service统一使用的数据结构
    IEthernetManager.aidl         -> aidl文件,用于Manager和service通信

在此可以发现网络状态机也在监听NETWORK_STATE_CHANGED_ACTION广播,广播发送者不再这里,那应该就是在service那了,继续往下。

frameworks/base/services/java/com/android/server/EthernetService.java

    private class InterfaceStateReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals(EthernetManager.INTERFACE_STATE_CHANGED_ACTION)) {
                ...
                Intent newIntent = new Intent(EthernetManager.NETWORK_STATE_CHANGED_ACTION);
                newIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
                newIntent.putExtra(EthernetManager.EXTRA_ETHERNET_INFO, ei);

在service中,可以看到发送NETWORK_STATE_CHANGED_ACTION的发送动作,而这个发送行为还不是底层上报的状态直接启动的,而是上面说的网络状态机,它发送的INTERFACE_STATE_CHANGED_ACTION广播信息,怎么源头又跑上面去了?有些人可能并不理解为什么在framework里面要把一个简单的事件广播要这么来回的发送,等明白了网络状态机的作用,就知道这些过程的逻辑性了。

我们知道statemachine的特点是有一个rootstate,然后向下由多个state发展而成一个树状结构,state之间的转换会伴随着enter(),processMessage()等动作。EthernetStateMachine的状态初始化如下:

            addState(mRootState);
            addState(mIdleState, mRootState);
            //addState(mObtainingLinkState, mRootState);
            addState(mObtainingIpState, mRootState);
            addState(mIPConnectedState, mRootState);
            addState(mDisconnectingState, mRootState);

接着前面说到的INTERFACE_STATE_CHANGED_ACTION广播继续来看下状态机中的逻辑。

在ethernetstatemachine中,state状态的变化控制着网络状态的广播通知,部分代码如下:

    private void sendInterfaceStateChangedBroadcast() {
        if (DBG) Slog.d(TAG, "Sending INTERFACE_STATE_CHANGED_ACTION for "
                + mEthernetInfo.getName());
        Intent intent = new Intent(EthernetManager.INTERFACE_STATE_CHANGED_ACTION);
        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
        intent.putExtra(EthernetManager.EXTRA_ETHERNET_INFO, new EthernetInfo(mEthernetInfo));
        mContext.sendBroadcast(intent);
    }
    private void setNetworkDetailedState(DetailedState state) {
        if (DBG) Slog.d(TAG, mEthernetInfo.getName() + " setDetailed state, old ="
                + mEthernetInfo.getDetailedState() + " and new state=" + state);
        if (state != mEthernetInfo.getDetailedState()) {
            mEthernetInfo.setDetailedState(state, null, null);
            mEthernetInfo.setIsAvailable(true);
            sendInterfaceStateChangedBroadcast();
        }
    }
    void dhcpSuccess(DhcpResults dr) {
        if (DBG) Slog.d(TAG, mEthernetInfo.getName() + " DHCP successful");
        LinkProperties lp = dr.linkProperties;
            ...
            setNetworkDetailedState(DetailedState.CONNECTED);
    }

上面就是网络状态机的逻辑功能,而状态机的消息来源是service,

    public void updateInterface(EthernetInfo newInfo) {
        if (newInfo == null) {
            Slog.e(TAG, "Null EthernetInfo");
            return;
        }
        if (mAvailableInterface == null) {
            Slog.e(TAG, "Unable to find statemachine for interface " + newInfo.getName());
            return;
        }

        sendMessage(mAvailableInterface,
                EthernetStateMachine.CMD_UPDATE_INTERFACE,
                newInfo);

        if(DBG) Slog.d(TAG, newInfo.getName() + " updateInterface done");
    }

看到了来来回回的广播,至此算是结束了,这里还要注意一点,广播收发程序中,我们要注意一个序列化参数的传递,就是EthernetInfo对象,这个对象存储着当前网络状态参数。

我们可以这么理解:网络状态机是EthernetService的辅助逻辑处理单元,service通过给状态机发送消息并等待状态机处理结果,然后将结果发送给应用程序。这个就是网络部分framework层的大致逻辑,了解这个之后,我们继续分析service是从哪里取得网络状态消息的。

来看下EthernetService的构造函数:

    public EthernetService(Context context) {
        mContext = context;
        mNetd = INetworkManagementService.Stub.asInterface(
                ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE)
        );

        try {
            mNetd.registerObserver(new NetworkManagementEventObserver());
        } catch (RemoteException e) {
            Slog.e(TAG, "Remote NetworkManagementService error: " + e);
        }

看下NetworkManagementEventObserver的实现:

    private class NetworkManagementEventObserver extends INetworkManagementEventObserver.Stub {
        public void interfaceAdded(String iface) {
            if(DBG) Slog.d(TAG, "interfaceAdded: " + iface);
            addInterface(iface);
        }
        public void interfaceRemoved(String iface) {
            if(DBG) Slog.d(TAG, "interfaceRemoved: " + iface);
            removeInterface(iface);
        }
        public void limitReached(String limitName, String iface) {}
        public void interfaceClassDataActivityChanged(String label, boolean active) {}
        public void interfaceLinkStateChanged(String iface, boolean up) {
            if(DBG) Slog.d(TAG, "interfaceLinkStateChanged for " + iface + ", up = " + up);
            if (mAvailableInterface != null && up) {
                //sendMessage(mAvailableInterface,
                //EthernetStateMachine.CMD_LINK_UP);
            }
        }
        public void interfaceStatusChanged(String iface, boolean up) {
            if(DBG) Slog.d(TAG, "interfaceStatusChanged for " + iface + ", up = " + up);
            //addInterface(iface);
        }
        public void addressUpdated(String address, String iface, int flags, int scope) {}
        public void addressRemoved(String address, String iface, int flags, int scope) {}
    }

这里我们看到seivice从NetworkManagementService进行函数回调。

NetworkManagementService也是注册到系统中的服务项,顾名思义负责网络管理服务,具体功能在本篇不做深入分析,其中之一通过socket和netd进行交互,这个在后面继续跟踪。

    SystemServer.java    try {
         Slog.i(TAG, "NetworkManagement Service");
         networkManagement = NetworkManagementService.create(context);
         ServiceManager.addService(Context.NETWORKMANAGEMENT_SERVICE, networkManagement);
    } catch (Throwable e) {
         reportWtf("starting NetworkManagement Service", e);
    }

在后续的分析中,我们开始了解到framework到native的交互,这里我们先看一张网络的图示

我们按照图示的最上层来看看NetworkManagementService.java

    private static final String NETD_SOCKET_NAME = "netd";
    private NetworkManagementService(Context context, String socket) {
        mContext = context;

        if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
            return;
        }

        mConnector = new NativeDaemonConnector(
                new NetdCallbackReceiver(), socket, 10, NETD_TAG, 160);
        mThread = new Thread(mConnector, NETD_TAG);

        // Add ourself to the Watchdog monitors.
        Watchdog.getInstance().addMonitor(this);
    }

这里socket值就是"netd"字符串,service启动了名为NativeDaemonConnector的Runnable线程,同时从构造函数中传递了NetdCallbackReceiver
对象,用于回调处理各种网络事件。

    private class NetdCallbackReceiver implements INativeDaemonConnectorCallbacks {
         @Override
        public void onDaemonConnected() {

        @Override
        public boolean onEvent(int code, String raw, String[] cooked) {
            switch (code) {
            case NetdResponseCode.InterfaceChange:
                    } else if (cooked[2].equals("linkstate") && cooked.length == 5) {
                        // 网络状态变化事件在这里回调处理
                        notifyInterfaceLinkStateChanged(cooked[3], cooked[4].equals("up"));
                        return true;
                    }
   /**
     * Notify our observers of an interface link state change
     * (typically, an Ethernet cable has been plugged-in or unplugged).
     */
    private void notifyInterfaceLinkStateChanged(String iface, boolean up) {
        final int length = mObservers.beginBroadcast();
        for (int i = 0; i < length; i++) {
            try {
                mObservers.getBroadcastItem(i).interfaceLinkStateChanged(iface, up);
            } catch (RemoteException e) {
            } catch (RuntimeException e) {
            }
        }
        mObservers.finishBroadcast();
    }

上面说了NativeDaemonConnector是一个Runnable的实现,那么这个线程在后台做些什么工作呢?在线程run函数中可以看到线程在while死循环中一直listenToSocket,可以猜想这里是在监听获取native中网络相关事件的地方了。

    @Override
    public void run() {
        mCallbackHandler = new Handler(FgThread.get().getLooper(), this);

        while (true) {
            try {
                listenToSocket();
            } catch (Exception e) {
                loge("Error in NativeDaemonConnector: " + e);
                SystemClock.sleep(5000);
            }
        }
    }
    private void listenToSocket() throws IOException {
        LocalSocket socket = null;

        try {
            // 创建一个socket
            socket = new LocalSocket();
            LocalSocketAddress address = determineSocketAddress();
            socket.connect(address);
            // 从socket中获取流数据并处理
            InputStream inputStream = socket.getInputStream();
            synchronized (mDaemonLock) {
                mOutputStream = socket.getOutputStream();
            }
            ...
                mCallbackHandler.sendMessage(mCallbackHandler.obtainMessage(
                                        event.getCode(), event.getRawEvent()));
    // 收到流数据时,直接发给主线程,通过NetdCallbackReceiver 对象进行回调处理
    @Override
    public boolean handleMessage(Message msg) {
        String event = (String) msg.obj;
        try {
            if (!mCallbacks.onEvent(msg.what, event, NativeDaemonEvent.unescapeArgs(event))) {
                log(String.format("Unhandled event '%s'", event));
            }
        } catch (Exception e) {
            loge("Error handling '" + event + "': " + e);
        }
        return true;
    }

    private LocalSocketAddress determineSocketAddress() {
        // If we're testing, set up a socket in a namespace that's accessible to test code.
        // In order to ensure that unprivileged apps aren't able to impersonate native daemons on
        // production devices, even if said native daemons ill-advisedly pick a socket name that
        // starts with __test__, only allow this on debug builds.
        if (mSocket.startsWith("__test__") && Build.IS_DEBUGGABLE) {
            return new LocalSocketAddress(mSocket);
        } else {
            return new LocalSocketAddress(mSocket, LocalSocketAddress.Namespace.RESERVED);
        }
    }

我们接下来看一下LocalSocket相关的类,了解一下这个socket是如何connect和get的。

LocalSocket*相关的类定义在

frameworks/base/core/java/android/net/

|->LocalSocket.java

|->LocalSocketAddress.java

|->LocalSocketImpl.java

这里重点看下LocalSocketImpl类,其中就可以看到大量的native函数了,也就是通过jni完成java到C++的交互,有些人可能会有疑问,既然这里使用jni调用了C++库函数,但是这里没有看到System.loadlibary字眼啊。通过jni的基础我们知道这里java类使用的jni名字应该是android_net_LocalSocket*样子的,那么在android工程代码中也确实存在这个名字的cpp文件,路径是frameworks/base/core/jni/。可以确认我们这里的java层就是调用这里的lib库了,而编译后我们知道这个库名为libandroid_runtime.so。那么这个库在哪里load的呢?下面简要提一下。

我们知道android启动时,第一个进程init在解析init.rc时创建了app_process,app_process在创建zygote进程前首先建立dalvikvm虚拟机环境,初始化android runtime,这里就在C环境下预先加载了libandroid_runtime.so库。如下:

frameworks/base/core/jni/AndroidRuntime.cpp

static const RegJNIRec gRegJNI[] = {
    REG_JNI(register_android_net_LocalSocketImpl),

了解了库加载的问题后,我们接着看LocalSocketImpl.java中使用的几个重要的函数:

connect()
getInputStream()
getOutputStream()

函数具体内容不具体贴出来,其中可以看到调用了本地方法如read_native(),writeba_native()等。我们就走到native的大门了。打开本地函数文件,看下native中本地函数列表。

frameworks/base/core/jni/android_net_LocalSocketImpl.cpp

static JNINativeMethod gMethods[] = {
     /* name, signature, funcPtr */
    {"getOption_native", "(Ljava/io/FileDescriptor;I)I", (void*)socket_getOption},
    {"setOption_native", "(Ljava/io/FileDescriptor;III)V", (void*)socket_setOption},
    {"connectLocal", "(Ljava/io/FileDescriptor;Ljava/lang/String;I)V",
                                                (void*)socket_connect_local},
    {"bindLocal", "(Ljava/io/FileDescriptor;Ljava/lang/String;I)V", (void*)socket_bind_local},
    {"listen_native", "(Ljava/io/FileDescriptor;I)V", (void*)socket_listen},
    {"accept", "(Ljava/io/FileDescriptor;Landroid/net/LocalSocketImpl;)Ljava/io/FileDescriptor;", (void*)socket_accept},
    {"shutdown", "(Ljava/io/FileDescriptor;Z)V", (void*)socket_shutdown},
    {"available_native", "(Ljava/io/FileDescriptor;)I", (void*) socket_available},
    {"pending_native", "(Ljava/io/FileDescriptor;)I", (void*) socket_pending},
    {"read_native", "(Ljava/io/FileDescriptor;)I", (void*) socket_read},
    {"readba_native", "([BIILjava/io/FileDescriptor;)I", (void*) socket_readba},
    {"writeba_native", "([BIILjava/io/FileDescriptor;)V", (void*) socket_writeba},
    {"write_native", "(ILjava/io/FileDescriptor;)V", (void*) socket_write},
    {"getPeerCredentials_native",
            "(Ljava/io/FileDescriptor;)Landroid/net/Credentials;",
            (void*) socket_get_peer_credentials}
    //,{"getSockName_native", "(Ljava/io/FileDescriptor;)Ljava/lang/String;",
    //        (void *) socket_getSockName}

};
int register_android_net_LocalSocketImpl(JNIEnv *env){}

好了,今晚先写到这里,下一篇再续!

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-09 07:32:04

Android Ethernet从上至下解析一的相关文章

【Android自学日记】【转】Android Fragment 真正的完全解析(下)

上篇博客中已经介绍了Fragment产生原因,以及一些基本的用法和各种API,如果你还不了解,请看:Android Fragment 真正的完全解析(上). 本篇将介绍上篇博客提到的:如何管理Fragment回退栈,Fragment如何与Activity交互,Fragment与Activity交互的最佳实践,没有视图的Fragment的用处,使用Fragment创建对话框,如何与ActionBar,MenuItem集成等~~ 1.管理Fragment回退栈 类似与Android系统为Activi

Android Fragment 真正的完全解析(下)

Android Fragment 真正的完全解析(下) 标签: AndroidFragmentDialogFragmentMenuItem 目录(?)[-] 管理Fragment回退栈 Fragment与Activity通信 Fragment与Activity通信的最佳实践 如何处理运行时配置发生变化 Fragmeny与ActionBar和MenuItem集成 没有布局的Fragment的作用 使用Fragment创建对话框 转载请标明出处:http://blog.csdn.net/lmj623

Android网络下解析XML

XML(Extensible Markup Language)可拓展标记语言,它与HTML一样,都是SGML(标准通用标记语言),它可以用来标记数据.定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言. 它非常适合万维网传输,提供统一的方法来描述和交换独立于应用程序或供应商的结构化数据.在Android下有三种方式解析XML,分别为SAX.DOM.PULL:它们有各自的特点,在网络编程中会经常使用,根据实际情况选择哪一种解析方式. 1.内存占用 由于Android手机性能相对于PC还是

Android Fragment 真正的完全解析(下)---转

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/37992017 上篇博客中已经介绍了Fragment产生原因,以及一些基本的用法和各种API,如果你还不了解,请看:Android Fragment 真正的完全解析(上). 本篇将介绍上篇博客提到的:如何管理Fragment回退栈,Fragment如何与Activity交互,Fragment与Activity交互的最佳实践,没有视图的Fragment的用处,使用Fragment创

【转】Android事件分发机制完全解析,带你从源码的角度彻底理解(下)

转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/9153761 记得在前面的文章中,我带大家一起从源码的角度分析了Android中View的事件分发机制,相信阅读过的朋友对View的事件分发已经有比较深刻的理解了. 还未阅读过的朋友,请先参考 Android事件分发机制完全解析,带你从源码的角度彻底理解(上) . 那么今天我们将继续上次未完成的话题,从源码的角度分析ViewGruop的事件分发. 首先我们来探讨一下,什么是View

Android ListView工作原理完全解析(转自 郭霖老师博客)

原文地址:http://blog.csdn.net/guolin_blog/article/details/44996879 在Android所有常用的原生控件当中,用法最复杂的应该就是ListView了,它专门用于处理那种内容元素很多,手机屏幕无法展示出所有内容的情况.ListView可以使用列表的形式来展示内容,超出屏幕部分的内容只需要通过手指滑动就可以移动到屏幕内了. 另外ListView还有一个非常神奇的功能,我相信大家应该都体验过,即使在ListView中加载非常非常多的数据,比如达到

Android Fragment 真正的完全解析(上)

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/37970961 自从Fragment出现,曾经有段时间,感觉大家谈什么都能跟Fragment谈上关系,做什么都要问下Fragment能实现不~~~哈哈,是不是有点过~~~ 本篇博客力求为大家说明Fragment如何产生,什么是Fragment,Fragment生命周期,如何静态和动态的使用Fragment,Fragment回退栈,Fragment事务:以及Fragment的一些特

Android异步加载全解析之Bitmap

Android异步加载全解析之Bitmap 在这篇文章中,我们分析了Android在对大图处理时的一些策略--Android异步加载全解析之大图处理  戳我戳我 那么在这篇中,我们来对图像--Bitmap进行一个更加细致的分析,掌握Bitmap的点点滴滴. 引入 Bitmap这玩意儿号称Android App头号杀手,特别是3.0之前的版本,简直就是皇帝般的存在,碰不得.摔不得.虽然后面的版本Android对Bitmap的管理也进行了一系列的优化,但是它依然是非常难处理的一个东西.在Androi

Android异步加载全解析之使用多线程

异步加载之使用多线程 初次尝试 异步.异步,其实说白了就是多任务处理,也就是多线程执行,多线程那就会有各种问题,我们一步步来看,首先,我们创建一个class--ImageLoaderWithoutCaches,从命名上,大家也看出来,这个类,我们实现的是不带缓存的图像加载,不多说,我们再创建一个方法--showImageByThread,通过多线程来加载图像: /** * Using Thread * @param imageView * @param url */ public void sh