I'm running a native background process from an android app with Runtime.exec().
It seems like every few seconds the process is put to sleep for like 200 ms.
It looks like the android operating system is just starving that process.
Is there some way I can change the process priority from a background process to some other type of priority?
Assume the application that's running the process can get super user access.
Edit:
The command I'm using to run the process:
String fullCommand = "export LD_LIBRARY_PATH=LD_LIBRARY_PATH:. && ./MyProc";
Runtime.getRuntime().exec(new String[]{"su", "-c ", fullCommand });
If you know the PID of the process, you can change its priority with renice
(taken from this answer).
You can call renice like this:
renice priority [[-p] pid ...] [[-g] pgrp ...] [[-u] user ...]
Trying using the .setPriority() method.
Thread t=new Thread();
t.setPriority(Thread.MAX_PRIORITY);
Related
#Override
public void run() {
try {
exec = Runtime.getRuntime().exec("getevent | grep event1");
InputStreamReader is = new InputStreamReader(
exec.getInputStream());
String s;
BufferedReader br = new BufferedReader(is);
Log.i("br.readLine", " = " + br.readLine());
while ((s = br.readLine()) != null) {
Log.i("s2", " " + s);
}
// is.close();
// exec.destroy();
} catch (IOException e) {
Log.e("thread ioexception", " " + e.getCause());
e.printStackTrace();
}
}
I tried logging the results of the command to getevent in thread.
But put the results of the command in the buffer, the buffer is empty(null).
Perhaps as soon as this command is executed, it seems that ends without receiving any input.
While maintaining thread I want to continue to be run getevent command.
How this can be done? any idea?
Edit
getErrorStream()
getevent [-t] [-n] [-s switchmask] [-S] [-v [mask]] [-d] [-p] [-i] [-l] [-q] [-c count] [-r] [device]
-t: show time stamps
-n: don't print newlines
-s: print switch states for given bits
-S: print all switch states
-v: verbosity mask (errs=1, dev=2, name=4, info=8, vers=16, pos. events=32, props=64)
-d: show HID descriptor, if available
-p: show possible events (errs, dev, name, pos. events)
-i: show all device info and possible events
-l: label event types and names in plain text
-q: quiet (clear verbosity mask)
-c: print given number of events then exit
-r: print rate events are received
GetErrorStream comes out as the result of a manual getevent.
If the instruction is to operate normally, it would not have been any log (the other normal commands actually did. Ex) ls )
I think getevent command seems to have caused various problems since the CALLBACK method.
Edit: I think now I got your problem. The pipe behavior is not cross-platform, as it is a functionality of the executed shell. Thats why it is not working as you expect in java. Try to use a script like the following to spawn a new shell which implements the pipe function as you expect.
String[] cmd = {
"/system/bin/sh",
"-c",
"getevent | grep event1"
};
I'll keep the rest of the answer, as it could help somebody.
I think you are executing getevent | grep event1 without the correct permissions. At least you need to be root user or in input group.
Edit: I still think you do not have the correct permissions. Of course, for running getevent no special permissions are necessary, as it can be executed by everyone. But keep in mind, that getevent reads /dev/input/*, which has the following permissions:
crw-rw---- root input 13, 64 2016-01-24 21:34 event1
Try to make sure your application is really allowed to read event1.
You should also try to use getErrorStream() instead of getInputStream() to see what is going wrong.
Regarding the second part of your question, I am not sure I understand it correctly. You want to execute the command periodically in the same thread right? (I did not, see edit below.)
Then you can simply run your code in a while(1) loop and sleep as long as you want. A bit more advanced would be to use a Timer and a TimerTask.
If you want to control every loop from outside your Thread you can simply use Object.wait() and Object.notify(). Another, also more advanced, possibility is to use Thread pools. The interesting one for you could be the SingleThreadExecutor.
Edit: As I thought, I did not understood you correctly. However, I'll keep the second part of my answer, maybe it helps someone else.
For your use case your code should work correctly. I think it does return immediately because an error is happening. As described above, try to use getErrorStream() to see what is actually happening.
I need my app to perform some su commands programatically (phone is rooted).
When done using adb, the commands work.
For instance:
su -c "mkdir /sdcard/testdir" creates a directory called "testdir" in /sdcard.
When I call:
p = Runtime.getRuntime().exec("su -c \"mkdir /sdcard/testdir\"");
p.waitFor();
It just moves on and no change happens.
I tried reading the input:
DataInputStream dis = new DataInputStream(p.getInputStream());
while((temp = dis.readLine())!=null)
Log.d(ctx.TAG,"shell:"+temp);
But it reports nothing (loop does 0 iterations).
Has anyone ever faced this issue before? How can it be solved?
Needless to day, non-su commands do work programatically with this method.
Note: I gave mkdir as an example (I know it doesn't necessarily require su). I need a lot of varied commands to be performed under su
Thank you!
EDIT: when I call su -c "id" programatically, there's output that uid=0.
I can get stuck on a problem for days, and the moment I gather up the courage to ask about it on StackOverflow, it is solved within minutes.
The fix is:
p=Runtime.getRuntime().exec("su");
DataOutputStream dos = new DataOutputStream(p.getOutputStream());
dos.writeBytes("mkdir /sdcard/testdir\n");
dos.writeBytes("exit\n");
dos.flush();
dos.close();
p.waitFor();
Don't forget \n at the end of each command you write to the DataOutputStream, as it will not work without it.
You wrote that you "need varied commands to be performed under su". Note that the use of "Runtime.exec()" is discouraged by Chainfire, the developer of the most famous SuperSU root app.
It is tempting to use Runtime.getRuntime().exec("su -c [command]");, but you should be aware that [command] should be a single parameter, and thus may require quoting. Unfortunately both quoting the [command] parameter as well as passing the paramaters as separate variables to either Runtime.exec() or ProcessBuilder does not work consistently across all Android versions, and thus this construct should be avoided entirely. It is not impossible to do this right - but there's a high risk of problems.
See the How to SU Document. So you might want to follow his recommendation here:
3.2. Making the call
A common method to call su that avoids the known issues listed above is by creating an interactive shell and piping commands to it. This is done by calling Runtime.getRuntime().exec("su");, and retrieving input and output streams from the returned Process object. Doing this is a fairly straight-forward piece of code, but including the debug logs and checks it's a bit long to reproduce here.
The core code is located here: [libsuperuser :: Shell.java # GitHub]. Shell.run() is a generic call to run shell code, the following more specific (static) utility functions are the ones you will probably end up using:
List<String> Shell.SH.run(String command)
List<String> Shell.SH.run(List<String> commands)
List<String> Shell.SH.run(String[] commands)
List<String> Shell.SU.run(String command)
List<String> Shell.SU.run(List<String> commands)
List<String> Shell.SU.run(String[] commands)
The SH variants are used for a non-root shell, where the SU variants are used for a root shell. These calls return a List containing the output of the shell commands. If there was no output, the list is empty, but not null. The result is only null in case an error occured - including the user not granting your app su access. These are blocking calls.
Note that in debug compiles, all shell STDIN/STDOUT/STDERR will be logged to logcat, and these calls will (intentionally) crash your app if called from the main thread. The reason for this will be discussed in section 4. When to call su.
If you use double quotes, it will work:
su -c ""command with args""
You might be calling Runtime.getRuntime().exec() in main thread and p.waitFor() makes your main thread wait until it executes. Try calling in another thread, like the following snippet.
new Thread(){
#override
public void run(){
p = Runtime.getRuntime().exec("su -c \"mkdir /sdcard/testdir\"");
p.waitFor();
}.start();
}
So I need to make a Qt Application (with GUI) that executes the "adb logcat" command (it's a log that keeps being generated until ^c is pressed).
I need a GUI button to make the process stop and pass the output to a Text Browser.
This is the code I use to get the QProcess output:
QProcess process;
process.start("adb logcat");
process.waitForFinished(-1);
QByteArray logcatOut = process.readAllStandardOutput();
ui->devicesOutput->setText(logcatOut);
Thank you
process.waitForFinished(-1);
would prevent your program of being executed further, till the process "adb" has finished.
So your GUI will be frozen.
You should define QProcess process as a class variable. Use QProcess
*process; instead of creating it on stack. (Best practice for all QObject derivates)
Declare a slot which handles clicked-signal of your button.
call process->terminate() in the slot.
use QProcess::terminate to stop running app
I want to execute the screenshot command "adb shell /system/bin/screencap -p /sdcard/img.png" into C. I was searching for the same and I got a solution for another command and I modified the command as
execl("/system/bin/screencap", "-p", "storage/sdcard0/screenShot.png", (char *)NULL);
but when I run my application and call method of above command, application gets crash.
How should I modify the "/system/bin/screencap -p /sdcard/img.png" command to run from C code.
Update after tom answer
Application is getting closed again and here is log
06-21 11:52:01.488: I/WindowState(279): WIN DEATH: Window{40fed2c0 u0 com.mytest.ndktestapplication/com.mytest.ndktestapplication.MainActivity}
06-21 11:52:01.498: I/ActivityManager(279): Process com.mytest.ndktestapplication (pid 7745) has died.
06-21 11:52:01.498: W/ActivityManager(279): Force removing ActivityRecord{40ea9ab8 u0 com.mytest.ndktestapplication/.MainActivity}: app died, no saved state
This is the expected result of exec() family functions.
What they do is replace the current program with the specified one. So bye-bye app.
To avoid that you would first need to call fork(), and then call exec() only in the child, something like this:
if (!fork()) {
// fork() returned zero, so we are in the child
execl...
}
You might also have to do some cleanup before calling the exec function.
Note however that you will not be able to take a screenshot from an app on most devices, as application code runs under a user id which lacks the permission to do so. But I seem to recall that there was a narrow period where some devices shipped without permission checks on this functionality, so it might work on those.
The invocation is
execl(path, arg0, arg1, ..., (char*) NULL);
The second argument, arg0, is the name the program is told was used to invoke it. The actual arguments given to the program only start at arg1.
So you should change your code to
execl("/system/bin/screencap", "screencap", "-p", "<pic>", (char *)NULL);
our Android app spawns a logcat shell process and then reads its result for processing.
However, when the app stops (e.g. when restarted after recompilation during development), the logcat process will keep running. Here's an example of this behaviour:
processes = new ArrayList<Process>();
try {
processes.add(Runtime.getRuntime().exec("logcat -v time"));
processes.add(Runtime.getRuntime().exec("logcat -v time"));
processes.add(Runtime.getRuntime().exec("logcat -v time"));
processes.add(Runtime.getRuntime().exec("logcat -v time"));
} catch (IOException e) {
// oh no!
}
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
for (Process p : processes) {
p.destroy();
}
};
});
Add this to the onCreate() method of a test app, start it, then force-stop it using the settings manager. The child processes will keep running, now with a parent-id of 1.
In How to kill logcat process initiated by Android application? it was suggested to use ProcessBuilder, but that wasn't a solution and the process will keep running, too.
In Stop child process when parent process stops it was suggsted to use a shutdown hook - that doesn't work either as demonstrated above.
Thanks!
What you could do is spwaning another script of which the sole purpose is to watch your Java program. Whenever it dies, kill all of its children too.
A fragile example:
int pid = android.os.Process.myPid();
String script = "while [ -d /proc/" + pid + " ];do sleep 1;done; killall logcat";
Process p = Runtime.getRuntime().exec("/system/bin/sh", "-c", script);
This assumes that your process does not run as root, thereby only killing its own logcat processes. In your shutdown function, you should first kill the other processes (logcat) and then run p.destroy(); to stop this killer script.
The script above can be improved by removing the use of killall. Instead, get the process IDs of your logcat processes using Reflection (this answer points to http://www.golesny.de/p/code/javagetpid) and pass them to kill.