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);
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
In android native activity you can handle main commands from system in your own method. I know how to do this, but i dont know what all states i can handle. I know few states like APP_CMD_SAVE_STATE, APP_CMD_INIT_WINDOW... but that´s not all. I cant find list of states or android_native_app_glue.h header file anywhere. Can someone refer me or write list of events with their numebers and when they´r called?Thanks
void CMDMethod(struct android_app* app, int32_t state)
{
switch(state)
{
case APP_CMD_SAVE_STANE: //some code
break;
//what can be next cases?
}
}
void android_main(struct android_app* state)
{
state->onAppCmd = CMDMethod;
}
Heres the enum found in android_native_app_glue.h
enum {
APP_CMD_INPUT_CHANGED,
APP_CMD_INIT_WINDOW,
APP_CMD_TERM_WINDOW,
APP_CMD_WINDOW_RESIZED,
APP_CMD_WINDOW_REDRAW_NEEDED,
APP_CMD_CONTENT_RECT_CHANGED,
APP_CMD_GAINED_FOCUS,
APP_CMD_LOST_FOCUS,
APP_CMD_CONFIG_CHANGED,
APP_CMD_LOW_MEMORY,
APP_CMD_START,
APP_CMD_RESUME,
APP_CMD_SAVE_STATE,
APP_CMD_PAUSE,
APP_CMD_STOP,
APP_CMD_DESTROY,
};
APP_CMD_INPUT_CHANGED - Command from main thread: the AInputQueue has changed. Upon processing this command, android_app->inputQueue will be updated to the new queue (or NULL).
APP_CMD_INIT_WINDOW - Command from main thread: a new ANativeWindow is ready for use. Upon receiving this command, android_app->window will contain the new window surface.
APP_CMD_TERM_WINDOW - Command from main thread: the existing ANativeWindow needs to be terminated. Upon receiving this command, android_app->window still contains the existing window; after calling android_app_exec_cmd it will be set to NULL.
APP_CMD_WINDOW_RESIZED - Command from main thread: the current ANativeWindow has been resized. Please redraw with its new size.
APP_CMD_WINDOW_REDRAW_NEEDED - Command from main thread: the system needs that the current ANativeWindow be redrawn. You should redraw the window before handing this to android_app_exec_cmd() in order to avoid transient drawing glitches.
APP_CMD_CONTENT_RECT_CHANGED - Command from main thread: the content area of the window has changed, such as from the soft input window being shown or hidden. You can find the new content rect in android_app::contentRect.
APP_CMD_GAINED_FOCUS - Command from main thread: the app's activity window has gained input focus.
APP_CMD_LOST_FOCUS - Command from main thread: the app's activity window has lost input focus.
APP_CMD_CONFIG_CHANGED - Command from main thread: the current device configuration has changed.
APP_CMD_LOW_MEMORY - Command from main thread: the system is running low on memory. Try to reduce your memory use.
APP_CMD_START - Command from main thread: the app's activity has been started.
APP_CMD_RESUME - Command from main thread: the app's activity has been resumed.
APP_CMD_SAVE_STATE - Command from main thread: the app should generate a new saved state for itself, to restore from later if needed. If you have saved state, allocate it with malloc and place it in android_app.savedState with the size in android_app.savedStateSize. The will be freed for you later.
APP_CMD_PAUSE - Command from main thread: the app's activity has been paused.
APP_CMD_STOP - Command from main thread: the app's activity has been stopped.
APP_CMD_DESTROY - Command from main thread: the app's activity is being destroyed, and waiting for the app thread to clean up and exit before proceeding.
I think I saw something about that in this book http://www.packtpub.com/android-ndk-beginners-guide/book. But can be wrong. In there there definitely is a chapter about creating a completely native activity with that loop processing all those states.
Whether I use this:
process = Runtime.getRuntime().exec("logcat -d time");
or that:
process = new ProcessBuilder()
.command("logcat", "-d", "time")
.redirectErrorStream(true)
.start();
I get the same results: it often hangs within the exec() or start() call, no matter what I tried to do!
The thread running this cannot even be interrupted with Thread.interrupt()! The child process is definitely started and if killed the above commands return.
These calls may fail on first attempt, so THERE IS NO WAY TO READ THEIR OUTPUT! I can also use a simple "su -c kill xxx" command line, same result!
EDIT: Started debugging the java_lang_ProcessManager.cpp file in an NDK project with some debugging logs! So here is what I found so far, after the fork() the parent does this:
int result;
int count = read(statusIn, &result, sizeof(int)); <- hangs there
close(statusIn);
Though the child process is not supposed to block on it: That's what the child does (if started at all!):
// Make statusOut automatically close if execvp() succeeds.
fcntl(statusOut, F_SETFD, FD_CLOEXEC); <- make the parent will not block
// Close remaining unwanted open fds.
closeNonStandardFds(statusOut, androidSystemPropertiesFd); <- hangs here sometimes
...
execvp(commands[0], commands);
// If we got here, execvp() failed or the working dir was invalid.
execFailed:
int error = errno;
write(statusOut, &error, sizeof(int));
close(statusOut);
exit(error);
The child can fail for 2 reproducible reasons:
1- child code is not running, but the parent believes it is!
2- child blocks on
closeNonStandardFds(statusOut, androidSystemPropertiesFd);
In either case the read(statusIn...) in the parent ends in deadlock! and a child process is left dead (and cannot be accessed, pid unknown, no Process object)!
This problem is fixed in Jelly Bean (Android 4.1) but not in ICS (4.0.4) and I guess it will never be fixed in ICS.
Above solution didn't prove to be reliable in any ways, causing more issues on some devices!
So I reverted back to the standard .exec() and kept digging...
Looking at the child code that hangs, I noticed the child process will hang while trying to close all file descriptors inherited from the parent (except the one created within the exec() call) !
So I search the whole app code for any BufferedReader/Writer and similar classes to make sure those would be closed when calling exec()!
The frequency of the issue was considerably reduced, and actually never occured again when I removed the last opened file descriptor before calling exec().
NB: Make sure SU binary is up-to-date, it can actually cause this issue too!
Enjoy your search ;)
Bug fix in Bionic was commited monthes ago, but it still hasn't been included in Android 4.0.4.
I have the same problem on ICS (seem to works fine on Android < 4). Did you find a solution?
A simple workaround could be to call the "exec" method in a dedicated thread with a timeout-join so that this situation could be "detected" (yes I know it's not very elegant...)