自从Android 6.0之后adb升级到了1.0.32发现原来adb连接设备断开后再重新连接上是可以按向上箭头键(UP键)输入历史记录的,CTRL +C退出adb,但是1.0.32之后这些功能无法使用了。一直没搞懂问什么,一开始猜想是adb监听了UP键、CTRL+C特意做过处理,新版本移除掉了或者Android 的history命令发生的变化没有记录历史记录。
老版本:1.0.31(部分32版本也支持)
新版本:1.0.39
所以针对新老adb 做了对比验证:
(1)老版本adb工具adb shell后可以按向上箭头使用历史命令,但history命令显示记录是空
(2)新版本adb工具adb shell后按向上箭头无法使用历史命令,history命令显示记录也是空
(3)新老版本adb 每次进入sh进程号都发生变化
(4)history源码路径external/mksh/src/histrap.c,是编译在sh程序内的
(5)history的几个flag 在external/mksh/src/sh.h,HIST_FLUSH清空,HIST_APPEND追加,HIST_STORE存储
(6)老版本adb CTRL+C可以退出程序,CTRL+D不可以,新版本反之
(7)使用老版本adb,新开一个cmd,也无法使用向上箭头历史输入
(8)但是使用doskey /history 查看cmd输入历史却不会显示adb shell 内输入的记录,说明输入历史不是cmd 记录的,很有可能是adb自己记录的
(9)老版本adb 即使history 命令有记录,按向上方向键显示的也不是history的记录,说明这个键值并没有发送到adbd,仅仅是在Windows端就处理了
综上,很有可能,导致向上箭头不能使用历史命令的原因很有可能是按键的处理变更导致,且向上箭头的历史命令是因为cmd进程自身记录的,
不是进入adb shell后记录的。因为每次退出sh进程号都变更了,history显示都是空,当cmd进程号更新向上箭头也无法使用。
所以,问题应该就是adb.exe对向上箭头做了特殊处理,导致无法使用
有了以上分析推测,最终问题逐渐锁定在commandline.cpp 和sysdeps_win32.cpp两个文件
新版本sysdeps_win32中有对VK_UP按键的case,一开始猜测是这里对按键做了处理,但老版本压根就没有这个按键的足迹,也没有
ReadConsoleInput
等Windows终端函数的调用,再结合之前的实验,基本推翻这里对按键做处理的可能性。
新老版本对终端输入的读取是在
stdin_read_thread_loop
函数中
unix_read_interruptible
调用开始有差异的,老版本调用的
unix_read
,新版本调用的unix_read_interruptible,新版本是Windows CMD的API,老版本是unix api.移植老版本unix_read到新版本上验证。
换用老版本方式,可以输入常规字符,方向键等是无反应了,进一步证明方向键是adb.exe自己处理了
输入Enter键会导致出错,返回-1,这一点很奇怪,注意到可能unix_read有一个参数是读取长度,而这个参数在上面有if (args->raw_stdin)
buffer_size = 1; 默认是1024,而且老版本传的参数是固定1024,所以会不是因为enter键不是一个字符长度导致出错,所以打印buffer_size。
发现确实是1,所以修改
commandlie.cpp RemoteShell
函数中
bool raw_stdin = false
;再次编译验证,没有错误了,方向键也可以使用历史记录了。
最终确定就是这里的问题。
分析:老版本默认使用标准输入输出函数,每次输入后按Enter才将输入内容发送出去;新版本使用raw_stdin,每次读取一个字符,自动发送出去了,而且
使用了Windows的终端标准函数。
优缺点:老版本TAB无法补全,原因需要Enter后TAB才传进去,可以使用方向键历史记录;ctrl +D需Enter才能退出
新版本可以使用TAB补全,CTRL+D可以直接退出adb
意外收获:adb shell 可以传参数确定是否使用pty,
adb shell -t/-T
;详见adb_shell函数
猜想:那如果再将unix_read函数换回原来的unix_read_interruptible,即调用Windows 的输入输出函数呢?
结果:仍然可以使用方向键翻看历史记录,所以问题的关键是输入输出是是否采用RAW 模式
重点:如何修改新版本adb 为老版本支持方向键使用历史记录
commandlie.cpp RemoteShell函数中bool raw_stdin = (type_arg == kShellServiceArgPty ||(type_arg.empty() && command.empty()));修改为bool raw_stdin = false; 即可
源码下载:
https://download.csdn.net/download/u013463707/11250572
工具下载:
https://download.csdn.net/download/u013463707/11250580
上面写的有点乱,因为分析经历了将近3天时间,中途多次想放弃,分析过程坎坎坷坷,天马行空,分析时并没有一个清晰的思路,记录也是零零碎碎,但还是一点一点尝试验证,最终锁定了问题所在。
上面的内容来源于分析时,所做的笔记,下面是笔记全部内容
延伸:如何让Android设备支持保存history记录,不依赖电脑adb,请在下面笔记中寻找。。。
(1)老版本adb工具adb shell后可以按向上箭头使用历史命令,但history命令显示记录是空
(2)新版本adb工具adb shell后按向上箭头无法使用历史命令,history命令显示记录也是空
(3)新老版本adb 每次进入sh进程号都发生变化
(4)history源码路径external/mksh/src/histrap.c,是编译在sh程序内的
(5)history的几个flag 在external/mksh/src/sh.h,HIST_FLUSH清空,HIST_APPEND追加,HIST_STORE存储
(6)老版本adb CTRL+C可以退出程序,CTRL+D不可以,新版本反之
(7)使用老版本adb,新开一个cmd,也无法使用向上箭头历史输入
(8)但是使用doskey /history 查看cmd输入历史却不会显示adb shell 内输入的记录,说明输入历史不是cmd 记录的,很有可能是adb自己记录的
(9)老版本adb 即使history 命令有记录,按向上方向键显示的也不是history的记录,说明这个键值并没有发送到adbd,仅仅是在Windows端就处理了
综上,很有可能,导致向上箭头不能使用历史命令的原因很有可能是按键的处理变更导致,且向上箭头的历史命令是因为cmd进程自身记录的,
不是adb shell记录的。因为每次退出sh进程号都变更了,history显示都是空,当cmd进程号更新向上箭头也无法使用。
所以,问题应该就是adb.exe对向上箭头做了特殊处理,导致无法使用
CTRL+ \ (SIGQUIT)可以退出进程不退出adb shell
CTRL+C (SIGINT)
adb 源码中CTRL C处理 ctrlc_handler()
老版本: exit(STATUS_CONTROL_C_EXIT);
新版本:android::base::quick_exit(STATUS_CONTROL_C_EXIT);
获取控制台输入函数:
老版本:unix_read
新版本:unix_read_interruptible
两个函数都调用:_console_read
老版本unix_read 实现 return read(fd, buf, len);
新版本实现return TEMP_FAILURE_RETRY(unix_read_interruptible(fd, buf, len));
换用老版本方式,可以输入常规字符,方向键等是无反应了,进一步证明方向键是adb.exe自己处理了
输入Enter键会导致出错,返回-1,这一点很奇怪,注意到可能unix_read有一个参数是读取长度,而这个参数在上面有if (args->raw_stdin)
buffer_size = 1; 默认是1024,而且老版本传的参数是固定1024,所以会不是因为enter键不是一个字符长度导致出错,所以打印buffer_size。
发现确实是1,所以修改commandlie,cpp RemoteShell函数中bool raw_stdin = false;再次编译验证,没有错误了,方向键也可以使用历史记录了。
最终确定就是这里的问题。
分析:老版本默认使用标准输入输出函数,每次输入后按Enter才将输入内容发送出去;新版本使用raw_stdin,每次读取一个字符,自动发送出去了,而且
使用了Windows的终端标准函数。
优缺点:老版本TAB无法补全,原因需要Enter后TAB才传进去,可以使用方向键历史记录;ctrl +D需Enter才能退出
新版本可以使用TAB补全,CTRL+D可以直接退出adb
意外收获:adb shell 可以传参数确定是否使用pty,adb shell -t/-T ;详见adb_shell函数
猜想:那如果再将unix_read函数换回原来的unix_read_interruptible,即调用Windows 的输入输出函数呢?
结果:仍然可以使用方向键翻看历史记录,所以问题的关键是输入输出是是否采用RAW 模式
trap 命令可以捕获SIGINT等信号,使程序不退出,在adb源码中test_device.py有搜到,应该只是测试用
新版本对CTRL+C的处理
stdin_raw_init()函数中配置SetConsoleMode 去除屏蔽ENABLE_PROCESSED_INPUT即可
这样CTRL+C就可以退出程序了
参考https://www.cnblogs.com/tocy/p/console_io_function_intro.html
MSM8937 Android6.0 的adb 也是1.0.32,但此版本表现与新版本adb一样,无法使用CTRL C和向上箭头!!!
Ctrl-c Kill foreground process 常用
Ctrl-z Suspend foreground process
Ctrl-d Terminate input, or exit shell 常用 有时也会使程序退出,例如没有参数的cat命令,从终端读一行显示一行,知道Ctrl+D终结输入并终结进程
Ctrl-s Suspend output
Ctrl-q Resume output
Ctrl-o Discard output
Ctrl-l Clear screen
控制字符都是可以用(stty命令)更改的。可以用stty -a看看终端配置。
由于Linux history 默认是将历史记录保存在~/.bash_history文件
所以Android应该也支持
修改external/mksh/Android.mk 将-DHAVE_PERSISTENT_HISTORY=0 修改为1
修改external/mksh/mkshrc 添加: ${HISTFILE:=/data/local/tmp/history} 此目录下shell具有权限
export HOSTNAME TMPDIR HISTFILE
HISTSIZE保持默认即可2047
重新编译,替换新的sh和mkshrc文件
但是重启不能开机,adb也没有,通过串口抓logcat log显示
W Cryptfs : sending SIGHUP to processes with open files
01-18 08:51:33.494 242 274 E ProcessKiller: Process /system/bin/sh (269) has open file /data/local/tmp/history
01-18 08:51:33.494 242 274 W ProcessKiller: Sending Terminated to process 269
01-18 08:51:34.500 242 274 W Cryptfs : sending SIGKILL to processes with open files
01-18 08:51:34.584 242 274 E ProcessKiller: Process /system/bin/sh (269) has open file /data/local/tmp/history
01-18 08:51:34.584 242 274 W ProcessKiller: Sending Killed to process 269
Cryptfs : unmounting /data failed: Device or resource busy
data分区umount失败导致无法开机,所以只能想办法折中一下:
开机后设置HISTFILE,关机unset HISTFILE(关机不用unset)
在init.rc on property:sys.boot_completed=1 后加 export HISTFILE /data/local/tmp/history
注意initrc export 没用‘=’,sh文件有‘=’
注意放在on boot 后也无法开机,原因和放在mkshrc中一样
使用on property:persist.history.enable=1 方式也未成功,环境变量未生效,原因未知。
adb.exe 和 history实现历史记录优劣:
adb记录不同设备在这台电脑上都适用,无需设备支持,换设备仍可使用输入记录;关闭cmd或换cmd窗口清空,
history记录此设备在任何一台电脑上都适用,无需电脑支持;重启记录还在(文件存在);但需要刷定制软件
CMD CTRL+C 发出信号处理
https://bbs.csdn.net/topics/392200779
https://bbs.csdn.net/topics/390981223?page=1
Linux中Ctrl+C、Ctrl+D等按键操作&进程相关命令
https://blog.csdn.net/hellocsz/article/details/82085761
kill 2 与kill 9的区别
https://blog.csdn.net/a1010256340/article/details/75253353
如何在cmd框中实现上下键回溯历史记录
https://bbs.csdn.net/topics/390371104
1.0.31 adb 貌似只是Linux
https://github.com/louiskoo/adb
此版本adb与新的差异过大,很难找到相关线索
1.0.32 win64 adb
https://github.com/qmfrederik/adb-win64
https://github.com/findeway/adb-windows-project/tree/master/adb
adb shell中设置android系统内部环境变量
https://blog.csdn.net/u011311586/article/details/51037250
cmd 输出参数作为输入
https://www.crifan.com/windows_cmd_one_command_output_as_part_of_para_of_another_command/