Java runtime.exec用户输入竞争条件

时间:2014-12-18 20:06:30

标签: java race-condition runtime.exec su

我希望我的应用能够使用全局su实例。我有代码可以做到这一点,但我相信我遇到了竞争条件。

我正在为su存储一些变量,如下所示:

public static List<Object> rootObjects = Collections.synchronizedList(new ArrayList<>());

protected void onCreate(Bundle savedInstanceState) {
    ...
    if(PreferenceManager.getDefaultSharedPreferences(
            getApplicationContext()).getBoolean("use_su", false) && rootObjects.isEmpty())
    {
        try {
            Process process = Runtime.getRuntime().exec("su");
            rootObjects.add(process);
            InputStream inputStream = new DataInputStream(process.getInputStream());
            rootObjects.add(inputStream);
            OutputStream outputStream = new DataOutputStream(process.getOutputStream());
            rootObjects.add(outputStream);
        } catch (IOException e) {
            Log.d(MainActivity.mainActivity.getPackageName(), e.getLocalizedMessage());
        }
        finally {
            synchronized (rootObjects) {
                rootObjects.notifyAll();
            }
        }
    }
}

并像这样使用它们:

byte[] getPrivateKeyAsSuperUser() {
    byte[] data = null;
    DataInputStream inputStream = null;
    DataOutputStream outputStream = null;

    if(MainActivity.rootObjects.size() != 3)
        synchronized (MainActivity.rootObjects)
        {
            try {
                MainActivity.rootObjects.wait();
            } catch (InterruptedException e) {
                Log.d(MainActivity.mainActivity.getPackageName(), e.getLocalizedMessage());
            }
        }

    for(Object rootObj : MainActivity.rootObjects)
    {
        if(rootObj instanceof DataInputStream)
            inputStream = (DataInputStream) rootObj;
        else if(rootObj instanceof DataOutputStream)
            outputStream = (DataOutputStream) rootObj;
    }
    try {
        outputStream.writeBytes(String.format("cat \"%s\"\n", sshPrivateKey.getAbsolutePath()));
        outputStream.flush();
        data = readStream(inputStream);
    } catch (IOException e) {
        Log.d(MainActivity.mainActivity.getPackageName(), e.getLocalizedMessage());
    }
    return data;
}

private byte[] readStream(InputStream stream) {
    byte[] data = null;
    try {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        byte buff[] = new byte[1024];
        int count = 0;

        while (stream.available() != 0 && (count = stream.read(buff)) != -1) {
            bos.write(buff, 0, count);
        }
        data = bos.toByteArray();
        //System.out.println(new String(data));
    } catch (IOException e) {
        Log.d(MainActivity.mainActivity.getPackageName(), e.getLocalizedMessage());
    }
    return data;
}

但它并没有像我期望的那样等待,我立即收到Toast返回的私钥对我的理智检查无效(它可能为空)。

如果我让Process完成初始化,代码就可以运行,但我希望程序可以帮我完成。

我已尝试过其他一些同步技术,例如锁定,但显然只要您知道对象是否有锁定,您的信息就会过时。

如果getPrivateKeyAsSuperUser()未正确初始化,那么让Process调用者等待的最佳线程安全方法是什么?

修改

我想补充一点,通过一些调试,我发现我不想等待Process初始化(因为我已经做了什么),而是由su生成的shell是有效接受进一步的命令。我想我可以有一个类似echo DONE的线程管道并循环直到我得到DONE,但这似乎会浪费CPU马力。如果有人可以提供一些关于这个问题的知识,我将非常感激。

2 个答案:

答案 0 :(得分:1)

您在这里尝试单身人士模式。我不确定为什么要将这些对象存储在列表中。存储它们的最明智的方法是在一个对象中保证创建一个实例。有几种方法可以做到这一点。我认为在你的情况下,以下工作

public class SuProcessHolder {
    // store the state of the process here - this would be your Process and streams as above
    // these should be non-static members of the class

    // this would be the singleton instance you'll use - it will be constructed once 
    // on first use
    private static SuProcessHolder singletonInstance = new SuProcessHolder();

    public SuProcessHolder() {
        // put your construction code in here to create an SU process
    }


    // this will access your SU process
    public static SuProcessHolder getInstance() { return singletonInstance; }
}

然后,无论您何时需要SU流程,只需致电

即可
SuProcessHolder.getInstance()

它就像迈克尔杰克逊的歌一样。

答案 1 :(得分:0)

我已经解决了。我最终不得不回显并检查完成,但是我已经完成了它没有循环,或者在我的线程中睡觉,所以它会尽快触发,而不会占用CPU。我正在寻找的并发课程也是CountDownLatch

作业如下:

process = Runtime.getRuntime().exec("su");
outputStream = new DataOutputStream(process.getOutputStream());
outputStream.writeBytes("echo DONE\n");
outputStream.flush();
inputStream = new DataInputStream(process.getInputStream());
byte[] buff = new byte[4];
inputStream.read(buff);
if(new String(buff).equals("DONE"));
     MainActivity.rootLatch.countDown();

getPrivateKeyAsSuperUser()成了:

byte[] getPrivateKeyAsSuperUser() {

    byte[] data = null;
    try {
        MainActivity.rootLatch.await();
    } catch (InterruptedException e) {
        Log.d(MainActivity.mainActivity.getPackageName(), e.getLocalizedMessage());
    }
    Su su = Su.getStaticInstance();
        try {
            su.outputStream.writeBytes(String.format("cat \"%s\"\n", sshPrivateKey.getAbsolutePath()));
            su.outputStream.flush();
            data = readStream(su.inputStream);
        } catch (IOException e) {
            Log.d(MainActivity.mainActivity.getPackageName(), e.getLocalizedMessage());
        }
    return data;

}

虽然这感觉有点草率,但我最终可能会在Code Review上发布这个内容。