我该如何运行" export" Android上的java进程内的命令?

时间:2014-10-22 06:41:49

标签: java android shell processbuilder

我尝试使用Java编写的应用程序在Android上运行shell脚本。我希望能够以root身份运行Android命令,例如ampm,之前需要export LD_LIBRARY_PATH=/system/lib命令。不幸的是,export命令不起作用。这是我写的Java代码:

private String readScript(String scriptPath) {

    String line = null;
    StringBuffer content = new StringBuffer();

    try {

        // Read the script line by line
        BufferedReader reader = new BufferedReader(new FileReader(scriptPath));
        while ((line = reader.readLine()) != null) {
            content.append(line).append('\n');
        }
        content.append("exit").append('\n');
        reader.close();

    } catch (IOException e) {

        Log.d(UpdateService.LOG_NAME, "Impossible to read script : " + e.getMessage());

    }

    return content.toString();

}

public StringBuffer runScript(String scriptPath) {

    String line = null;
    StringBuffer output = new StringBuffer();

    try {

        // Start the shell process
        ProcessBuilder pb = new ProcessBuilder("su");
        pb.redirectErrorStream(true);
        pb.directory(new File(context.getApplicationInfo().dataDir));
        pb.environment().put("LD_LIBRARY_PATH", "/system/lib");
        Process process = pb.start();

        // Execute the script
        OutputStreamWriter os = new OutputStreamWriter(process.getOutputStream());
        os.write(readScript(scriptPath));
        os.flush();
        os.close();

        // Get the output of the commands
        BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
        while ((line = in.readLine()) != null) {
            output.append(line).append('\n');
        }
        in.close();

    } catch (Exception e) {

        Log.d(UpdateService.LOG_NAME, "Error while executing " + scriptPath + " : " + e.getMessage());

    }

    return output;

}

我简单地称之为:

Shell = new Shell();
StringBuffer output = shell.runScript("script.sh");

我尝试运行的脚本:

export LD_LIBRARY_PATH=/system/lib
printenv
am start -W -n com.android.settings/.Settings\$InputMethodAndLanguageSettingsActivity

给我输出:

_=/system/bin/printenv
ANDROID_BOOTLOGO=1
ANDROID_PROPERTY_WORKSPACE=9,32768
LOOP_MOUNTPOINT=/mnt/obb
EXTERNAL_STORAGE=/mnt/sdcard
ANDROID_DATA=/data
RANDOM=21985
ANDROID_SOCKET_zygote=10
ASEC_MOUNTPOINT=/mnt/asec
BOOTCLASSPATH=/system/framework/core.jar:/system/framework/core-junit.jar:/system/framework/bouncycastle.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/android.policy.jar:/system/framework/services.jar:/system/framework/apache-xml.jar:/system/framework/filterfw.jar
ANDROID_ROOT=/system
ANDROID_ASSETS=/system/app
PATH=/sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin
Segmentation fault

正如您所看到的,使用environment() ProcessBuilder方法设置的环境变量似乎未设置,脚本中的export也无法正常工作。< / p>

对我做错了什么的任何想法? 谢谢!

修改

根据建议,我还尝试了exec()的版本:

public StringBuffer runScript(String scriptPath) {

    String line = null;
    StringBuffer output = new StringBuffer();

    try {

        // Start the shell process
        Process process = Runtime.getRuntime().exec("su");

        // Execute the script commands
        OutputStreamWriter os = new OutputStreamWriter(process.getOutputStream());
        os.write(readScript(scriptPath));
        os.flush();
        os.close();

        // Get the output of the command
        BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
        while ((line = in.readLine()) != null) {
            output.append(line).append('\n');
        }
        in.close();

        process.waitFor();

    } catch (Exception ex) {

        Log.d(UpdateService.LOG_NAME, "Error while executing " + scriptPath + ".");

    }

    return output;

}

这次我没有stderr(分段错误)得到相同的输出:

