Android用命令行启动应用

Android是基于Linux内核的操作系统,用Java写的应用程序被Android运行时虚拟机运行。

因为Android是基于Linux的,而Linux执行ELF格式的可执行文件,所以用C++编写的ELF格式的可执行文件也可以在Android运行,但有些限制。首先,Android /sdcard目录不能给文件设置+x可执行位,而把文件复制到其他文件夹需要root权限。其次,把一般Linux里的ELF可执行文件复制到Android,是不能运行的,因为系统架构等不匹配。但是,从Android Open Source Project(AOSP)源代码里可以验证,Android操作系统里的许许多多的功能都是C++写的ELF格式的可执行程序,只要在AOSP里恰当地写一个C++程序,恰当地编译,是可以在Android运行的。

因为Android运行时(Android Runtime,ART)是一个Java虚拟机,所以Android可以运行Java程序。Java程序包括带有main函数的程序(我把它称为纯Java程序)和Android Studio生成的所谓的应用。

可以想到,Android运行时一定是个C++程序。要运行纯Java程序的代码类似art hello.class

在Android系统中,应用程序是由Launcher启动起来的,其实,Launcher本身也是一个应用程序,其它的应用程序安装后,就会Launcher的界面上出现一个相应的图标,点击这个图标时,Launcher就会对应的应用程序启动起来。[1]罗升阳. Android应用程序启动过程源代码分析. . 2011-08-19 [2019-05-20].

通过学习Android OS编译流程可以知道,ART会被编译为两份,一份是host,一份是target。target指手机上运行的ART。host版本把Android预装的应用编译为ELF二进制文件(?)。既然有host ART,下文介绍的用命令行启动纯Java程序可以在电脑上运行。

运行方式为

out/host/linux-x86/bin/art --32 -cp ~/Project/Java/2/Hello.dex 
    -EnableRWProfiling:true -EnableHeapSizeProfiling:false Hello

[2]out/host/linux-x86/bin/art的源文件是art/tools/art。

用命令行启动纯Java程序

dalvikvm是Android 4.4以前就存在的命令,在4.4以后其内部调用art。[3]JesusFreke. How to execute the dex file in android with command?. . 2019-01-26 [2019-05-20].TODO:显示源代码

调用方式如下

dalvikvm -cp /sdcard/Hello.dex -EnableRWProfiling:true -EnableHeapSizeProfiling:true Hello

-EnableRWProfiling:true -EnableHeapSizeProfiling:true是我自己定义的两个art虚拟机选项。

app_process是Android 4.4加入的命令,在内部调用art。app_process的源代码在frameworks/base/cmds/app_process/app_main.cpp,文件开头有注释

/*
 * Main entry of app process.
 *
 * Starts the interpreted runtime, then starts up the application.
 *
 */

这样看来用app_process启动程序,一定会用解释方式执行,不会进行AOT静态编译,把代码编译成本地代码。我猜JIT即时编译仍然可能发生。

app_process的调用方式如下

app_process -Djava.class.path=Hello.dex -EnableRWProfiling:true -EnableHeapSizeProfiling:true /sdcard/ Hello
app_process是Android上所有Java应用进程的起源。init.zygote32.rc在系统启动阶段启动app_process,并把它命名为zygote。[4]罗升阳. Android系统进程Zygote启动过程的源代码分析. . 2011-09-19 [2019-06-02].

用命令行启动应用

用命令行启动应用需要指定应用的包名和活动名。

adb shell cmd package list packages可以列出所有包名:

...
package:com.android.managedprovisioning
package:com.android.dreams.phototable
package:com.facebook.katana
package:com.android.smspush
...

可见facebook的包名是com.facebook.katana。

注,Android 7.0以前需要使用命令pm list packages[5]Todd Kennedy. Implement shell commands for package and user services
. . 2015-10-30 [2019-05-20].

应用的AndroidManifest.xml文件写着默认活动名,就是在启动器上点击应用图标,启动的活动。但是apk是压缩文件,但直接解压缩的话,AndroidManifest.xml是乱码的。我们要用apktool解压缩apk文件。Android SDK里面没有这样的工具。

注意AndroidManifest.xml属于资源文件,运行apktool的时候不能加选项--no-res。如果apktool报错“Exception in thread “main” brut.androlib.AndrolibException: unsupported res type name for bags. Found: style2”,参见https://github.com/iBotPeaches/Apktool/issues/1719 ,可以用thejunkjon的fork

在解压后的AndroidManifest.xml里面搜索“android.intent.action.MAIN”,会找到

<activity android:name="com.facebook.katana.LoginActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <category android:name="android.intent.category.LAUNCHER"/>
    </intent-filter>
</activity>

说明com.facebook.katana.LoginActivity是默认activity。有的程序会把这个值写成”.LoginActivity“,前面的包名可以省略。[6]ccpat. Android应用的默认Activity配置. . 2017-01-24 [2019-05-20].

知道了包名和活动名,就可以启动应用了。

am start -n com.facebook.katana/.LoginActivity

调用cat /system/bin/am,可以发现am是一个Bash脚本。

#!/system/bin/sh

if [ "$1" != "instrument" ] ; then
    cmd activity "$@"
else
    base=/system
    export CLASSPATH=$base/framework/am.jar
    exec app_process $base/bin com.android.commands.am.Am "$@"
fi

所以am其实调用app_process。这就表示如果要传入虚拟机参数,不能使用am命令。所以在Android用命令行启动应用的方法是

CLASSPATH=/system/framework/am.jar app_process -EnableRWProfiling:true -EnableHeapSizeProfiling:true  /system/bin com.android.commands.am.Am start -S -n com.facebook.katana/.LoginActivity

[7]变量赋值和命令写在同一行的语法合法性在《Bash脚本解决一种问题的多种方法》有分析。

1 罗升阳. Android应用程序启动过程源代码分析. . 2011-08-19 [2019-05-20].
2 out/host/linux-x86/bin/art的源文件是art/tools/art。
3 JesusFreke. How to execute the dex file in android with command?. . 2019-01-26 [2019-05-20].
4 罗升阳. Android系统进程Zygote启动过程的源代码分析. . 2011-09-19 [2019-06-02].
5 Todd Kennedy. Implement shell commands for package and user services
. . 2015-10-30 [2019-05-20].
6 ccpat. Android应用的默认Activity配置. . 2017-01-24 [2019-05-20].
7 变量赋值和命令写在同一行的语法合法性在《Bash脚本解决一种问题的多种方法》有分析。

“Android用命令行启动应用”的一个回复

  1. 有可能通过命令启动应用的时候,禁用 jit 嘛
    就是达到清单文件加入 vmSafeMode=true 的效果
    就是三方 app 无法修改清单文件,通过命令启动来实现禁用这个 app 使用 jit

发表评论

电子邮件地址不会被公开。

:wink: :twisted: :roll: :oops: :mrgreen: :lol: :idea: :evil: :cry: :arrow: :?: :-| :-x :-o :-P :-D :-? :) :( :!: 8-O 8)