我希望我的应用能够使用全局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马力。如果有人可以提供一些关于这个问题的知识,我将非常感激。
答案 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上发布这个内容。