简体   繁体   中英

Android Java Runtime.exec(“su”) hangs (Device Problem? works fine on other devices)

I'm running a tcpdump command on my android app, that requires root permissions. All of a sudden, the "su" command followed by a series of commands (any command not only the example) causes the process to hang (process.waitFor() waits forever). It was working fine, and out of no where the issue occurred and I tried rolling back to previous versions that work and the problem is persisting.

I am using Redmi Note 4X Specs: Android Version: 6.0 MRA58K | MIUI version: MIUI Global 10.2.1.0 (Not sure what else is needed)

I have tried running it without root and reading the error stream which works fine it produces an error saying I do not have permission (which its suppose to). I have tried manually killing some processes thinking there might be some zombie process blocking new process stream, but I have no idea what is the problem. I have tried googling for answers but they mainly refer to getting output. I have tried the command in adb shell and it works perfectly fine.

java
        BufferedReader in = null;
        Process process = null;
        try {
            process = Runtime.getRuntime().exec("su");
            DataOutputStream cmd = new DataOutputStream(process.getOutputStream());
            cmd.writeBytes("tcpdump --version\n");
            cmd.flush();
            cmd.writeBytes("exit\n");
            cmd.flush();
            in = new BufferedReader(new InputStreamReader(process.getErrorStream()));
            String line = null;
            process.waitFor();
            if(in.ready()){
                line = in.readLine();
            }

            if(line != null){
                Log.d(TAG, line);
            }else{
                Log.d(TAG, "NO OUTPUT!");
            }

            in.close();
        } catch (IOException e) {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException ex) {
                }
            }
        } catch (InterruptedException e) {
            if(in != null){
                try{
                    in.close();
                } catch (IOException ex) { }
            }
        }finally {
            if(process != null){
                process.destroy();
            }
        }

I expect the process to end, but the thread is stuck at process.waitFor().

UPDATE 1.0 I have implemented the ProcessBuilder as suggested. However, there is no output in the log and the Main Thread is still hanging (p.waitFor() waiting forever). As I have also stated, the "su" command does not requests for any password in my environment that I am testing. It is working on a Droid4X emulator.

private void updateSpinner(){

        BufferedReader in = null;
        Process p = null;
        try {
            ProcessBuilder processBuilder = new ProcessBuilder("su", "tcpdump", "--version");
            processBuilder.redirectErrorStream(true);
            p = processBuilder.start();
            in = new BufferedReader(new InputStreamReader(p.getInputStream()));
            String line;
            while ((line = in.readLine()) != null) {
                Log.d(TAG, line);
            }
            in.close();
            int exitCode = p.waitFor();
        } catch (IOException e) {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException ex) {
                }
            }
        } catch (InterruptedException e) {
            if(in != null){
                try{
                    in.close();
                } catch (IOException ex) { }
            }
        }finally {
            if(p != null){
                p.destroy();
            }
        }

You are probably using the wrong command. The su command description is as follows:

"The su command is used to become another user during a login session ."

But what you have here is not a login session.

"The user will be prompted for a password, if appropriate."

In your case, since you are attempting to su to root , the su command will prompt for the root password. Your Java code isn't sending a password. This is probably why su is hanging!


So lets take a step back.

Doing this using su is a bad idea. Your Java application is going to need to "know" the root password. That means it must be stored somewhere that the program can get it. Since the program will be unprivileged at that point, this presents a big security problem.

A better idea would be to use sudo . The sudo command executes a single command as a privileged command; eg sudo ls -l runs ls -l as root. Normally, it will prompt for a password, but:

  • it is possible to configure sudo so that certain groups of users or commands are exempt from this, and

  • you can pass the password to sudo as a command line option.

It is also possible to configure sudo so that certain users can only execute certain commands as root . Read man sudo and man sudoers for the details.


So if you were to use sudo tcpdump --version , from Java (and you have dealt with the password and privilege control issues) you would just need to do something like this:

Process p = new ProcessBuilder("sudo", "tcpdump", "--version").start();
Reader in = new InputStreamReader(p.getInputStream());
// read the version string from the processes stdout
in.close();
int exitCode = p.waitFor();

Note that ProcessBuilder allows you to do lots more besides; see the javadoc .

If you need to run multiple commands, consider writing a shell script to do them and then sudo the script. (But wary of various trickery that someone could do to change the behavior of a shell script via environment variables.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM