应该有不少开发Android系统的人有这样的感觉,修改好了hal或者jni后,编译,验证的时候需要把编译的文件发到机器上去,那么发送的时候使用什么方式,应该是adb吧,我也是使用该方法。可是实际使用的时候是很烦心的,比如,adb有时候的识别问题,这次发送成功了下次可能就出点毛病,再捣鼓捣鼓,几分钟过去了,程序还没验证,毫无效率;还比如识别还好,在一次发送过程中识别的速度也并不快,如果验证的文件需要重启机器,那么下次发送还是需要慢的识别。总之调试起来不是很舒服。不知道大神是怎么玩这个的,如果有好的方法,告诉我一下。
于是为了方便的调试编译结果,就想使用nfs方式挂载Android系统,这样就不需要往机器上频繁的发送文件了。想法是美好的,现实是骨感的。
思路上是可以的,可是问题是,Android使用nfs怎么跑起来呢。
网上搜索了很多的资料,大多都是一个意思,在out的板级目录下,找到rootfs和system组合成文件系统目录,然后放到建立的nfs文件下,在Android开发板上设置u-boot的启动环境,来挂载nfs。思路上没问题,然而这样的思路只是在跑Linux的过程中有用,开始启动Android的时候就挂了,网络中断。
下面是我使用的环境设置:
setenv bootargs root=/dev/nfs console=ttyAMA0,115200 androidboot.hardware=s5p4418_realv1c androidboot.console=ttyAMA0 androidboot.serialno=0123456789abcdef init=/init nfsroot=172.16.24.7:/wsh-space/nfsboot/4418/android/system ip=172.16.24.6:172.16.24.7:172.16.24.254:255.255.255.0::eth0:on
init.rc文件内容需要注释掉下面
# once everything is setup, no need to modify /
#mount rootfs rootfs / ro remount
注意在init.rc文件中务必确定有下面两句,一般在on boot下:
class_start core #star core class
class_start main #star main class
这两个是用来表示启动的类型,每一个服务下都有一个class字段,后边跟的就是类型,一般为core、main。如下:
service vold /system/bin/vold
class core
socket vold stream 0660 root mount
ioprio be 2
service netd /system/bin/netd
class main
socket netd stream 0660 root system
socket dnsproxyd stream 0660 root inet
socket mdns stream 0660 root system
socket fwmarkd stream 0660 root inet
/dev/block/platform/dw_mmc.0/by-num/p7 /data ext4 noatime,nosuid,nodev,nomblk_io_submit,errors=panic wait,check
这里说明一下为什么这样,刚开始我是全部注释掉,直接使用nfs文件系统目录下的data文件夹,可是由于权限的问题,在修改和写入数据时总是报告无权限错误,原因不详,无奈之下就使用了开发板原本挂载的data目录。由于data目录为系统启动后的目录,对于系统的原文件无影响,所以使用开发板的也是可以的,一般调试的文件都在system目录下,以及以下.rc文件。
做了上面的修改后理论上应该可以挂载了,可是实际上是不行的,前面也说了,走Linux启动部分时挂载正常无问题,可是走到Android启动各种服务后就挂掉了,网络失去联系。经过各种跟踪排查(接触的不够深刻,开始不知道从哪里查起,走了不少的弯路),总算是定位到问题所在了,是netd服务的问题。
找到netd服务很不容易,走了很多的弯路,不过也值得,认识的更多了。
在查找的时候有个问题,怎么查看Android系统的日志,也就是想看Android启动的log,进而查看死在了什么地方,由于Android系统未启动,在启动过程中卡主了,那么logcat命令也就无法输入了,怎么办呢。于是想使用重定位的功能,把log输出到文件,可是行不通,因为读取的main日志为空,不知道为什么,可能是外部独立的进程无法读取。这个行不通就想使用往文件写入数据来代替log,也就是使用open、read、write函数实现的,这个方法确实可以,可以得到想要的输出,但是需要修改源码不少,目前还不想这么整,因为偶然想到了另外的方法,不过该方法在其他都不可使用时用来调试jni和hal(仅限系统无法启动的过程,能够启动就不需要了)还是可以的,很笨的方法。
那么偶然想到的方法是什么呢。Linux系统启动部分nfs挂载是没问题的,在加载Android库和虚拟机的时候会有一个时间差,大概有几秒的时间吧,应该不超过五秒,这个时候是可以接受终端输入的命令的,这时候手动或者直接粘贴的方式,输入logcat命令就可以打印log信息了,即使是卡主也不会影响日志的输出了。试了一下果然可以,嘿嘿。
随着系统的启动发现启动netd服务后Android的nfs就挂掉了,看来是netd服务修改了Linux的网络数据,导致ip等信息改变,进而使网络中断,无法进行系统的挂载。
既然这样那么试试不让netd启动,看看结果是什么,做如下修改:
service netd /system/bin/netd
#class main
socket netd stream 0660 root system
socket dnsproxyd stream 0660 root inet
socket mdns stream 0660 root system
socket fwmarkd stream 0660 root inet
注释掉class main那一行,重启后发现可以过了,而且进入了Android启动的界面,在更新app,当然了,没有netd服务Android系统是无法启动的,因为网络的相关服务netd是必须的,Android系统就卡在了更新完app状态下,打印logcat,日志提示网络服务的相关问题,再次以失败告终。
离目标很近了,看来是需要修改netd的源码了,不过没精力搞了,以后再研究吧。
需要补充一下,如果可以实现了应该还需要做如下的工作:
在调试过程中为了保证Android启动后网络就是打开的,修改了源码EthernetServiceImpl.java(路径/frameworks/opt/net/ethernet/java/com/android/server/ethernet)中的star函数,添加了下面的一行,来默认开启以太网:
Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.ETHERNET_ON,EthernetManager.ETH_STATE_ENABLED);
另外还需要一个配置文件ipconfig.txt,路径为data/misc/ethernet/ipconfig.txt,该文件用来设置Android启动后为静态IP,让其与u-boot中的IP一致。
ipconfig.txt文件的获得方法就不多说了,可以按照源码的格式写一个出来(源码位置frameworks/base/services/core/java/com/android/server/net/IpConfigStore.java),函数是readIpAndProxyConfigurations。也可以在Android系统中设置为静态保存,然后把文件拷贝出来。
以上为nfs挂载的记录,有了解的朋友可以一起交流。
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
下面为另外一种相对来说调试方便的方法。应该可以对以太网和wifi网络都有效,我只使用了以太网方式。
该方法是使用nfs挂载PC上的一个目录,把编译的文件复制到pc的nfs目录,在开发板上再执行一个复制命令拷贝到指定位置即可完成传输,相对使用adb应该快一些,但是弊端是网络换得好,以及支持局域网,也就是需要有wifi或者以太网。
在使用nfs之前我们需要一个工具,就是busybox,原因是Android自带的mount工具是阉割版,功能不全,nfs的文件系统无法挂载,所以只能使用busybox工具来实现了,busybox自带mount命令和umount命令。把busybox通过adb方式push到/system/xbin目录下,并修改为777权限,保证可执行。
挂载的命令如下,注意在执行下面命令前确保网络是通的。
busybox mount -t nfs -o nolock 172.16.24.7:/work-space/nfsboot/4418/android/workspace_nfs /data/work
其中/data/work为开发板上的目录,可以自由设置,不过最好在data目录下,避免因为权限引起的问题。
这样在开发板操作/data/work目录即是在操作PC上的目录,拷贝文件什么的就很方便了。也无需USB连接的需要,没有识别不稳定的问题了。
为了挂载和卸载方便,可以做成.sh脚本的形式,比如:
my_mount.sh
#!/bin/sh
busybox mount -t nfs -o nolock 172.16.24.7:/work-space/nfsboot/4418/android/workspace_nfs /data/work
my_umount.sh
#!/bin/sh
busybox umount /data/work
把这两个文件放到/system/xbin目录下即可直接调用了,更加方便,对于调试的库文件,也可以写成脚本的形式,编译完直接执行脚本即可完成传输。
记录完毕。