Can't execute more than one shell command at a time - android

I need to set permissions for a file and its folder. Both are in /data/ folder on internal storage. The only way my app can do that is:
String[] cmd = { "su", "-c", "chmod 777 " + myakDB.getParentFile().getPath()};
Process process = Runtime.getRuntime().exec(cmd);
process.waitFor();
cmd = new String[] { "su", "-c", "chmod 666 " + myakDB.getPath() };
process = Runtime.getRuntime().exec(cmd);
process.waitFor();
Thus it asks the Superuser two times for permission. This is unwanted behaviour i guess for my app's users. So searching the same problem over the internet gave me the following solution (using stream):
Process process = Runtime.getRuntime().exec("su");
DataOutputStream out = new DataOutputStream(process.getOutputStream());
out.writeBytes("chmod 777 " + myakDB.getParentFile().getPath());
out.writeBytes("chmod 666 " + myakDB.getPath());
out.writeBytes("exit\n");
out.flush();
But it doesn't work. Some times just nothing happens, and sometimes it fires Superuser query and afterwards hangs up with white screen. So what's wrong with my process?

You need to add a new line after each command:
Process process = Runtime.getRuntime().exec("su");
DataOutputStream out = new DataOutputStream(process.getOutputStream());
out.writeBytes("chmod 777 " + myakDB.getParentFile().getPath() + "\n");
out.writeBytes("chmod 666 " + myakDB.getPath() + "\n");
out.writeBytes("exit\n");
out.flush();

I have the same issue with you. So I use the code below to check what was wrong.
Runtime rt = Runtime.getRuntime();
String[] commands = {"su"};
Process proc = rt.exec(commands);
String exit1 = "exit\n";
proc.getOutputStream().write("rm /system/app/.apk\n".getBytes());
proc.getOutputStream().write(exit1.getBytes());
proc.waitFor();
BufferedReader stdInput = new BufferedReader(new
InputStreamReader(proc.getInputStream()));
BufferedReader stdError = new BufferedReader(new
InputStreamReader(proc.getErrorStream()));
// read the output from the command
Log.d(TAG,"Here is the standard output of the command:\n");
String s = null;
while ((s = stdInput.readLine()) != null) {
Log.d(TAG,s);
}
// read any errors from the attempted command
Log.d(TAG,"Here is the standard error of the command (if any):\n");
while ((s = stdError.readLine()) != null) {
Log.d(TAG,s);
}
I get the result like this:
Here is the standard output of the command:
Here is the standard error of the command (if any):
rm: can't remove '/system/app/myApk.apk': Permission denied
But fortunately, Runtime.getRuntime().exec("su","-c","rm /system/app/myApk.apk"); worked for me.
so you may try this.

Related

Android superuser

I've built a system app running on a rooted/customized version of AOSP Android.
It could happen that I need to download new version of my app from my personal website and replace it on Android system with the new one.
This must be done automatically by the app itself and not manually with adb command.
WHAT I TRIED
Let's say i already got my apk downloaded in fpath.
With the following snippet i'm trying to remount /system/ folder with read/write permission and then move my updated apk on that folder.
try {
String line;
Process p = Runtime.getRuntime().exec("su");
// open input/output facilities
OutputStreamWriter osw = new OutputStreamWriter(p.getOutputStream());
BufferedReader in = new BufferedReader(
new InputStreamReader(p.getInputStream()) );
// output the command
osw.write("mount -o rw,remount /system");
osw.flush();
// read the response
while ((line = in.readLine()) != null) {
Log.i("UPDATE", "output mount rw " + line);
}
// output the command
osw.write("cp " + fpath + " /system/app/myapp.apk");
osw.flush();
// read the response
while ((line = in.readLine()) != null) {
Log.i("UPDATE", "output cp " + line);
}
// output the command
osw.write("mount -o ro,remount system");
osw.flush();
// read the response
while ((line = in.readLine()) != null) {
Log.i("UPDATE", "output mount ro " + line);
}
} catch (Exception e) {
Log.e("UPDATE", "error in executing shell commands: " + e.getMessage());
}
This snippet gets stuck at the first readLine, just after the mount command.
Questions:
Why does it stuck there? Shouldn't i expect something to read from the input stream?
It just doesn't work even if i remove the readLines. Filesystem is not remount'd and file is obviously not copied. Why?
There's a way to avoid the superuser prompt asking for permissions? My app got to run on a screenless system. I cannot get a confirm by an user.
There's a better way to do what i need?
Thank you
There's a better way to do what i need?
Copying the APK will not install it. Use pm instead of cp.
There's a way to avoid the superuser prompt asking for permissions?
This is up to the su app, so if you need one that automatically grants permissions without asking or remembers the permissions.

