Android Ethernet从上至下解析一

2024-02-17 08:18
文章标签 android 解析 ethernet

本文主要是介绍Android Ethernet从上至下解析一,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

http://www.2cto.com/kf/201508/438539.html

最近遇到不少框架问题,比如关于网口的,开机后拔掉有线网,状态栏和设置项中有线网显示图标不会更新,还有双网口的需求,下面就带着这个问题,以跟踪网络状态问题为引线,本篇将贯穿分析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/
?
1
2
3
4
5
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
?
1
2
3
4
5
6
7
8
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的状态初始化如下:
?
1
2
3
4
5
6
addState(mRootState);
addState(mIdleState, mRootState);
//addState(mObtainingLinkState, mRootState);
addState(mObtainingIpState, mRootState);
addState(mIPConnectedState, mRootState);
addState(mDisconnectingState, mRootState);

接着前面说到的INTERFACE_STATE_CHANGED_ACTION广播继续来看下状态机中的逻辑。 在ethernetstatemachine中, state状态的变化控制着网络状态的广播通知 ,部分代码如下:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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,
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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的构造函数:
?
1
2
3
4
5
6
7
8
9
10
11
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的实现:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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进行交互,这个在后面继续跟踪。
?
1
2
3
4
5
6
7
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的交互,这里我们先看一张网络的图示  \
卓老师三合一建站代理,0加盟
【点击进入】
独立品牌,免备案,PC+手机+微站一键生成 0加盟费,送百万建站系统+销售系统


加载中...
加载中...
我们按照图示的最上层来看看NetworkManagementService.java
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 对象,用于回调处理各种网络事件。
?
1
2
3
4
5
6
7
8
9
10
11
12
13
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 ;
                 }

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
   * 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中网络相关事件的地方了。
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
@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/ Android Runtime.cpp
?
1
2
static const RegJNIRec gRegJNI[] = {
     REG_JNI(register_android_net_LocalSocketImpl),
了解了库加载的问题后,我们接着看LocalSocketImpl.java中使用的几个重要的函数:
?
1
2
3
connect()
getInputStream()
getOutputStream()
函数具体内容不具体贴出来,其中可以看到调用了本地方法如read_native(),writeba_native()等。我们就走到native的大门了。打开本地函数文件,看下native中本地函数列表。 frameworks/base/core/jni/android_net_LocalSocketImpl.cpp
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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){}


这篇关于Android Ethernet从上至下解析一的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/717197

相关文章

网页解析 lxml 库--实战

lxml库使用流程 lxml 是 Python 的第三方解析库,完全使用 Python 语言编写,它对 XPath表达式提供了良好的支 持,因此能够了高效地解析 HTML/XML 文档。本节讲解如何通过 lxml 库解析 HTML 文档。 pip install lxml lxm| 库提供了一个 etree 模块,该模块专门用来解析 HTML/XML 文档,下面来介绍一下 lxml 库

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

android-opencv-jni

//------------------start opencv--------------------@Override public void onResume(){ super.onResume(); //通过OpenCV引擎服务加载并初始化OpenCV类库,所谓OpenCV引擎服务即是 //OpenCV_2.4.3.2_Manager_2.4_*.apk程序包,存

OWASP十大安全漏洞解析

OWASP(开放式Web应用程序安全项目)发布的“十大安全漏洞”列表是Web应用程序安全领域的权威指南,它总结了Web应用程序中最常见、最危险的安全隐患。以下是对OWASP十大安全漏洞的详细解析: 1. 注入漏洞(Injection) 描述:攻击者通过在应用程序的输入数据中插入恶意代码,从而控制应用程序的行为。常见的注入类型包括SQL注入、OS命令注入、LDAP注入等。 影响:可能导致数据泄

从状态管理到性能优化:全面解析 Android Compose

文章目录 引言一、Android Compose基本概念1.1 什么是Android Compose?1.2 Compose的优势1.3 如何在项目中使用Compose 二、Compose中的状态管理2.1 状态管理的重要性2.2 Compose中的状态和数据流2.3 使用State和MutableState处理状态2.4 通过ViewModel进行状态管理 三、Compose中的列表和滚动

Spring 源码解读:自定义实现Bean定义的注册与解析

引言 在Spring框架中,Bean的注册与解析是整个依赖注入流程的核心步骤。通过Bean定义,Spring容器知道如何创建、配置和管理每个Bean实例。本篇文章将通过实现一个简化版的Bean定义注册与解析机制,帮助你理解Spring框架背后的设计逻辑。我们还将对比Spring中的BeanDefinition和BeanDefinitionRegistry,以全面掌握Bean注册和解析的核心原理。

CSP 2023 提高级第一轮 CSP-S 2023初试题 完善程序第二题解析 未完

一、题目阅读 (最大值之和)给定整数序列 a0,⋯,an−1,求该序列所有非空连续子序列的最大值之和。上述参数满足 1≤n≤105 和 1≤ai≤108。 一个序列的非空连续子序列可以用两个下标 ll 和 rr(其中0≤l≤r<n0≤l≤r<n)表示,对应的序列为 al,al+1,⋯,ar​。两个非空连续子序列不同,当且仅当下标不同。 例如,当原序列为 [1,2,1,2] 时,要计算子序列 [

多线程解析报表

假如有这样一个需求,当我们需要解析一个Excel里多个sheet的数据时,可以考虑使用多线程,每个线程解析一个sheet里的数据,等到所有的sheet都解析完之后,程序需要提示解析完成。 Way1 join import java.time.LocalTime;public class Main {public static void main(String[] args) thro