Je suis en train de développer ma première application Android
et je suis curieux de savoir s'il existe des moyens "standard" d'exécuter des commandes privilégiées Shell
. Je n'ai trouvé qu'une seule façon de le faire, en exécutant su
, puis en ajoutant mes commandes à stdin
du processus su
.
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";
J'ai lu comment encapsuler des appels privilégiés (superuser
) dans JNI
: est-ce possible? Si oui, comment procéderait-on? En dehors de cela, existe-t-il d'autres façons d'appeler des instructions privilégiées à partir de Java
?
Pour autant que je sache, vous ne pouvez exécuter des commandes de ligne de commande qu'en utilisant les privilèges root. Vous pouvez utiliser cette classe générique que j'ai faite qui encapsule l'accès root dans votre code: http://muzikant-Android.blogspot.com/2011/02/how-to-get-root-access-and-execute .html
Tout ce que vous devez faire est d'étendre cette classe et de remplacer la méthode getCommandsToExecute
pour renvoyer les commandes que vous souhaitez exécuter en tant que 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();
}
Une solution possible que je connais est de signer votre application en tant que système, ce qui n'est pas exactement la même chose que root pour autant que je sache: Comment signer Android avec signature système? . Mais je suppose que ce n'est pas ce que vous vouliez.
Une autre chose que j'ai faite est de créer une application native qui fait ce qui est nécessaire, en l'exécutant comme un processus externe. Mais il est nécessaire de donner à cette application native les privilèges dont vous avez besoin et le bit suid, à condition que la partition ne soit pas nosuid. Mais ce n'est pas ce dont vous aviez besoin non plus, je suppose.
Le code C appelé via JNI devrait être soumis aux mêmes limitations que le fait de vivre dans le même processus, je suppose.
Si vous avez le binaire su disponible, vous pouvez exécuter des commandes depuis Java avec quelque chose comme: Runtime.getRuntime().exec("su -c reboot")
.
Je ne me souviens d'aucune autre façon.