概述
陆陆续续接触Android已经一年有余了,记得最初开始接触的是Honeycomb版本,当时那种青涩啊,那种看到sp<>,看到aidl时的惶恐还记忆犹新。。
算了,快写成抒情文了
我喜欢在PC上跑Android(效果还不是一般的好),最近因为要调试一些网络的东西,所以需要联网
三种主流选择:
1是WIFI
2是mobile
3是Ethernet
Mobile当然没有了,没有选择WIFI是因为X86上需要WIFI设备,还需要热点于是想用Ethernet在网上搜了下Android ethernet的方案,都貌似是froyo上面的,以前的版本貌似还没有ethernet支持,大神们做了很多工作
在新的代码上搜了一遍,居然找到了EthernetDataTracker.java这个文件
(说明一下,笔者所用的代码都是谷歌的原生Android代码,版本为4.1)
http://source.android.com/source/downloading.html
#repo init -u https://android.googlesource.com/platform/manifest -b android-4.1.1_r4
#repo sync
回到正题
有这个文件至少说明谷歌是针对了以太网的,所以可能不需要移植什么的(到底是不是呢?惶恐啊)总得找个切入点
我用鼠标点了下桌面上的默认的google search
看了看后面的Logcat,看到了一个小清新类:GoogleSuggestClient
又发现这个类有个巨牛X的方法:isNetworkConnected()
再到ConnectivityManager.getActiveNetworkInfo()一看又是那套service模式了
getActiveNetworkInfo说明系统可能有很多网络连接,但只能使用一个(要么以太网要么手机网络要么wifi)
继续跟踪到service吧
ConnectivityService.java
- public NetworkInfo getActiveNetworkInfo() {
- enforceAccessPermission();
- final int uid = Binder.getCallingUid();
- return getNetworkInfo(mActiveDefaultNetwork, uid);
- }
- private NetworkInfo getNetworkInfo(int networkType, int uid) {
- NetworkInfo info = null;
- if (isNetworkTypeValid(networkType)) {
- final NetworkStateTracker tracker = mNetTrackers[networkType];
- if (tracker != null) {
- info = getFilteredNetworkInfo(tracker, uid);
- }
- }
- return info;
- }
还有一个mActiveDefaultNetwork,这一定是默认连接了
一开始打印了下返回去的NetworkInfo,居然是个Null,这也难怪,我一个PC上面WIFI,MOBILE网络都没有嘛
但是我的以太网是好的(用ubuntu可以上网),这说明上面的framework可能和下面没有接上。
想法设法证实这一点吧
入口点当然是这组NetworkStateTracker是在哪初始化的
第一个想到的当然是ConnectivityService的构造了
至于构造又是在哪调用的,应该是大名鼎鼎的SystemServer了
看看构造
ConnectivityService.java
- <span style="white-space:pre"> </span>String[] raStrings = context.getResources().getStringArray(
- com.android.internal.R.array.radioAttributes);
- for (String raString : raStrings) {
- RadioAttributes r = new RadioAttributes(raString);
- ......
- mRadioAttributes[r.mType] = r;
- }
- String[] naStrings = context.getResources().getStringArray(
- com.android.internal.R.array.networkAttributes);
- for (String naString : naStrings) {
- try {
- NetworkConfig n = new NetworkConfig(naString);
- ......
- mNetConfigs[n.type] = n;
- mNetworksDefined++;
- } catch(Exception e) {
- // ignore it - leave the entry null
- }
- }
- <span style="white-space:pre"> </span>for (int netType : mPriorityList) {
- switch (mNetConfigs[netType].radio) {
- case ConnectivityManager.TYPE_WIFI:
- mNetTrackers[netType] = new WifiStateTracker(netType,
- mNetConfigs[netType].name);
- mNetTrackers[netType].startMonitoring(context, mHandler);
- break;
- case ConnectivityManager.TYPE_MOBILE:
- mNetTrackers[netType] = new MobileDataStateTracker(netType,
- mNetConfigs[netType].name);
- mNetTrackers[netType].startMonitoring(context, mHandler);
- break;
- ......
- case ConnectivityManager.TYPE_ETHERNET:
- mNetTrackers[netType] = EthernetDataTracker.getInstance();
- mNetTrackers[netType].startMonitoring(context, mHandler);
- break;
- default:
- loge("Trying to create a DataStateTracker for an unknown radio type " +
- mNetConfigs[netType].radio);
- continue;
- }
- mCurrentLinkProperties[netType] = null;
- if (mNetTrackers[netType] != null && mNetConfigs[netType].isDefault()) {
- mNetTrackers[netType].reconnect();
- }
- }
com.android.internal.R.array.radioAttributes
com.android.internal.R.array.networkAttributes
分别保存到
mRadioAttributes和mNetConfigs两个数组里面
后面的初始化就靠这两个东西了
找到xml文件
/frameworks/base/core/res/res/values/config.xml
- <string-array translatable="false" name="networkAttributes">
- <item>"wifi,1,1,1,-1,true"</item>
- <item>"mobile,0,0,0,-1,true"</item>
- <item>"mobile_mms,2,0,2,60000,true"</item>
- <item>"mobile_supl,3,0,2,60000,true"</item>
- <item>"mobile_hipri,5,0,3,60000,true"</item>
- <item>"mobile_fota,10,0,2,60000,true"</item>
- <item>"mobile_ims,11,0,2,60000,true"</item>
- <item>"mobile_cbs,12,0,2,60000,true"</item>
- <item>"wifi_p2p,13,1,0,-1,true"</item>
- </string-array>
- <string-array translatable="false" name="radioAttributes">
- <item>"1,1"</item>
- <item>"0,1"</item>
- </string-array>
看到上面那个没,貌似只有wifi和mobile的定义,简直无视了以太网
赶紧补上,
networkAttributes:
- <span style="white-space:pre"> </span><item>"eth,9,9,4,60000,true"</item>
radioAttributes
- <span style="white-space:pre"> </span><item>"9,1"</item>
看这个分支:
- case ConnectivityManager.TYPE_ETHERNET:
- mNetTrackers[netType] = EthernetDataTracker.getInstance();
- mNetTrackers[netType].startMonitoring(context, mHandler);
- break;
继续追踪到startMonitoring
EthernetDataTracker.java
- public void startMonitoring(Context context, Handler target) {
- mContext = context;
- mCsHandler = target;
- // register for notifications from NetworkManagement Service
- IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- mNMService = INetworkManagementService.Stub.asInterface(b);
- mInterfaceObserver = new InterfaceObserver(this);
- sIfaceMatch = context.getResources().getString(
- com.android.internal.R.string.config_ethernet_iface_regex);
- try {
- final String[] ifaces = mNMService.listInterfaces();
- for (String iface : ifaces) {
- if (iface.matches(sIfaceMatch)) {
- mIface = iface;
- mNMService.setInterfaceUp(iface);
- InterfaceConfiguration config = mNMService.getInterfaceConfig(iface);
- mLinkUp = config.isActive();
- if (config != null && mHwAddr == null) {
- mHwAddr = config.getHardwareAddress();
- if (mHwAddr != null) {
- mNetworkInfo.setExtraInfo(mHwAddr);
- }
- }
- reconnect();
- break;
- }
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Could not get list of interfaces " + e);
- }
- }
这里先取出一个xml属性
com.android.internal.R.string.config_ethernet_iface_regex
放到sIfaceMatch中,这个一看就是个匹配字符串
这里匹配的是所有eth\d,查了下JAVA的正则表达式,就是eth0,eth1什么的
mNMService.listInterfaces()
这个什么NMService不是尼玛Service,而是NetworkManagerService
显然有call到一个管网络连接的Service去了
跟踪到NetworkManagerService.java
- public String[] listInterfaces() {
- mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- try {
- return NativeDaemonEvent.filterMessageList(
- mConnector.executeForList("interface", "list"), InterfaceListResult);
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
- }
- }
mConnector:NativeDaemonConnector
继续跟踪:
- public NativeDaemonEvent[] execute(int timeout, String cmd, Object... args)
- throws NativeDaemonConnectorException {
- final ArrayList<NativeDaemonEvent> events = Lists.newArrayList();
- final int sequenceNumber = mSequenceNumber.incrementAndGet();
- final StringBuilder cmdBuilder =
- new StringBuilder(Integer.toString(sequenceNumber)).append(' ');
- final long startTime = SystemClock.elapsedRealtime();
- makeCommand(cmdBuilder, cmd, args);
- final String sentCmd = cmdBuilder.toString(); /* logCmd + */
- synchronized (mDaemonLock) {
- if (mOutputStream == null) {
- throw new NativeDaemonConnectorException("missing output stream");
- } else {
- try {
- mOutputStream.write(sentCmd.getBytes(Charsets.UTF_8));
- } catch (IOException e) {
- throw new NativeDaemonConnectorException("problem sending command", e);
- }
- }
- }
- <span style="white-space:pre"> </span>......
- }
参数args就是"interface"
这里把两个字符串组成了一个一定格式的command
最后发送出去:
mOutputStream.write(sentCmd.getBytes(Charsets.UTF_8));
mOutputStream是哪来的啊。。命令到底发送到哪里去了??
只好跟踪mOutputStream
- private void listenToSocket() throws IOException {
- LocalSocket socket = null;
- try {
- socket = new LocalSocket();
- LocalSocketAddress address = new LocalSocketAddress(mSocket,
- LocalSocketAddress.Namespace.RESERVED);
- socket.connect(address);
- InputStream inputStream = socket.getInputStream();
- synchronized (mDaemonLock) {
- mOutputStream = socket.getOutputStream();
- }
原来NativeDaemonConnector会去启动一个线程,这个线程首先会call到native去初始化一个socket
android_net_LocalSocketImpl.cpp
- static jobject
- socket_create (JNIEnv *env, jobject object, jboolean stream)
- {
- int ret;
- ret = socket(PF_LOCAL, stream ? SOCK_STREAM : SOCK_DGRAM, 0);
- if (ret < 0) {
- jniThrowIOException(env, errno);
- return NULL;
- }
- return jniCreateFileDescriptor(env,ret);
- }
这个mSocket是NativeDaemonConnector初始化的时候就有了的
NativeDaemonConnector是NetworkManagerService的小弟
- private NetworkManagementService(Context context) {
- mContext = context;
- mConnector = new NativeDaemonConnector(
- new NetdCallbackReceiver(), "netd", 10, NETD_TAG, 160);
- mThread = new Thread(mConnector, NETD_TAG);
- }
再看看connect函数,烂七八糟的一直会call到JNI去
- static void
- socket_connect_local(JNIEnv *env, jobject object,
- jobject fileDescriptor, jstring name, jint namespaceId)
- {
- int ret;
- const char *nameUtf8;
- int fd;
- nameUtf8 = env->GetStringUTFChars(name, NULL);
- fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
- ret = socket_local_client_connect(
- fd,
- nameUtf8,
- namespaceId,
- SOCK_STREAM);
- env->ReleaseStringUTFChars(name, nameUtf8);
- }
- int socket_local_client_connect(int fd, const char *name, int namespaceId,
- int type)
- {
- struct sockaddr_un addr;
- socklen_t alen;
- size_t namelen;
- int err;
- err = socket_make_sockaddr_un(name, namespaceId, &addr, &alen);
- if (err < 0) {
- goto error;
- }
- if(connect(fd, (struct sockaddr *) &addr, alen) < 0) {
- goto error;
- }
- return fd;
- error:
- return -1;
- }
- int socket_make_sockaddr_un(const char *name, int namespaceId,
- struct sockaddr_un *p_addr, socklen_t *alen)
- {
- memset (p_addr, 0, sizeof (*p_addr));
- size_t namelen;
- switch (namespaceId) {
- case ANDROID_SOCKET_NAMESPACE_RESERVED:
- namelen = strlen(name) + strlen(ANDROID_RESERVED_SOCKET_PREFIX);
- /* unix_path_max appears to be missing on linux */
- if (namelen > sizeof(*p_addr)
- - offsetof(struct sockaddr_un, sun_path) - 1) {
- goto error;
- }
- strcpy(p_addr->sun_path, ANDROID_RESERVED_SOCKET_PREFIX);
- strcat(p_addr->sun_path, name);
- break;
- default:
- // invalid namespace id
- return -1;
- }
- p_addr->sun_family = AF_LOCAL;
- *alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1;
- return 0;
- error:
- return -1;
- }
这里使用的是UNIX域的socket,在最后的socket_make_sockaddr_un函数中
参数name便是"netd"
ANDROID_RESERVED_SOCKET_PREFIX是"/dev/socket"
所以合起来的socket地址是"/dev/socket/netd"
之前的送的"list interface"就是送到这个socket去了,同时也发现,这个NativeDaemonConnector也在不停的倾听这个socket,了解那边发生了什么
在这里猜到,“那边”有个东西可以接收我们的命令,然后返回结果
"那边"的这个东西也可能主动向我们汇报一些事件
神奇的那边
我又搜索了一下代码
发现那边就是netd这个守护进程。。
哎 第一次认真写博客,发现还不是一般的累
netd下次再写吧。。搞几把DOTA。。
向所有的原创bloger致敬!
凡是有始有终 继续写吧
netd = net daemon
目的是为了监视网络状况,比如带宽变化,网络设备的增加/移除
netd时候在init执行的时候被启动的
看看init.rc有这么一段:
service netd /system/bin/netd
class main
socket netd stream 0660 root system
socket dnsproxyd stream 0660 root system
socket mdns stream 0660 root system
init程序解释执行这一段时会执行service_start
- void service_start(struct service *svc, const char *dynamic_args)
- {
- ... ...
- NOTICE("starting '%s'n", svc->name);
- pid = fork();
- if (pid == 0) {
- struct socketinfo *si;
- struct svcenvinfo *ei;
- char tmp[32];
- int fd, sz;
- umask(077);
- for (si = svc->sockets; si; si = si->next) {
- int socket_type = (
- !strcmp(si->type, "stream") ? SOCK_STREAM :
- (!strcmp(si->type, "dgram") ? SOCK_DGRAM : SOCK_SEQPACKET));
- int s = create_socket(si->name, socket_type,
- si->perm, si->uid, si->gid);
- if (s >= 0) {
- publish_socket(si->name, s);
- }
- }
- setpgid(0, getpid());
- /* as requested, set our gid, supplemental gids, and uid */
- if (!dynamic_args) {
- if (execve(svc->args[0], (char**) svc->args, (char**) ENV) < 0) {
- ERROR("cannot execve('%s'): %sn", svc->args[0], strerror(errno));
- }
- } else {
- char *arg_ptrs[INIT_PARSER_MAXARGS+1];
- int arg_idx = svc->nargs;
- char *tmp = strdup(dynamic_args);
- char *next = tmp;
- char *bword;
- /* Copy the static arguments */
- memcpy(arg_ptrs, svc->args, (svc->nargs * sizeof(char *)));
- while((bword = strsep(&next, " "))) {
- arg_ptrs[arg_idx++] = bword;
- if (arg_idx == INIT_PARSER_MAXARGS)
- break;
- }
- arg_ptrs[arg_idx] = '