My problem is how get root access once while app start in android.The first time i run this code,it's very correct.But the second time i run this code,it throws a IOException.
I init process with root access in onCreate()
#Override
public void onCreate() {
super.onCreate();
// get root access
try {
process = Runtime.getRuntime().exec("su");
} catch (IOException e) {
e.printStackTrace();
}
}
then,i execute shell with this method.
/**
* execute Shell commands
*
* #param commands
* the commands to execute
*/
public static void execShell(String[] commands) {
DataOutputStream os = null;
try {
// get DataOutputStream
os = new DataOutputStream(
process.getOutputStream());
for (String command : commands) {
if (command == null) {
continue;
}
// donnot use os.writeBytes(commmand), avoid chinese charset
// error
// The first time i run this code,it's very correct.But the
// second time i run this code,it throws a IOException
os.write(command.getBytes());
os.writeBytes("\n");
os.flush();
}
//exit
os.writeBytes("exit\n");
os.flush();
//wait process complete
process.waitFor();
} catch (Exception e) {
e.printStackTrace();
} finally {
//close os
}
}
how to avoid IOException?this is my first time to ask question in stackoverflow,so tried!Because my english is very poor
Related
I have a custom device android 4.3. Problem occurs with some commands, one of an example:
su -c 'pm enable com.android.systemui'
When I run this command over adb it works. However when I run the code programatically using this library it just does not work, no error is shown as well.
Interesting observations:
Shell.SU.available() : false
Shell.SU.isSELinuxEnforcing() : false
Ok so device is rooted. Any reason why you are trying to do that command using that library?
What I am trying to say is why can't you just run the shell command yourself?
runRootCommand method:
static boolean runRootCommand(String command) {
boolean status = true;
DataOutputStream os = null;
try {
Process process = Runtime.getRuntime().exec("su");
os = new DataOutputStream(process.getOutputStream());
os.writeBytes(command + "\n");
os.writeBytes("exit\n");
os.flush();
process.waitFor();
} catch (IOException | InterruptedException e) {
Log.e(TAG, e.toString());
status = false;
} finally {
try {
if (os != null)
os.close();
} catch (IOException e) {
Log.e(TAG, e.toString());
status = false;
}
}
return status;
}
And then call that method like this:
boolean success = runRootCommand("pm enable com.android.systemui");
if(success) {
// command was successful
} else {
// command was NOT successful
}
This will run the command as "su" (superuser).
Hope this helps.
I have an Activity which every second write a counter to the logcat:
Runnable rLog = new Runnable() {
#Override
public void run() {
i++;
Log.d("bbb", "i= " + i);
handler.postDelayed(this, 1000);
}
};
In addition - I have a service which read from "logcat -s bbb" and log it:
Runnable rGetLog = new Runnable() {
#Override
public void run() {
Runtime rt = Runtime.getRuntime();
Process process = null;
try {
process = rt.exec("logcat -s bbb");
} catch (IOException e1) {
e1.printStackTrace();
}
BufferedReader bufferedReader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
try {
while ((line = bufferedReader.readLine()) != null)
{
Log.d("aaa", "get line = " + line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
};
This code works well. The problem starts when I change the tag "bbb" to a real tag such as "AndroidRuntime" or another tag... I got an empty response from logcat
(if I run at the same time "logcat -s AndroidRuntime" from adb I got lots of lines...)
Who knows what the problem is? what can be different?
Thanks!
From Android Jelly Bean, applications cannot read log entries from other applications, unless your device is rooted and you read the logs as superuser.
try using sudo to get permissions:
process = rt.exec("su && logcat -s YOUR_TAG");
How to run an interactive shell on android? (rooted device)
I need the nexts steps:
1 - Execute shell process (onCreate)
Process p = Runtime.getRuntime().exec(String[]{"su","-c","sh"});
2 - Get the output (onCreate)
BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream));
//dos is a field (a class attribute)
dos = new DataOutputStream(p.getOutputStream());
new Thread(new Runnable() {
#Override
public void run() {
String l;
//wait the console output and write it
while((l = br.readLine()) != null) {
Log.v("info", l);
}
}
}).start();
3 - Execute a command (Button -> onClick())
cmd("wpa_cli");
The cmd method is:
public void cmd(String cmd) {
dos.writeBytes(cmd + "\n");
dos.flush();
}
The Log never shows the console output.
Next step:
4 - Get the output of a subcommand of wpa_cli (another Button -> onClick())
cmd("help");
Should show the wpa_cli help but it doesn't work.
If i press the button many times appears incomplete output (help)
What is the correct way to initialize a process when the Activity is creating and keep it active to interact?
Thanks.
PostData
I replaced the line Log.v("info", l);
with
fos.write(l);
Message msg = handlerTest.obtainMessage();
msg.obj = l;
handlerTest.sendMessage(msg);
fos -> FileOutputStream object
Handler handlerTest = new Handler() {
#Override
handleMessage(Message msg) {
if (msg != null) {
//alert... (String)msg.obj;
}
}
};
And only displays the alert from the direct commands, as help for example. The command status only work when the stream is closed. (after execute cmd("quit"); and cmd("exit");)
I don't understand. stdout is more that one? I can interact with the output without close the stream?
With adb shell i read the output file (created after closing the stream) and it's complete.
PostData2:
The problem is the buffer. Between the binary file (executed) output and java process i can disable the buffer without using Native I/O?
The Android Terminal Emulator works fine, it's using NIO?
Solution by OP.
Without using the tool stdbuf (of coreutils) in Android I found the next solution:
I'm using two processes called process1 and process2: both -> new ProcessBuilder(new String[]{"su", "-c", "sh"}).start();
In the first process I run wpa_cli
dos1.write("wpa_cli".getBytes());
dos1.flush();
In a thread I wait 5 seconds and run wpa_cli in the second process.
dos2.write("wpa_cli".getBytes());
dos2.flush();
To get the output from wpa_cli is necessary to close it with quit
dos1.write("quit".getBytes());
dos1.flush();
The main thread that work with both processes:
new Thread(new Runnable() {
#Override
public void run() {
boolean firstProcess = true;
while (follow) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (follow) {
if (firstProcess) {
cmd("wpa_cli", dos2);
cmd("quit", dos1);
} else {
cmd("wpa_cli", dos1);
cmd("quit", dos2);
}
firstProcess = !firstProcess;
}
}
}
}).start();
Important: Both processes have this (when the process is created):
new Thread(new Runnable() {
#Override
public void run() {
BufferedReader br = new BufferedReader(new InputStreamReader(process1.getInputStream()));
String l;
try {
Message msg;
while ((l = br.readLine()) != null) {
msg = handlerStdout.obtainMessage();
msg.obj = l;
handlerStdout.sendMessage(msg);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
The handler handlerStdout does the desired job. It receives the output of wpa_cli every 5 seconds: the output comes from process1 and process2 alternatively
An alternative solution would be to compile coreutils or stdbuf for Android. Or install compiled coreutils from http://forum.xda-developers.com/showthread.php?t=2613243 (for me not work stdbuf because the libstdbuf.so not found)
With stdbuf -oL wpa_cli the output isn't buffered (buffer one line). Without it the output is buffered (4k).
I have an activity with a listview that displays the products registered with the images of the SDCARD. Some users are reporting that they receive the error "The application closed" while on this activity.
I've done all the tests to try to simulate it, and the error did not occur.
I thought it might be something with the device memory or something.
Here's a class I developed long ago for catching errors in a jar file I had distributed to several friends. It catches output to std out and stderr and writes it to a file.
If your users can use a file manager to find the file that is written they could send it to you. I've done some preliminary testing on an app and it worked for me.
if you use it and have problems, please let me know.
package com.normstools;
import java.io.*;
//------------------------------------------------------------------------
public class SaveStdOutput extends PrintStream {
final static boolean debug = false; // controls debug output
static OutputStream logfile;
static PrintStream oldStdout = null;
static PrintStream oldStderr = null;
private boolean echoOutput = true; //Also output to old setting
// Constructor - we're the only one that can use it!
private SaveStdOutput(PrintStream ps, boolean echoOutput) {
super(ps);
this.echoOutput = echoOutput;
// System.out.println("SaveStdOutput constructor called");
} // end Constructor
//------------------------------------------------------------
// Starts copying stdout and stderr to the file f.
public static void start(String f) throws IOException {
// Create/Open logfile.
OutputStream os = new PrintStream(
new BufferedOutputStream(
new FileOutputStream(f, true))); // append to current
doCommon(os, true);
} // end start()
// Copy STDOUT and STDERR to an output stream
public static void start(OutputStream os) {
doCommon(os, true);
} // end start()
public static void start(OutputStream os, boolean eO) {
doCommon(os, eO);
} // end start()
//-------------------------------------------------------
// Finish up
private static void doCommon(OutputStream os, boolean echoOutput) {
// Only allow to be called once
if (oldStdout != null) {
if (debug)
System.err.println("SaveStdOutput start() called twice");
return; // Exit if already open
}
logfile = os;
// Save old settings.
oldStdout = System.out;
oldStderr = System.err;
// Start redirecting the output.
System.setOut(new SaveStdOutput(System.out, echoOutput));
System.setErr(new SaveStdOutput(System.err, echoOutput));
} // end doCommon()
//--------------------------------------
// Restores the original settings.
public static void stop() {
if (oldStdout == null) {
if (debug)
System.err.println("SaveStdOutput stop() called before start()");
return;
}
System.setOut(oldStdout);
oldStdout = null; //Clear
System.setErr(oldStderr);
try {
logfile.close();
} catch (Exception ex) {
System.err.println("SaveStdOutput stop() ex " + ex.getMessage());
ex.printStackTrace();
}
} // end stop()
// Override the PrintStream write methods
public void write(int b) {
try {
logfile.write(b);
} catch (Exception e) {
e.printStackTrace();
setError();
}
if (echoOutput)
super.write(b);
} // end write()
// PrintStream override.
public void write(byte buf[], int off, int len) {
try {
logfile.write(buf, off, len);
} catch (Exception e) {
e.printStackTrace();
setError();
}
if (echoOutput)
super.write(buf, off, len);
} // end write()
//-------------------------------------------------------------------
// Following for testing SaveStdOutput class: Comment out when done!
public static void main(String[] args) {
try {
// Start capturing characters into the log file.
SaveStdOutput.start("log.txt");
// Test it.
System.out.println("Here's is some stuff to stdout. "
+ new java.util.Date());
System.err.println("Here's is some stuff to stderr.");
System.out.println("Let's throw an exception...");
new Exception().printStackTrace();
throw new Exception("this is thrown");
} catch (Exception e) {
e.printStackTrace();
} finally {
// Stop capturing characters into the log file
// and restore old setup.
SaveStdOutput.stop();
}
System.out.println("This should be to console only!");
} // end main() */
} // end class SaveStdOutput
The main() method has sample usage.
Call the start() method in onStart() and the close() method in onStop(),
or add menu items to control it. Add a few calls to System.out.println()
and some try{}catch blocks with printStackTrace().
Error "The application closed" occurs most of the time when Android detects your application is not loading or responding within 3-5 Seconds, you should try to check the availability of the SD card, space available, permission or even run some of the process in the background. Rewrite your code to handle these situations and it should work.
On a rooted android device, I tried to run a cat command that read kernel log, as follow:
Process p = Runtime.getRuntime().exec("su");
p = Runtime.getRuntime().exec("/system/bin/cat /proc/kmsg");
The su command was successfully executed but not the cat.
I tried to read the output of the command using getInputStream() but nothing was there, as follow:
BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
while ((read = err.read(buffer)) > 0)
{ //read error to buffer
catOutput.append(buffer, 0, read);
}
in.close();
I used the same code with ls command instead of displaying the kernel log, it worked just fine and show me the result.
I wonder if what error I am getting and wantted to see the error message on the shell when executing the cat command. Tried the p.getErrorStream() but it doesn't give me any result.
Could any one help me with this ? Thanks.
Here's a comprehensive example on how to do this - note that I got the idea from this answer:
public void catKmsg() {
Runtime runtime = Runtime.getRuntime();
Process proc = null;
OutputStreamWriter osw = null;
StringBuilder sbstdOut = new StringBuilder();
StringBuilder sbstdErr = new StringBuilder();
String command="/system/bin/cat /proc/kmsg";
try { // Run Script
proc = runtime.exec("su");
osw = new OutputStreamWriter(proc.getOutputStream());
osw.write(command);
osw.flush();
osw.close();
} catch (IOException ex) {
ex.printStackTrace();
} finally {
if (osw != null) {
try {
osw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
try {
if (proc != null) {
proc.waitFor();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
sbstdOut.append(ReadBufferedReader(new InputStreamReader
(proc.getInputStream())));
sbstdErr.append(ReadBufferedReader(new InputStreamReader
(proc.getErrorStream())));
if (proc.exitValue() != 0) {
}
}
I finally found the solution for the problem by using RootTools library.
Recently released (few months after my question was asked), RootTools provides a easy-to-use tool set that helps running commands that required root privilege. I created a wrapper to check if root access is available before executing shell command:
void testRootToolsCommand(String command){
if (RootTools.isRootAvailable())
toastMessage("Root is available !!!");
else {
toastMessage("NO ROOT !!! ");
return;
}
int timeOut = 1000;
try {
List<String> output = RootTools.sendShell(command,timeOut);
toastMessage("OUTPUT of the command \n" + output.toString());
} catch (RootToolsException re) {
toastMessage("Funny thing happened with RootTools!!! ");
} catch (TimeoutException te)
{
toastMessage("Timeout exception - Increase timeout !!! !!! ");
}
catch (Exception e) {
toastMessage(e.getMessage().toString());
}
}
An example of a function call is:
testRootToolsCommand("cat /proc/kmsg > /sdcard/jun11_4h51.txt");
Note: The Tool also support running multiple commands at once.