_=/system/bin/printenv
ANDROID_BOOTLOGO=1
ANDROID_PROPERTY_WORKSPACE=9,32768
LOOP_MOUNTPOINT=/mnt/obb
EXTERNAL_STORAGE=/mnt/sdcard
ANDROID_DATA=/data
RANDOM=21985
ANDROID_SOCKET_zygote=10
ASEC_MOUNTPOINT=/mnt/asec
BOOTCLASSPATH=/system/framework/core.jar:/system/framework/core-junit.jar:/system/framework/bouncycastle.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/android.policy.jar:/system/framework/services.jar:/system/framework/apache-xml.jar:/system/framework/filterfw.jar
ANDROID_ROOT=/system
ANDROID_ASSETS=/system/app
PATH=/sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin

因此导出LD_LIBRARY_PATH=/system/lib仍然无效。在我看来,ProcessBuilder是一个更好的方式来启动流程,因为它致力于它。是不是?

EDIT2

ampm实际上是启动java可执行文件的包装器。我试图实现这个包装器的C版本来创建一个适用于任何环境的完整环境。这是am的内容:

# Script to start "am" on the device, which has a very rudimentary
# shell.
#
base=/system
export CLASSPATH=$base/framework/am.jar
exec app_process $base/bin com.android.commands.am.Am "$@"

这是我的C版:

#include <stdio.h>
#include <string.h>
#include <errno.h>

int main(int argc, const char *argv[], const char *envp[]) {
    const char **p;

    const int NUM_ARG = 3;
    const char *APP_PROCESS = "/system/bin/app_process";
    const char *BIN = "/system/bin";
    const char *AM = "com.android.commands.am.Am";

    const int NUM_ENV = 11;
    const char *ENV[] = {
        "PATH=/usr/bin:/usr/sbin:/bin:/sbin:/system/sbin:/system/bin:/system/xbin:/system/xbin/bb:/data/local/bin",
        "SHELL=/system/bin/sh",
        "MKSH=/system/bin/sh",
        "HOME=/data",
        "HOSTNAME=android",
        "USER=root",
        "LOGNAME=root",
        "TERM=xterm",
        "LD_LIBRARY_PATH=/system/lib/",
        "CLASSPATH=/system/framework/am.jar",
        NULL
    };

    int i = 0;
    char *a[argc + NUM_ARG];
    a[i] = (char *)malloc(strlen(APP_PROCESS) + 1);
    strcpy(a[i++], APP_PROCESS);
    a[i] = (char *)malloc(strlen(BIN) + 1);
    strcpy(a[i++], BIN);
    a[i] = (char *)malloc(strlen(AM) + 1);
    strcpy(a[i++], AM);
    p = argv + 1;
    for (; i < argc + NUM_ARG - 1; i++) {
        a[i] = (char *)malloc(strlen(*p) + 1);
        strcpy(a[i], *p++);
    }
    a[i] = (char *)malloc(1);
    a[i] = NULL;

    p = envp;
    int envc = 0;
    while (*p++ != NULL) envc++;

    char *v[envc + NUM_ENV];
    for (i = 0; i < envc; i++) {
        v[i] = (char *)malloc(strlen(envp[i]) + 1);
        strcpy(v[i], envp[i]);
    }
    p = ENV;
    while (*p != NULL) {
        v[i] = (char *)malloc(strlen(*p) + 1);
        strcpy(v[i++], *p++);
    }
    v[i] = (char *)malloc(1);
    v[i] = NULL;

    execve(APP_PROCESS, a, v);
    fprintf(stderr, "am: %s  error: %s\n", APP_PROCESS, strerror(errno));
    for (i = 0; i < argc + NUM_ARG; i++) {
        free(a[i]);
    }
    for (i = 0; i < envc + NUM_ENV; i++) {
        free(v[i]);
    }

    return 1;
}

它与ADB或SSH完美配合,但即使使用此实现,我也会得到一个&#34;分段错误&#34;执行execve()后的ProcessBuilder行。为什么???

EDIT3

好的,我查看了脚本init.rc的内容,其中列出了环境变量。正确设置了LD_LIBRARY_PATH变量。

