本系列文章:
Android STB 遥控器适配
Android STB ROM体积精简
Android STB 添加系统接口
Android STB 高效调试技巧
Android STB 海思平台调试
Android STB HDMI开发
Android STB 编译自定义jar
在开发机顶盒ROM时,有一些高效的开发技巧,可以让开发工作效率大大提升,本篇文章基于Android4.4.2系统,简单介绍下一些常见的高效调试方法。
先说编译,是因为Android系统开发和Android应用开发有个明显不同的地方:完整ROM编译的时间会比较长,可能会达到一个小时以上,所以要谨慎全编版本。
先简单说下全编译版本,此处就以Hi3798MV300为例介绍,全编ROM的过程分为3个步骤:
1>source build/envsetup.sh
该步骤其实是调用了build/envsetup.sh脚本,该脚本的作用是初始化编译环境,并引入一些辅助的Shell函数,如mm、mmm等。
2>lunch Hi3798MV300H-eng
该步骤是调用了lunch函数,这个函数后面的参数包含两部分:硬件类型和版本类型。
3>make bigfish -j 2>&1 | tee log_Hi3798MV300H.txt
此时开始真正编译,make bigfish是编译版本的命令;2>&1是将将标准错误重定向到标准输出;tee log_Hi3798MV300H.txt是从标准输入设备读取数据,将其内容输出到标准输出设备,同时保存成文件。简单来说,该命令的作用是:开始编译版本,并且将编译日志保存到系统代码根目录下的log_Hi3798MV300H.txt文件。
说完了全编,接下来就要说单编某个模块。要单编模块,需要先执行source build/envsetup.sh和lunch Hi3798MV300H-eng命令,然后再执行单编命令。经常单编的模块如下:
1>apk
在packages/app/ApkName目录,执行mm -B即可。编出来的 Apk 一般在out/target/product/Hi3798MV300H/system/app目录。如在packages/app/Bluetooth目录下执行mm -B,则编出来的apk是out/target/product/Hi3798MV300H/system/app/Bluetooth.apk。
但也有特殊情况,如在packages/app/Settings目录执行mm -B,则编出来的apk是out/target/product/Hi3798MV300H/system/priv-app目录下。
关于system/priv-app和system/app的区别,可以简单理解为system/priv-app目录下的系统应用可以获取的权限更高一些。
2>jar
此处指的是修改framework代码,单编命令也是mm -B和mmm,常见的单编命令执行目录及其对应的jar如下:
目录 | jar |
---|---|
frameworks/base/services | services.jar |
frameworks/base/policy | android.policy.jar |
frameworks/base | framework.jar |
3>so
在调试播放器、HDMI等涉及到较底层代码时,常常会编译出so文件,如在device/hisilicon/bigfish/frameworks/hidisplaymanager/hal执行mm -B,会生成out/target/product/Hi3798MV300H/system/lib/hw/hidisplay.bigfish.so文件。
4>bin
在frameworks/base/cmds/bootanimation目录执行mm -B,会生成out/target/product/Hi3798MV300H/system/bin/bootanimation文件;在external/dhcpcd执行mm -B,会生成out/target/product/Hi3798MV300H/system/bin/dhcpcd文件。
除了这些公共的可以单编的模块,在不同的芯片平台,还有一些特殊的可以单编的模块,如在Amlogic905上,就有以下模块可以单编:
即Amlogic的bootloader,此处以gxlx_p261.dts配置为例,编译uboot文件方式如下:
1、配置编译环境
export PATH=sdk路径/toolchain/gcc-arm-none-eabi-6-2017-q2-update/bin/:$PATH
2、修改makefile脚本
diff --git a/uboot/Makefile b/uboot/Makefile
index 641b28c..551c757 100755
--- a/uboot/Makefile
+++ b/uboot/Makefile
@@ -246,7 +246,7 @@ ifeq ($(HOSTARCH),$(ARCH))
CROSS_COMPILE ?=
endif
-export CROSS_COMPILE=/opt/gcc-linaro-aarch64-none-elf-4.8-2013.11_linux/bin/aarch64-none-elf-
+export CROSS_COMPILE=sdk路径/toolchain/gcc-linaro-aarch64-none-elf-4.8-2013.11_linux/bin/aarch64-none-elf-
KCONFIG_CONFIG ?= .config
3、在sdk/uboot目录执行: ./mk gxl_p211_v1
生成的uboot文件为:uboot/fip/gxl目录下的u-boot.bin、u-boot.bin.sd.bin、u-boot.bin.usb.bl2和u-boot.bin.usb.tpl。
编译的命令为:make bootimage,此命令会同时生成boot.img和dt.img。
编译简单介绍完了,就要说调试了,机顶盒ROM的调试方式比较多样,就先接着第一章节,介绍一下上面的问题怎么替换。
第一章节编出来的文件有apk、so、bin、jar等,替换到out目录下对应的机顶盒路径中即可。这句话可能有点不好理解,例子如下:
源码目录 | 机顶盒中路径 |
---|---|
out/target/product/Hi3798MV300H/system/bin/dhcpcd | system/bin/dhcpcd |
out/target/product/Hi3798MV300H/system/framework/android.policy.jar | system/framework/android.policy.jar |
out/target/product/Hi3798MV300H/system/lib/hw/hidisplay.bigfish.so | system/lib/hw/hidisplay.bigfish.so |
out/target/product/Hi3798MV300H/system/app/BluetoothSet.apk | system/app/BluetoothSet.apk |
替换文件时,除了apk、so、jar之外,别的(还有如:20-dns.conf、bootAnimation.zip等)都要保持替换前后权限一致。查看文件权限的命令是"ll filename",文件权限为"r:4 w:2 x:1 ",常见的权限如下:
-rw------- (600) 只有拥有者有读写权限。
-rw-r--r-- (644) 只有拥有者有读写权限;而属组用户和其他用户只有读权限。
-rwx------ (700) 只有拥有者有读、写、执行权限。
-rwxr-xr-x (755) 拥有者有读、写、执行权限;而属组用户和其他用户只有读、执行权限。
-rwx--x--x (711) 拥有者有读、写、执行权限;而属组用户和其他用户只有执行权限。
-rw-rw-rw- (666) 所有用户都有文件读、写权限。
-rwxrwxrwx (777) 所有用户都有读、写、执行权限。
特殊的文件,可简单理解为不能直接替换到机顶盒中的文件,比如1.3章节中编出的文件,替换方式如下(usb_disk代表U盘挂载到终端后的路径):
1>boot.img
cat usb_disk/boot.img > /dev/block/boot
2>uboot
cat usb_disk/u-boot.bin.sd.bin > /dev/block/bootloader
要想让开发过程变得高效,常用的调试命令必须要能够熟练地使用。在不同的芯片平台上,U盘挂载后的路径是不同的,如下:
芯片 | 常规U盘路径 |
---|---|
海思 | mnt/sda/sda1 |
Amlogic | storage/external_storage/sda1 |
Mstar | mnt/usb/sda1 |
为方便表示,下文中的U盘路径均用usb_disk表示。
ADB命令 | 串口命令 | 意义 |
---|---|---|
adb connect IP:port | - | 连接 Android 设备 |
adb disconnect | - | 断开 ADB 连接 |
adb kill-server | - | 关闭 PC ADB 调试,效果等同断开连接 |
adb shell | - | 进入 shell 模式,效果等同于串口连接 |
adb remount | mount -o remount,rw /system | 将 ‘/system’ 部分置于可写入的模式 |
ll file(adb shell后) | ll file | 查看该目录下文件的详细信息,如最后修改时间、权限等 |
chmod 755 file(adb shell后) | chmod 755 file | 赋予文件755权限 |
adb reboot | reboot | 重启 |
reboot recovery(adb shell后) | reboot recovery | 重启后进入recovery |
adb install -r **.apk | pm install -r **.apk | 覆盖安装某个应用 |
adb uninstall com.exp.test | pm uninstall com.exp.test | 卸载某个应用(com.exp.test为应用的包名) |
pm list packages(adb shell后) | pm list packages | 查看已安装的应用及其对应的包名 |
adb logcat –c | logcat -c | 清除log缓存 |
logcat(adb shell后) | logcat | 直接查看运行log |
adb logcat –v threadtime >E:\1.log | logcat -v threadtime -f /sdcard/.log | 抓取1.log |
dmesg(adb shell后) | dmesg | 直接查看运行内核log |
tcpdump -s 0 -i eth0 -w /data/dhcp.pcap(adb shell后) | tcpdump -s 0 -i eth0 -w /data/dhcp.pcap | 抓取网络包 |
adb pull system/media/bootanimation.zip E:\ | cp -r system/media/bootanimation.zip usb_disk | 从终端中拷贝文件出来 |
adb push E:\Provision.apk system/app/Provision.apk | cp -r usb_disk/Provision.apk system/app/Provision.apk | 替换文件到终端中 |
adb shell sync | sync | 将内存缓冲区中的数据 写入到磁盘 |
am start -n 包名/完整Activity路径(adb shell后) | am start -n 包名/完整Activity路径 | 启动Activity |
am broadcast -a “broadcastactionfilter”(adb shell后) | am broadcast -a “broadcastactionfilter” | 发送Broadcast |
am startservice “com.exm.test/.TestService”(adb shell后) | am startservice “com.exm.test/.TestService” | 启动Service |
content query --uri content://stbconfig/authentication(adb shell后) | content query --uri content://stbconfig/authentication | 查询ContentProvider内容 |
am force-stop com.exp.test(adb shell后) | am force-stop com.exp.test | 强制关闭应用 |
screencap /sdcard/screen.png(adb shell后) | screencap /sdcard/test.png | 截屏 |
screenrecord /sdcard/demo.mp4(adb shell后) | screenrecord /sdcard/demo.mp4 | 录制视频 |
input text “123”(adb shell后) | input text “123” | 输入一段字符串 |
input keyevent 23(adb shell后) | input keyevent 23 | 模拟按键 |
dumpsys -l/service list(adb shell后) | dumpsys -l/service list | 查看当前运行的Service |
dumpsys package(adb shell后) | dumpsys package | 查看应用的四大组件信息 |
dumpsys activity (adb shell后) | dumpsys activity | 查询AMS服务相关信息 |
dumpsys window(adb shell后) | dumpsys window | 查询WMS服务相关信息 |
dumpsys cpuinfo(adb shell后) | dumpsys cpuinfo | 查询CPU情况 |
dumpsys meminfo(adb shell后) | dumpsys meminfo | 查询内存情况 |
top(adb shell后) | top | 查询试试内存情况 |
ps(adb shell后) | ps | 查看进程 |
start adbd(adb shell后) | start adbd | 启用adbd,可以使用adb连接功能 |
stop adbd(adb shell后) | stop adbd | 禁用adbd |
start console(adb shell后) | start console | 启用串口调试功能 |
stop console(adb shell后) | stop console | 禁用串口调试功能 |
getprop(adb shell后) | getprop | 查看属性 |
setprop prop_name prop_val(adb shell后) | setprop prop_name prop_val | 设置属性值 |
cat node_path(adb shell后) | cat node_path | 查看节点值 |
remotecfg(adb shell后,Amlogic上使用) stop ir_user(adb shell后,海思上使用) busybox devmem 0x1f007b00 16 0x0000 (adb shell后,Mstar9380上使用) |
remotecfg(Amlogic上使用) stop ir_user(海思上使用) busybox devmem 0x1f007b00 16 0x0000 (Mstar9380上使用) |
禁用红外遥控器使用功能 |
./system/bin/remotecfg.sh(adb shell后,Amlogic上使用) start ir_user(adb shell后,海思上使用) busybox devmem 0x1f007b00 16 0x01bf(adb shell后,Mstar9380上使用) |
./system/bin/remotecfg.sh(Amlogic上使用) start ir_user(海思上使用) busybox devmem 0x1f007b00 16 0x01bf (Mstar9380上使用) |
启用红外遥控器使用功能 |
settings get secure ntp_server (adb shell后) | settings get secure ntp_server | 查看系统数据库中值 |
settings put secure ntp_server cn.pool.ntp.org(adb shell后) | settings put secure ntp_server cn.pool.ntp.org | 设置系统数据库中值 |
wm size(adb shell后) | wm size | 查看UI分辨率 |
wm size 1920x1080(adb shell后) | wm size 1920x1080 | 修改UI分辨率 |
wm density(adb shell后) | wm density | 查看屏幕密度 |
wm density 240(adb shell后) | wm density 240 | 修改屏幕密度 |
cat /proc/cpuinfo(adb shell后) | cat /proc/cpuinfo | 查看CPU信息 |
LINUX命令 | 意义 |
---|---|
cd / cd - cd dirA |
切换到根目录 切换到上次操作目录 切换到dirA目录 |
pwd | 查看当前目录 |
su userName | 切换用户 |
df | 查看磁盘使用情况 |
mkdir | 创建目录 |
rm –f fileName | 强制删除文件 |
rm –rf folderName | 强制删除文件夹(慎用) |
mv aName bName | 文件(夹)重命名 |
cp –rf dirA dirB | 拷贝文件夹 A 内容到文件夹 B |
find . –name fileName | 在当前目录搜索某文件 |
grep –rin “str” ./* | 在当前目录检索字符串 |
tar –cvf test.tar ./fName | 压缩 fName 目录文件为 test.tar |
tar –xvf a.tar | 解压 test.tar |
make clean | 清除编译内容 |
busybox md5sum fileName | 查看文件 md5 |
echo 7 > /proc/sys/kernel/printk | 修改内核log等级 |
cat /proc/sys/kernel/printk | 查看内核log等级 |
java -Xmx2048m -jar signapk.jar -w platform.x509.pem platform.pk8 test.apk test_sign.apk | apk签名 |
java -Xmx2048m -jar signapk.jar -w testkey.x509.pem testkey.pk8 update.zip update_signed.zip | update.zip签名 |
此外,还有一些不那么常用、却很有用的命令,可以尽量掌握下:
1>lsmod
查看已加载的模块,输出结果和"cat /proc/modules"一致,示例内容如下:
dwc_otg 282708 0 - Live 0x0000000000000000
dwc3 18053 0 - Live 0x0000000000000000
bt_usb 14905 0 - Live 0x0000000000000000 (O)
mac80211 412290 0 - Live 0x0000000000000000
cfg80211 401371 1 mac80211, Live 0x0000000000000000
aml_thermal 18902 0 - Live 0x0000000000000000 (O)
tb_detect 5521 0 - Live 0x0000000000000000 (O)
mali 191194 26 - Live 0x0000000000000000 (O)
aml_nftl_dev 81488 0 - Live 0x0000000000000000 (PO)
第1列表示模块的名称,第2列表示模块的大小,第3列表示依赖模块的个数,第4列表示依赖模块的内容。
2>cat /proc/meminfo
查看内存信息。
VIM命令 | 意义 |
---|---|
vi fileName | 打开文件 |
:set nu | 显示行数 |
/ | 检索 |
:w | 保存修改 |
:q | 退出 |
i | 进入编辑模式 |
Esc | 退出编辑模式 |
dd | 删除一行 |
VIM命令 | 意义 |
---|---|
git branch | 查看本地分支 |
git status . | 查看当前目录哪些文件修改过还未提交 |
git diff filename/folderName | 查看某个文件/文件夹具体修改内容 |
git log | 查看本地所以修改记录 |
git log filename/folderName | 查看某个文件/文件夹修改记录 |
git checkout fileName | 回退某个文件未提交的修改 |
git pull | 更新本地代码,与服务器同步 |
git show commitId | 查看某次 commit 内容 |
git show –-stat commitId | 查看某次提交修改的文件 |
git clone + gitUrl | 拉取远程代码到本地 |
git checkout A B | 从分支 A 切换到分支 B |
git add/rm | 添加到暂存区/删除文件 |
git commit –m “” | 将修改提交到本地 |
git push origin branchRemoteName | 将本地分支修改提交到远程分支 |
首先,要尽量将上述ADB命令与" |grep "结合使用,如"ll/ls"与" |grep"使用,可过滤特定名称的文件/文件夹;“getprop"与” |grep "使用,可过滤特定名称的属性;“dumpsys meminfo"与” |grep"使用,可过滤特定名称
应用的内存使用情况;“logcat"与” |grep "使用,可过滤某些特定日志。
其次,多使用模糊搜索/特定目录搜索,如:"find frameworks/base –name Window.java"可搜索 frameworks/base 目录下名称中带 Window 的java文件。
再者,在 shell 界面使用 linux 命令时,命令前都要加上"busybox"。
最后,在向机顶盒中 push 文件或从机顶盒向外 pull 文件后,尽量使用一下"adb shell sync",来确保文件传输完整。
在以上两个章节中,涵盖了较多的日常机顶盒ROM调试命令。除此之外,还有些命令之外的技巧需要积累,如下:
某些功能,在源码中是已经实现好了的,在检测ROM中是否包含该功能时,只需要检查特定的属性或文件即可。如:
1>海思上的CEC开关
persist.sys.hdmi.cec为true,代表打开该功能;为false,代表关闭该功能。
2>海思上的HDMI待机开关
persist.hdmi.suspend.enable为1,代表打开该功能;为0,代表关闭该功能;
persist.hdmi.suspend.time为具体的待机时间,一般为5。
3>Amlogic静帧功能
system/etc目录下的blackout_whitelist.txt文件,文件内逐行写入要实现静帧功能的应用的包名、类名。
3>Amlogic DHCP重连功能
net.dhcp.repeat为enabled,代表打开DHCP重连功能;
net.dhcp.repeat.count为重试次数。
当某一功能实现出现问题,要遵循"先硬件/环境,再软件"和"先底层,再上层"的排查思路,如下:
1>先硬件/环境,再软件
遇到一个问题时,要先了解问题出现的具体情况,是否与环境、复现问题方式、机顶盒硬件有关,先做对比验证。确定是软件问题后,再从软件侧进行排查。
2>先底层,再上层
遇到一个问题时,可以先用命令从底层了解功能是否OK。如果底层OK,那就是上层调用逻辑出现了问题,再层层向上、逐步缩小debug范围。
至此,常用的调试技巧已介绍完毕。