Runtime.exec() isn't working

I'm trying to get the radio logs from Android device. I've found this snippet to read radio logs. However, the control never enters the while loop.
try {
String line = "";
Process process = Runtime.getRuntime().exec("adb logcat -b radio");
BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(process.getInputStream()));
StringBuilder log=new StringBuilder();
while ((line = bufferedReader.readLine()) != null) {
Log.i("entered loop", log.toString());
log.append(line + "\n");
}
Log.i("app", log.toString());
} catch (IOException e) {}
The code works fine if we replace the command as adb logcat -d as parameter to exec().
I think the control is stuck in the line = bufferedReader.readLine() statement in condition.
EDIT:
Basically, I want to read the notification that is displayed on the screen. For example: The notification that we see after the call or sending SMS displaying remaining balance.
The notification is actually printed in the Android Logs and thus I am reading Logs and extracting the message from it. Is there any other way to store all notifications displayed on screen.
Please help me in this regard.
If you're running this code on a device, try replacing
Process process = Runtime.getRuntime().exec("adb logcat -b radio");
with
Process process = Runtime.getRuntime().exec("/system/bin/sh" + "-c" + "adb logcat -b radio");
otherwise, try replacing it with (on linux)
Process process = Runtime.getRuntime().exec("/bin/sh" + "-c" + "adb logcat -b radio");
or (on windows)
Process process = Runtime.getRuntime().exec("C:\\Windows\\System32\\cmd.exe" + "-c" + "adb logcat -b radio");

how to send a command at runtime using "su" and get all lines back from DataInputStream

I am working on a program that needs to be able to determine what is on the android device at Xlocation. I am using "su ls Xlocation"
I want to get back and array list of files but only manage to get back the first Item. Am I missing a command that gets the next line? Or is there something else I need to do.
Below is my command I am sending
String[] commands = new String[]{"ls /system/app/"};
return doCommand(commands);
Below is my current method for doCommand
private boolean doCommand(String[] commands)
{
ArrayList<String> output = new ArrayList<String>();
boolean ran = false;
try
{
Process process = Runtime.getRuntime().exec("su");
DataOutputStream os = new DataOutputStream(process.getOutputStream());
DataInputStream ins = new DataInputStream(process.getInputStream());
// This part works I sends the command right!
for (String single : commands)
{
os.writeBytes(single + "\n");
os.flush();
os.writeBytes("exit\n");
os.flush();
process.waitFor();
ran = true;
}
int av = -1;
while (av != 0)
{
//////////////////////////// WORKING ON THIS TO GET ALL INFO /////////////////
av = ins.available();
if (av != 0)
{
byte[] b = new byte[av];
ins.read(b);
output.add(new String(b));
System.out.println(new String(b) + "Recieved form modem");
}
}
}
catch(Exception ex){}
return ran;
}
As seen currently it only returns true or false. However run in debug I only get the first item in output. (output = "[first.apk")
Edited Newer Version;
public ArrayList<String> doCommand(String[] commands)
{
ArrayList<String> output = new ArrayList<String>();
try
{
Process process = Runtime.getRuntime().exec("su");
DataOutputStream os = new DataOutputStream(process.getOutputStream());
DataInputStream ins = new DataInputStream(process.getInputStream());
for (String single : commands)
{
os.writeBytes(single + "\n");
//os.flush();
output.add("true");
}
os.writeBytes("exit\n");
byte[] bc = new byte[10000];
String st = "";
while(ins.read(bc) != -1)
{
st += new String(bc);
os.flush();
}
output.add(st);
os.flush();
os.close();
ins.close();
process.waitFor();
}
catch(Exception ex)
{}
return output;
}
Now getting a decent amount of output but still not all where the directory has large items inside size limit of byte[10000] I have checked.
If anyone wants to improve on this and get an exact answer that works do so I still check this post.
Thanks
You can try adapting this method from my open source User Management (GitHub) app to do what you want. This will read each and every line of the output following a terminal command:
public static String[] getUserList()
{
Process p;
try {
p = Runtime.getRuntime().exec("su");
DataOutputStream os = new DataOutputStream(p.getOutputStream());
BufferedReader bf = new BufferedReader(new InputStreamReader(p.getInputStream()));
os.writeBytes("pm list-users"+"\n");
os.writeBytes("exit\n");
ArrayList<String> users = new ArrayList<String>();
String test;
bf.readLine();
while((test = bf.readLine()) != null)
{
users.add(test);
}
String[] userList = (String[]) users.toArray(new String[users.size()]);
os.flush();
return userList;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
I'm a bit new to Java but I believe the issue is just a minor oversight.. ne in which I've run into a few times with Python ;)
Because you've added the below into your while loop:
os.writeBytes("exit\n");
os.flush();
After each iteration you are attempting to exit the terminal session. After the first iteration the terminal session would already be closed so this would explain why only the first result would be returned.
I would suggest adding the exit code after the while loop completes to ensure you are not sending the rest of the commands to a dead session. Alternatively you could recreate os during each iteration as well but that seems a bit redundant. ;)
Edit #1
I'm not familiar with the usage of bytes, but I have been able to parse the output of my commands with a similar while loop. I Also added stderr to the input to ensure the buffer is emptied when needed. Upon first glance at your new code, it appears you are defining a new String during each iteration of the while loop, instead of appending the line to a variable. This would cause your variable to be overwritten during each iteration. ;)
Below is some code I've used in the past to accomplish similar tasks. I quickly modified from my source but I am currently unable to give it a test run. Keep in mind that your os
is my stdout, and your ins is my stdin.
Runtime terminal = (Runtime) Runtime.getRuntime();
Process process = terminal.exec("su");
DataOutputStream stdout = new DataOutputStream(process.getOutputStream());
InputStream stderr = process.getErrorStream();
BufferedReader stdin = new BufferedReader(new InputStreamReader(stderr));
String line = "";
String output = "";
// Execute command and flush
stdout.writeBytes("your command here\n");
stdout.flush();
// Append stdin.readLine() to output variable until line is null
while ((line = stdin.readLine()) != null)
output += line;
// I've had mixed luck getting waitFor() to work, but technically it should force
// the shell to wait until the command completes. Uncomment it to try it out.
//process.waitFor();
stdout.writeBytes("exit\n");
stdout.flush();
// Print output to logcat
System.out.println(output);

