封装 Shell 脚本

调试和分析带有原生代码的应用时,能够在 APK 中绑定命令行开发者工具并在应用启动时自动在设备上运行这些工具往往很有用。

从 Android 8.0 开始,您可选择创建并封装启动可调试应用时系统自动在全新进程中运行的用户定义的 shell 脚本。 您可利用此功能编写脚本,以调用原生开发者工具来执行以下此类操作:

使用封装 shell 脚本

若要使用封装 shell 脚本在设备上运行原生开发者工具,请遵循以下步骤:

  1. 编译包含以下项目的自定义可调试 APK:
    • 您想要使用的原生开发者工具,以及
    • 包含运行原生开发者工具指令的 shell 脚本来(名为 wrap.sh)。 请参阅创建封装 shell 脚本封装 wrap.sh,了解更多详细信息。
  2. 在设备上安装可调试 APK。 根据设备上的 Android 平台版本的不同,您可能需要修改进程上的访问控制机制,以便封装 shell 脚本顺利运行。
    • Android 8.1(API 级别 27)或更高版本:无需进行此类修改。
    • Android 8.0(API 级别 26):设备必须停用 SELinux 强制功能。 您可通过 userdebug 或 eng 构建上的 adb 停用 SELinux 强制功能。 具有此类权限级别的受支持设备包含:
      • 带有 userdebug 系统映像的 Android 模拟器,或者
      • 刷入 AOSP eng 构建或根映像的 Google 设备。
  3. 通过应用启动器或从命令行,正常启动设备上的应用。
  4. 强烈建议启动应用后,在设备上重新启用 SELinux 强制功能。

创建封装 shell 脚本

您可创建名为 wrap.sh 的 shell 脚本来自定义运行时环境。 您可在您的脚本中,设置环境变量并修改运行时参数。 脚本应遵循 MirBSD Korn shell (mksh) 语法。

当您启用包含 wrap.sh 的可调试 APK 时,系统将执行脚本,并授权其对应用进程的控制权。 系统通过传递命令来启动请求的应用,并将其作为 wrap.sh 调用的参数。 请注意,您的脚本将负责启动应用。

以下代码段显示如何编写仅启动应用的简单 wrap.sh 文件:

#!/system/bin/sh
"$@"

若要使用 wrap.sh 启动 malloc 调试工具,您应添加以下行:

#!/system/bin/sh
LIBC_DEBUG_MALLOC_OPTIONS=backtrace logwrapper $@

封装 wrap.sh

若要利用 wrap.sh,您必须构建可调试 APK。 确保您 Android 清单中的 <application> 元素已配置 android:debuggable=”true” 设置。

您必须使用应用的原生库来封装 wrap.sh 脚本。 如果您的应用不包含原生库,请手动添加 lib 目录到项目目录。 对于您应用支持的所有架构,您必须在封装 shell 脚本对应的原生库架构目录下提供其副本。

以下项目目录示例显示用于支持 x86 和 ARMv8 架构的文件布局:

# App Directory
|- AndroidManifest.xml
|- …
|- lib
   |- x86
      |- ...
      |- wrap.sh
   |- arm64-v8a
      |- ...
      |- wrap.sh

确保您的 wrap.sh 脚本是可执行文件(即其可执行位已设置)。 例如:

$ ls -l lib/x86/wrap.sh
-rwxr-x--x 1 user user 0 Jan 01  1980 lib/x86/wrap.sh