2011-02-05 18 views
26

Estoy desarrollando mi primera aplicación Android, y tengo curiosidad si hay formas "estándar" para ejecutar los comandos privilegiados shell. Solo he podido encontrar una forma de hacerlo, ejecutando su, y luego agregando mis comandos al stdin del proceso su.ANDROID: ¿Cómo obtener acceso a la raíz en una aplicación de Android?

DataOutputStream pOut = new DataOutputStream(p.getOutputStream()); 
DataInputStream pIn = new DataInputStream(p.getInputStream()); 

String rv = ""; 

// su must exit before its output can be read 
pOut.writeBytes(cmd + "\nexit\n"); 
pOut.flush(); 

p.waitFor(); 

while (pIn.available() > 0) 
    rv += pIn.readLine() + "\n"; 

He leído acerca de envolver privilegiada (superuser) llama en JNI: ¿es esto posible? Si es así, ¿cómo lograrlo? Aparte de eso, ¿hay alguna otra forma de llamar instrucciones privilegiadas desde Java?

+2

No hay ningún medio para elevar el privilegio de un proceso existente, solo significa iniciar un nuevo proceso ejecutándose como root. Para ver un ejemplo donde su se pasa un comando para ejecutar, consulte http://stackoverflow.com/questions/4846241/more-than-one-superuser-command-android/4846312 –

+0

@Chris ¿qué pasa con el lanzamiento de una nueva actividad/tarea/¿Android app/etc de Java? –

+3

Eso requeriría grandes cambios en Android. Normalmente no se crean procesos para la ejecución de Android java, sino que se pide a Android que pida a 'zygote' que separe uno, lo que hace con la identificación de usuario de esa aplicación. Es más simple mantener las tareas de raíz como ejecutables nativos o scripts de shell haciendo cosas clave en nombre de la aplicación. O agréguelos al sistema propiamente dicho como servicios de sistema privilegiados. A menudo, la mejor manera es crear un grupo Unix y darle acceso a una capacidad particular (por ejemplo, acceder a un archivo de dispositivo) y ejecutar un servicio en ese grupo o vincularlo con un permiso de Android –

Respuesta

28

Hasta donde yo sé, solo puede ejecutar comandos de línea de comando usando privilegios de administrador. Se puede utilizar esta clase genérica que hice que envuelve el acceso de root en el código: http://muzikant-android.blogspot.com/2011/02/how-to-get-root-access-and-execute.html

Todo lo que necesita hacer es extender esta clase y reemplazar el método getCommandsToExecute para devolver los comandos que desea ejecutar como root.

public abstract class ExecuteAsRootBase 
{ 
    public static boolean canRunRootCommands() 
    { 
     boolean retval = false; 
     Process suProcess; 

     try 
     { 
     suProcess = Runtime.getRuntime().exec("su"); 

     DataOutputStream os = new DataOutputStream(suProcess.getOutputStream()); 
     DataInputStream osRes = new DataInputStream(suProcess.getInputStream()); 

     if (null != os && null != osRes) 
     { 
      // Getting the id of the current user to check if this is root 
      os.writeBytes("id\n"); 
      os.flush(); 

      String currUid = osRes.readLine(); 
      boolean exitSu = false; 
      if (null == currUid) 
      { 
       retval = false; 
       exitSu = false; 
       Log.d("ROOT", "Can't get root access or denied by user"); 
      } 
      else if (true == currUid.contains("uid=0")) 
      { 
       retval = true; 
       exitSu = true; 
       Log.d("ROOT", "Root access granted"); 
      } 
      else 
      { 
       retval = false; 
       exitSu = true; 
       Log.d("ROOT", "Root access rejected: " + currUid); 
      } 

      if (exitSu) 
      { 
       os.writeBytes("exit\n"); 
       os.flush(); 
      } 
     } 
     } 
     catch (Exception e) 
     { 
     // Can't get root ! 
     // Probably broken pipe exception on trying to write to output stream (os) after su failed, meaning that the device is not rooted 

     retval = false; 
     Log.d("ROOT", "Root access rejected [" + e.getClass().getName() + "] : " + e.getMessage()); 
     } 

     return retval; 
    } 

    public final boolean execute() 
    { 
     boolean retval = false; 

     try 
     { 
     ArrayList<String> commands = getCommandsToExecute(); 
     if (null != commands && commands.size() > 0) 
     { 
      Process suProcess = Runtime.getRuntime().exec("su"); 

      DataOutputStream os = new DataOutputStream(suProcess.getOutputStream()); 

      // Execute commands that require root access 
      for (String currCommand : commands) 
      { 
       os.writeBytes(currCommand + "\n"); 
       os.flush(); 
      } 

      os.writeBytes("exit\n"); 
      os.flush(); 

      try 
      { 
       int suProcessRetval = suProcess.waitFor(); 
       if (255 != suProcessRetval) 
       { 
        // Root access granted 
        retval = true; 
       } 
       else 
       { 
        // Root access denied 
        retval = false; 
       } 
      } 
      catch (Exception ex) 
      { 
       Log.e("ROOT", "Error executing root action", ex); 
      } 
     } 
     } 
     catch (IOException ex) 
     { 
     Log.w("ROOT", "Can't get root access", ex); 
     } 
     catch (SecurityException ex) 
     { 
     Log.w("ROOT", "Can't get root access", ex); 
     } 
     catch (Exception ex) 
     { 
     Log.w("ROOT", "Error executing internal operation", ex); 
     } 

     return retval; 
    } 
    protected abstract ArrayList<String> getCommandsToExecute(); 
} 
+0

. Parece que hay un error css en su bloque de código siempre el último personaje está en la primera posición ?! – dtrunk

+0

@dtrunk Gracias por la actualización. Solucioné el error en mi blog. – Muzikant

+0

Gracias por compartir tu clase. Me pareció más útil pasar los comandos como un parámetro, p. boolean execute (comandos ArrayList ) {...} – pmont

4

Una posible solución que conozco es firmar su aplicación como sistema, que no es exactamente lo mismo que root hasta donde yo sé: How to sign Android app with system signature?. Pero supongo que esto no es lo que querías.

Otra cosa que hice fue crear una aplicación nativa que hiciera lo necesario, ejecutándola como un proceso externo. Pero es necesario darle a esta aplicación nativa los privilegios que necesita y el bit de suid, siempre que la partición no sea nosuid. Pero esto no es lo que necesitabas, supongo.

El código C llamado a través de JNI debería estar sujeto a las mismas limitaciones que vivir en el mismo proceso, supongo.

Si tiene el binario disponible, puede ejecutar comandos desde java con algo como: Runtime.getRuntime(). Exec ("su -c reboot").

No recuerdo de ninguna otra manera.

Cuestiones relacionadas