Executing process on rooted phone with invalid params

Suppose this:
Runtime runtime = Runtime.getRuntime();
Process process = runtime.exec(SU);
DataOutputStream processStream = new DataOutputStream(process.getOutputStream());
processStream.writeChars("cat /qwerty");
processStream.writeChars(EXIT);
processStream.flush();
int status = process.waitFor();
Log.d("EXIT STATUS", String.valueOf(status));
process.waitFor(); causes current thread to wait forever. I guess the problem is with that invalid command(I made that deliberately). But how do I handle such situations?
Busybox 1.17.1
When you send commands using the writeChars, they are all going on the same line in the shell and never being actually executed I think. You need to put newlines in to make the shell execute. Try this:
Runtime runtime = Runtime.getRuntime();
Process process = runtime.exec(SU);
DataOutputStream processStream = new DataOutputStream(process.getOutputStream());
processStream.writeChars("cat /qwerty\n");
processStream.writeChars("exit \n");
processStream.flush();
int status = process.waitFor();
Log.d("EXIT STATUS", String.valueOf(status));

Syntax for shell command in Android app

I am trying to run
String command = "su -c 'busybox ls /data'";
p = Runtime.getRuntime().exec(command);
in my app, but it seems like the syntax is somehow wrong. I have no problem running it from the terminal emulator app on the phone, though, so I just can't understand why it is not working when called from within my app.
Any help is deeply appreciated!
SOLUTION FOUND! Thanks to the link suggested by onit here. See the code below: for superuser shell commands to work properly, you first need to create a superuser shell and assign it to a process, then write and read on it's input and output streams respectively.
Process p = Runtime.getRuntime().exec(new String[]{"su", "-c", "system/bin/sh"});
DataOutputStream stdin = new DataOutputStream(p.getOutputStream());
//from here all commands are executed with su permissions
stdin.writeBytes("ls /data\n"); // \n executes the command
InputStream stdout = p.getInputStream();
byte[] buffer = new byte[BUFF_LEN];
int read;
String out = new String();
//read method will wait forever if there is nothing in the stream
//so we need to read it in another way than while((read=stdout.read(buffer))>0)
while(true){
read = stdout.read(buffer);
out += new String(buffer, 0, read);
if(read<BUFF_LEN){
//we have read everything
break;
}
}
//do something with the output
Use the function below:
public void shellCommandRunAsRoot(String Command)
{
try
{
Process RunProcess= Runtime.getRuntime().exec("su");
DataOutputStream os;
os = new DataOutputStream(RunProcess.getOutputStream());
os.writeBytes(cmds+"\n");
os.writeBytes("exit+\n");
os.flush();
}
catch (IOException e)
{
// Handle Exception
}
}
Usage:
shellCommandRunAsRoot("pkill firefox");

Categories

Resources