2014-12-18 5 views
0

Я хочу, чтобы мое приложение могло использовать глобальный экземпляр su. У меня есть код, который делает это, но я уверен, что столкнулся с состоянием гонки.Java runtime.exec пользователь вводит состояние гонки

Я храню некоторые переменные для 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 что возвращенный закрытый ключ недействителен с моей проверкой работоспособности (это, вероятно, null).

Код работает, если я даю Process завершение инициализации, но я хотел бы, чтобы программа сделала это для меня.

Я пробовал некоторые другие методы синхронизации, такие как блокировки, но, по-видимому, как только вы знаете, если объект заблокирован, ваша информация устарела.

Что является лучшим потокобезопасным подходом к вызову getPrivateKeyAsSuperUser() ожидать, если Process не инициализирован правильно?

EDIT:

Я хотел бы добавить, что через какой-то отладки, я обнаружил, что я не хочу ждать процесса инициализации (потому что я ДЕЙСТВИТЕЛЬНО это), но, скорее, о том, что shell, порожденный su, действителен, чтобы принимать дополнительные команды. Я полагаю, что у меня могла бы быть нитьная труба вроде echo DONE и петля до тех пор, пока я не получу DONE назад, но похоже, что это потеряло бы мощность процессора. Если бы кто-то мог предоставить некоторые знания по этому предмету, я был бы чрезвычайно благодарен.

ответ

0

Я решил. Мне все равно пришлось эхо и проверить, что сделано, но я сделал это без цикла или спящего в моем потоке, поэтому он будет срабатывать, как только это будет возможно, не запуская процессор. Совместный класс, который я искал, был 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; 

} 

Хотя, это чувствует себя немного коряво, я могу закончить размещение этого на просмотр кода.

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() 

и он будет там, как песня Майкла Джексона.

+0

Вы абсолютно правы, так что вы заслуживаете голосования. Я должен использовать синглтон.Я делаю это в другом месте в своем приложении, и я не уверен, почему я этого не сделал. Тем не менее, мне все еще нужна помощь, ожидая, когда моя оболочка вернется в 'su', чтобы быть« интерактивной », так сказать (фактический вопрос). У вас есть предложение по этому поводу? – MeetTitan

Смежные вопросы