user1112789
user1112789

Reputation: 437

Ensuring right data is read from an InputStream

I have a root application that is supposed to capture the screen at some point during the execution. In order to accomplish this, I interact with the Android shell using the following code:

private static Process su = Runtime.getRuntime().exec("su");
private static DataOutputStream outputStream = new DataOutputStream(su.getOutputStream());
private static DataInputStream inputStream = new DataInputStream(su.getInputStream());

private void CaptureScreen() {
    outputStream.writeBytes("/system/bin/screencap -p\n");
    outputStream.flush();
    Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
    //outputStream.writeBytes("echo test\n");
    //outputStream.flush();
}

It works fine, even when I call it multiple times, but the moment I issue a dummy command that produces a shell output between CaptureScreen calls, BitmapFactory.decodeStream fails. Considering this, I have a few questions:

I know I can get around this problem by writing the image to a file then read it from there, but I would like to avoid I/O operations in favor of performance.

Upvotes: 2

Views: 532

Answers (2)

user1112789
user1112789

Reputation: 437

After toying around with this for a while, I found the answer to my own questions:

  • Although the runtime is single instance, executing a code on it starts a new process on its own. Input and output streams related to this process can only be written/read by this process (unless another process specifically redirects its streams to this process, which is extremely unlikely in my case).
  • Contents of an input stream are indeed "consumed" upon reading, thus calling this function multiple times writes/reads the image data wholly. "Polluting" the input stream of the process with data not related to the image breaks decodeStream's functionality.

Also keep in mind that "su" isn't a command that ends. It does not terminate until called to do so. Here's the revised class I use in my code:

public class BitmapScreencap {
    public final static BitmapScreencap Get = new BitmapScreencap();
    private BitmapScreencap() { }
    public Bitmap Screen() {
        try {
            Process process = Runtime.getRuntime().exec("su");
            OutputStreamWriter outputStream = new OutputStreamWriter(process.getOutputStream());
            outputStream.write("/system/bin/screencap -p\n");
            outputStream.flush();
            Bitmap screen = BitmapFactory.decodeStream(process.getInputStream());
            outputStream.write("exit\n");
            outputStream.flush();
            outputStream.close();
            return screen;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

And it can be called from anywhere within your project as:

BitmapScreencap.Get.Screen();

Upvotes: 2

Denis Borovikov
Denis Borovikov

Reputation: 747

How can I make sure to only get the data I need from the InputStream?

At least you can check exitValue of the Process

Why does CaptureScreen work correctly when called multiple times?

It simply reads stream until gets -1 as result of read. When you send "/system/bin/screencap -p\n" to process, InputStream starts to return new data again.

Upvotes: 0

Related Questions