on init
    sysclktz 0
    loglevel 7

    mkdir /system
    mkdir /data 0771 system system
    mkdir /cache 0770 system cache
    mkdir /config 0500 root root

    # Setup the global environment
    export PATH /sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin
    export LD_LIBRARY_PATH /vendor/lib:/system/lib
    export ANDROID_BOOTLOGO 1
    export ANDROID_ROOT /system
    export ANDROID_ASSETS /system/app
    export ANDROID_DATA /data
    export EXTERNAL_STORAGE /mnt/sdcard
    export ASEC_MOUNTPOINT /mnt/asec
    export LOOP_MOUNTPOINT /mnt/obb
    export BOOTCLASSPATH /system/framework/core.jar:/system/framework/core-junit.jar:/system/framework/bouncycastle.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/android.policy.jar:/system/framework/services.jar:/system/framework/apache-xml.jar:/system/framework/filterfw.jar

但是......出于某种原因,根据具体情况,它们会被更改或删除。例如,如果我通过ADB连接:

$ env
_=/system/xbin/env
ANDROID_BOOTLOGO=1
ANDROID_PROPERTY_WORKSPACE=9,32768
LOOP_MOUNTPOINT=/mnt/obb
PS1=$(precmd)$USER@$HOSTNAME:${PWD:-?} $ 
USER=shell
EXTERNAL_STORAGE=/mnt/sdcard
ANDROID_DATA=/data
RANDOM=22124
TERM=vt100
MKSH=/system/bin/sh
HOME=/data
LD_LIBRARY_PATH=/vendor/lib:/system/lib
ASEC_MOUNTPOINT=/mnt/asec
HOSTNAME=android
BOOTCLASSPATH=/system/framework/core.jar:/system/framework/core-junit.jar:/system/framework/bouncycastle.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/android.policy.jar:/system/framework/services.jar:/system/framework/apache-xml.jar:/system/framework/filterfw.jar
ANDROID_ROOT=/system
SHELL=/system/bin/sh
ANDROID_ASSETS=/system/app
PATH=/sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin

如果我通过SSH连接:

$ env
_=/system/xbin/env
ANDROID_PROPERTY_WORKSPACE=9,32768
ANDROID_BOOTLOGO=1
PS1=$(precmd)$USER@$HOSTNAME:${PWD:-?} $ 
USER=shell
EXTERNAL_STORAGE=/mnt/sdcard
LOGNAME=shell
ANDROID_DATA=/data
RANDOM=4546
TERM=xterm
SHELL=/system/bin/sh
MKSH=/system/bin/sh
HOME=/data/data/com.teslacoilsw.quicksshd/home
HOSTNAME=android
BOOTCLASSPATH=/system/framework/core.jar:/system/framework/core-junit.jar:/system/framework/bouncycastle.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/android.policy.jar:/system/framework/services.jar:/system/framework/apache-xml.jar:/system/framework/filterfw.jar
ANDROID_ROOT=/system
ANDROID_ASSETS=/system/app
PATH=/data/data/com.teslacoilsw.quicksshd/dropbear:/usr/bin:/usr/sbin:/bin:/sbin:/system/sbin:/system/bin:/system/xbin:/system/xbin/bb:/data/local/bin

如果我通过Java运行命令:

$ env
_=/system/bin/printenv
ANDROID_BOOTLOGO=1
ANDROID_PROPERTY_WORKSPACE=9,32768
LOOP_MOUNTPOINT=/mnt/obb
EXTERNAL_STORAGE=/mnt/sdcard
ANDROID_DATA=/data
RANDOM=7424
ANDROID_SOCKET_zygote=10
ASEC_MOUNTPOINT=/mnt/asec
BOOTCLASSPATH=/system/framework/core.jar:/system/framework/core-junit.jar:/system/framework/bouncycastle.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/android.policy.jar:/system/framework/services.jar:/system/framework/apache-xml.jar:/system/framework/filterfw.jar
ANDROID_ROOT=/system
ANDROID_ASSETS=/system/app
PATH=/sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin

通过ADB,命令am非常有效。通过SSH,如果我运行export LD_LIBRARY_PATH=/system/lib/,它可以工作。在Java中它永远不会起作用:/

0 个答案:

没有答案