#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.
Related
I am working on a small proof of concept on a rooted phone, which relys on being able to read dumpsys output.
If I call dumpsys on my (rooted) phone running Android 11 like this, using adb:
adb shell dumpsys telephony.registry | grep "mCi="
I get a pretty long printout. The grep filters for lines containing cell tower IDs, but that shouldn't e important here (it's just an example). Now I'm trying to execute the same command inside a very simple app, and log its output, like this:
private fun test() {
try {
val process = Runtime.getRuntime().exec("su dumpsys telephony.registry | grep \"mCi=\"")
val bufferedReader = BufferedReader(InputStreamReader(process.inputStream))
val string = bufferedReader.readText()
Timber.d("output: $string")
bufferedReader.close() // do I need this?
} catch (e: IOException) {
// handle Exception
}
}
I get no output at all (string length is 0). If I replace my process command with something simple like this: Runtime.getRuntime().exec("echo 'abcde'") the output is logged as intended (output: 'abcde').
I also tried shortening the possible output, in case that was the problem by appending --max-count=1, to have grep only put out the first found line. Again, it works using adb, does not work in code.
what am I doing wrong?
(I am using Timber to print my logs, if anyone doesn't know what that line is in the xample.)
The first thing you should do is to log the stderr stream that is available for your process as well. This will give you information about what is wrong with your command.
Your command is not correctly processed as it is seen as one command. The reason is explained in this answer.
The solution is to use a String[] as an argument of exec and explicitly execute the command with the shell. I wrote some code that executes your command, but it is in Java on an unrooted device. Still, it generates output and grep works.
String[] arrayCommand = {"sh", "-c","dumpsys telephony.registry | grep \"permission\""};
Runtime r = Runtime.getRuntime();
Process process = r.exec(arrayCommand);
String stdoutString = convertInputStreamToString(process.getInputStream());
String stderrString = convertInputStreamToString(process.getErrorStream());
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();
}
I am working on a simple app but don't know how to stop continuous action in terminal such as PING programmatically.
I just want to know the command, then I'll add it to runtime.getruntime.exec...
I know there's a CTRL+C shortcut in pc but how do I execute this on Android?
Sorry for not adding examples, I'm writing from my phone.
Another way:
Process proc = Runtime.getRuntime().exec("ping 127.0.0.1");
proc.destroy();
As you execute your command, you get the relating process. You can use it to stop your ping, too.
You can't directly send CTRL + C, but take a look at Process.sendSignal() (Android Developers)
First, get the process ID of the ping-process.
Then you can easily send a Process.sendSignal(yourPid, Process.SIGNAL_QUIT);
After debugging for a long time I found out how to solve the problem
"Kill results without the ping statistics being returned".
Get pid of ping process.
Ex:
Progress proc = runtime.exec("ping 192.168.1.1");
where proc will be something like Process[pid=2343], so you need to extract 2343.
Then when you are reading the ping output, you can use
"Runtime.getRuntime().exec("kill -INT " + 2243);" to kill the process.
InputStreamReader reader = new InputStreamReader(proc.getInputStream());
BufferedReader buffer = new BufferedReader(reader);
String line;
while ((line = buffer.readLine()) != null) {
echo.append(line).append("\n");
if (UserStopPing) {
Runtime.getRuntime().exec("kill -INT " + temp);
}
proc.waitFor();
This program will stop ping and you will get the statistic also [variable echo].
You can try this.
There is a CTRL option on your screen. Press that and then enter c.
This might help you.
Just in case people are still looking for a solution to this -- On an Android the equivalent of CTRL+C is "Volume down button" + C on your keyboard. This should stop the ping.
Is is possible to run getevent from an Android service and get output similar to what you see when running adb to call getevent from a command prompt on a development machine? When I try something like:
ProcessBuilder builder = new ProcessBuilder()
.command("getevent")
.redirectErrorStream(true)
.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(builder.getInputStream()));
...
the output I get for each device looks like:
could not open /dev/input/event[n], Permission denied
Is it just not possible to access low level information like this because of Android's security protections? Would it be possible on a "rooted" device?
Why I am trying to do this:
I would like to record a user's actions (touch and gesture events) on an Android device for the purpose of usability testing. An accessibility service seems to be the way to go, but the information is not detailed enough. For a swipe gesture, for example, I cannot get the screen coordinates of where the user swiped. I was thinking that getting the low-level input from the touch screen might let me get more detailed information. Maybe there is a better way to do this?
(I'm a newbie in the Android world. This kind of thing is easy on Windows.)
You can do like this.
th = new Thread(new Runnable(){
private Process exec;
#Override
public void run() {
try {
exec = Runtime.getRuntime().exec(new String[]{"su","-c","getevent -t " + device});
InputStreamReader is = new InputStreamReader(
exec.getInputStream());
String s;
BufferedReader br = new BufferedReader(is);
while(((s = br.readLine()) != null) && run){
...
}
is.close();
exec.destroy();
} catch (IOException e) {
e.printStackTrace();
}
}
You must use 'su' to get the root permission, but by this way you can't get the real time event, because there is a buffer size of 4k, you could get data only after contained 4k data.
Search for UIAutomator. This does what you want to do.
Your phone must be rooted to execute getevent/sendevent command.
One way is to install any terminal emulator from play store like Qute: Command Console & Terminal Emulator.
In terminal enter following:
1) su (it'll gain the root access required for getevent)
2) getevent (or getevent -c 8 to output only 8 lines else it would flood the terminal)
What is the easiest to time execution in Android?
I have looked around a bit and I found TimingLogger on the Android SDK,
and instructions here. It looks very convenient.
But I can't get it work. This is my code:
TimingLogger timings = new TimingLogger("TopicLogTag", "Parsing html");
My code to time here...
timings.dumpToLog();
It's supposed to dump the times in LogCat. But I can't see anything.. I What am I doing wrong? Eclipse doesn't show any varnings. I guess it has something with verbose ouput, but I have set LogCat to show Verbose.
Thank you..
I gave it a test run and I am experiencing the same thing. It all boils down to this little bit of the description in the Javadoc for TimingLogger:
If the Log.isLoggable is not enabled
to at least the Log.VERBOSE level for
that tag at creation time then the
addSplit and dumpToLog call will do
nothing.
I did a test locally:
TimingLogger timings = new TimingLogger("MyTag", "Initialization");
Log.d("MyTag", "Is Loggable? " + Log.isLoggable("MyTag", Log.VERBOSE));
timings.dumpToLog();
And oddly, I get an output to the log:
06-28 08:35:18.693: DEBUG/MyTag(24366): Is Loggable? false
But that's it. And since it's false, I doubt TimingLogger is doing anything, based on the TimingLogger code:
90 /**
91 * Clear and initialize a TimingLogger object that will log using
92 * the tag and label that was specified previously, either via
93 * the constructor or a call to reset(tag, label). If the
94 * Log.isLoggable is not enabled to at least the Log.VERBOSE
95 * level for that tag at creation time then the addSplit and
96 * dumpToLog call will do nothing.
97 */
98 public void reset() {
99 mDisabled = !Log.isLoggable(mTag, Log.VERBOSE);
100 if (mDisabled) return;
101 if (mSplits == null) {
102 mSplits = new ArrayList<Long>();
103 mSplitLabels = new ArrayList<String>();
104 } else {
105 mSplits.clear();
106 mSplitLabels.clear();
107 }
108 addSplit(null);
109 }
I'm not sure why Log.isLoggable is returning false when it's obviously logging at above VERBOSE, since my Log.d obviously logged.
You can enable logging for that tag manually from the [Log class Javadoc][3]:
You can change the default level by
setting a system property: 'setprop
log.tag. ' Where
level is either VERBOSE, DEBUG, INFO,
WARN, ERROR, ASSERT, or SUPPRESS.
SUPPRESS will turn off all logging for
your tag. You can also create a
local.prop file that with the
following in it:
'log.tag.=' and
place that in /data/local.prop.
Which I did through adb shell:
$ adb shell
# setprop
usage: setprop <key> <value>
# setprop log.tag.MyTag VERBOSE
#
Results in:
06-28 08:53:42.447: DEBUG/MyTag(24739): Is Loggable? true
06-28 08:53:44.744: DEBUG/MyTag(24739): Initialization: begin
06-28 08:53:44.744: DEBUG/MyTag(24739): Initialization: end, 0 ms
See droidgren's comment on this answer - apparently a call to addSplit is also necessary.
[3]: http://developer.android.com/reference/android/util/Log.html#isLoggable(java.lang.String, int)
I found another more simple solution which measures the exact same time as TimingLogger, which doesn't require setprop.
private long startnow;
private long endnow;
startnow = android.os.SystemClock.uptimeMillis();
*Your time consuming code here*
endnow = android.os.SystemClock.uptimeMillis();
Log.d("MYTAG", "Execution time: " + (endnow - startnow) + " ms");
If you guys take a look at its source code, actually the implementation for the TimingLogger class is quite simple.
So what I did, which perfectly fits for my use case, was to make my own version of the class but changing the reset() method to
public void reset() {
mDisabled = false; // <- This is what has changed.
if (mDisabled) return;
if (mSplits == null) {
mSplits = new ArrayList<Long>();
mSplitLabels = new ArrayList<String>();
} else {
mSplits.clear();
mSplitLabels.clear();
}
addSplit(null);
}
The catch here is changing from
mDisabled = !Log.isLoggable(mTag, Log.VERBOSE);
to
mDisabled = false;
This way we don't have to mess with adb.
If you simply looking for logs as explained in developer.android.com, you will not be able to see logs. So use below command:
adb shell setprop log.tag.MyTag VERBOSE
Note: MyTag is the first parameter you passed when creating new TimingLogger as below:
TimingLogger timings = new TimingLogger("MyTag", "MyMethodName");
For your questions's answer, you should execute below command:
adb shell setprop log.tag.TopicLogTag VERBOSE
And there you are. Happy coding !!!
Sometimes we don't need to know the exact time that took us an operation but we want to know, why that operation took so long. Thus, for speeding up code, we only need to know some kind of relational order of parts of that operation where the one taking up the most time seems to be the one you should optimize. Therefore, android brings method tracing:
Debug.startMethodTracing("YOUR_TRACE_FILE_NAME");
// Do your operations
Debug.stopMethodTracing();
Then, the os writes the trace file containing all call infos to filesystem.
Simply drag'n'drop that file onto traceview.bat and start inspecting what calls took how long.
Benefits:
You can inspect all called functions and methods that have been called while tracing.
No need to synchronize data for multithreaded apps.
Trace is written to file automatically - no log cat magic or whatever is necessary. All data is encapsulated together, ready to be inspected.
As soon as you start adding time measuring and especially logging, you wreck your timing anyways.
Link: http://developer.android.com/tools/debugging/debugging-tracing.html
Try doing:
adb shell logcat