Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android Ethernet從上至下解析一

Android Ethernet從上至下解析一

編輯:關於Android編程

最近遇到不少框架問題,比如關於網口的,開機後拔掉有線網,狀態欄和設置項中有線網顯示圖標不會更新,還有雙網口的需求,下面就帶著這個問題,以跟蹤網絡狀態問題為引線,本篇將貫穿分析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的交互,這裡我們先看一張網絡的圖示 \

 data-cke-saved-name=
我們按照圖示的最上層來看看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){}

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved