2014-12-18 4 views
0

내 앱에서 글로벌 su 인스턴스를 사용할 수있게하려고합니다. 나는 그것을하는 코드를 가지고 있지만, 나는 경쟁 조건에 부딪쳤다 고 나는 믿는다.Java 런타임 .exec 사용자 입력 경쟁 조건

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

을 등처럼 그들을 사용 :

그래서 같은 su에 대한 몇 가지 변수를 저장하고

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 초기화가 완료되면 코드가 작동하지만 프로그램에서 저를 대신 수행하고 싶습니다.

나는 잠금과 같은 다른 동기화 기술을 시도해 보았지만, 오브젝트에 잠금 기능이 있는지 알게되면 정보가 오래되었습니다.

Process이 제대로 초기화되지 않은 경우 호출자에게 getPrivateKeyAsSuperUser() 대기를 허용하는 가장 안전한 스레드 접근 방식은 무엇입니까?

편집 :

내가 디버깅을 통해이를 추가 할

, 나는 발견했다 나는 (내가 무엇을해야하는 것이 없기 때문에) 프로세스가 초기화 될 때까지 기다리는 것이 아니라는 것을 할 싶지 않아 su에 의해 생성 된 셸은 추가 명령을 받아 들일 수 있습니다. 나는 내가 DONE을 얻을 때까지 echo DONE과 같은 스레드 파이프를 가질 수 있다고 생각하지만, CPU 마력을 낭비하는 것처럼 보입니다. 누군가 주제에 대해 지식을 빌려줄 수 있다면 매우 감사하게 생각합니다.

답변

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; 

} 

있지만, 나는 코드 검토에이를 게시 끝낼 수 있습니다.

1

여기에 싱글 톤 패턴을 시도하고 있습니다. 왜 당신이 목록에 이러한 객체를 저장하고 싶은지 잘 모르겠습니다. 그것들을 저장하는 가장 현명한 방법은 하나의 인스턴스를 생성하는 것을 보장하는 객체에 있습니다. 당신이 이것을 할 수있는 몇 가지 방법이 있습니다. 나는 당신이 당신의 SU 프로세스를 필요로 어디든지 다음 그냥

SuProcessHolder.getInstance() 

를 호출하고 마이클 잭슨의 노래처럼이있을 것, 그리고

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; } 
} 

를 작동합니다 귀하의 경우 생각합니다.

+0

당신은 절대적으로 정확합니다. 그래서 당신은 상향 투표권을 가질 자격이 있습니다. 나는 싱글 톤을 사용해야한다.내 애플 리케이션에서 다른 곳에서, 그리고 내가 왜 여기에 없었는지 모르겠다. 그러나, 나는'su '가 말하는 "대화 형"이되도록 내 쉘을 기다리는 데 도움이 필요하다 (실제 질문). 그것에 대한 제안이 있습니까? – MeetTitan