Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android take screenshot on rooted device

UPDATE There are a number of other posts asking how to get a Screenshot in android but none seemed to have a full answer of how to do so. Originally I posted this as a question due to a particular issue I was running into while attempting to open a stream to the Frame Buffer. Now I've swapped over to dumping the Frame Buffer to a file so I've updated my post to show how I got there. For reference (and acknowledgement), I found the command to send the FrameBuffer to a file from this post (unfortunately he didn't provide how he got to that point). I'm just missing how to turn the raw data I pulled from the Frame Buffer into an actual image file.

My intention was to take a full dump of the actual screen on an Android Device. The only way I could find to do so without using the adb bridge was to directly access the Frame Buffer of the system. Obviously this approach will require root privileges on the device and for the app running it! Fortunately for my purposes I have control over how the Device is set up and having the device rooted with root privileges provided to my application is feasible. My testing is currently being done on an old Droid running 2.2.3.

I found my first hints of how to approach it from https://stackoverflow.com/a/6970338/1446554. After a bit more research I found another article that describes how to properly run shell commands as root. They were using it to execute a reboot, I use it to send the current frame buffer to an actual file. My current testing has only gotten as far as doing this via ADB and in a basic Activity (each being provided root). I will be doing further testing from a Service running in the background, updates to come! Here is my entire test activity that can export the current screen to a file:

public class ScreenshotterActivity extends Activity {
    public static final String TAG = "ScreenShotter";

    private Button _SSButton;
    private PullScreenAsyncTask _Puller;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);


        _SSButton = (Button)findViewById(R.id.main_screenshotButton);
        _SSButton.setOnClickListener(new OnClickListener() {

            public void onClick(View v) {
                if (_Puller != null)
                    return;
                //TODO: Verify that external storage is available! Could always use internal instead...

                _Puller = new PullScreenAsyncTask();
                _Puller.execute((Void[])null);
            }
        });
    }

    private void runSuShellCommand(String cmd) {
        Runtime runtime = Runtime.getRuntime();
        Process proc = null;
        OutputStreamWriter osw = null;
        StringBuilder sbstdOut = new StringBuilder();
        StringBuilder sbstdErr = new StringBuilder();

        try { // Run Script
            proc = runtime.exec("su");
            osw = new OutputStreamWriter(proc.getOutputStream());
            osw.write(cmd);
            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())));
    }

    private String readBufferedReader(InputStreamReader input) {

        BufferedReader reader = new BufferedReader(input);
        StringBuilder found = new StringBuilder();
        String currLine = null;
        String sep = System.getProperty("line.separator");
        try {
            // Read it all in, line by line.
            while ((currLine = reader.readLine()) != null) {
                found.append(currLine);
                found.append(sep);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return null;
    }

    class PullScreenAsyncTask extends AsyncTask<Void, Void, Void> {

        @Override
        protected Void doInBackground(Void... params) {

            File ssDir = new File(Environment.getExternalStorageDirectory(), "/screenshots");
            if (ssDir.exists() == false) {
                Log.i(TAG, "Screenshot directory doesn't already exist, creating...");
                if (ssDir.mkdirs() == false) {
                    //TODO: We're kinda screwed... what can be done?
                    Log.w(TAG, "Failed to create directory structure necessary to work with screenshots!");
                    return null;
                }
            }
            File ss = new File(ssDir, "ss.raw");            
            if (ss.exists() == true) {
                ss.delete();
                Log.i(TAG, "Deleted old Screenshot file.");
            }
            String cmd = "/system/bin/cat /dev/graphics/fb0 > "+ ss.getAbsolutePath();
            runSuShellCommand(cmd);
            return null;
        }

        @Override
        protected void onPostExecute(Void result) {
            super.onPostExecute(result);
            _Puller = null;
        }
    }
}

This also requires adding the android.permission.WRITE_EXTERNAL_STORAGE permission to the Manifest. As suggested in this post. Otherwise it runs, doesn't complain, doesn't create the directories nor the file.

Originally I couldn't get usable data from the Frame Buffer due to not understanding how to properly run shell commands. Now that I've swapped to using the streams for executing commands I can use '>' to send the Frame Buffer's current data to an actual file...

like image 357
cgolden Avatar asked Jun 09 '12 23:06

cgolden


People also ask

How do you screenshot a restricted app on a rooted phone?

Click on General, and enable these two options — Use screen context, and Donate screen captures (optional). Now go back to the restricted app and open Google Assistant again by swiping from the corner of the screen or long press the home button. Once launched, select the Share Screenshot option.

How do I take a screenshot on my Android without rooting?

Use the Android Screenshot Shortcut These days, taking screenshots using nothing more than your device is a breeze. Press and hold the Power + Volume Down buttons at the same time, and you'll see a brief onscreen animation followed by a confirmation in the notification bar that the action was successful.

How do you force a screenshot on an Android?

To take a screenshot on Android, press and hold the Power button then choose Screenshot from the menu. If there is no screenshot restriction imposed by the app, the image saves to Device > Pictures > Screenshots by default. However, if you see a notification that says, “Couldn't save screenshot.


3 Answers

An easy solution for ICS devices is to use the following from the command line

adb shell /system/bin/screencap -p /sdcard/screenshot.png
adb pull /sdcard/screenshot.png screenshot.png

This'll save the screenshot.png file in the current directory.

Tested on a Samsung Galaxy SII running 4.0.3.

like image 131
Ben Clayton Avatar answered Oct 22 '22 18:10

Ben Clayton


Programmatically you can run "adb shell /system/bin/screencap -p /sdcard/img.png" as below :

Process sh = Runtime.getRuntime().exec("su", null,null);
OutputStream  os = sh.getOutputStream();
os.write(("/system/bin/screencap -p " + "/sdcard/img.png").getBytes("ASCII"));
os.flush();
os.close();
sh.waitFor();
like image 32
Viswanath Lekshmanan Avatar answered Oct 22 '22 18:10

Viswanath Lekshmanan


That would be different for different phones. It depends on the underlying graphics format of your device. You can poll what the graphics format is using system calls. If you are only going to run this on devices that you know the graphics format of you can write a converter that turns it into a known format.

You can have a look at the following project: http://code.google.com/p/android-fb2png/

If you look at the source code for fb2png.c you can see that they poll FBIOGET_VSCREENINFO which contains info about how the device stores the screen image in memory. Once you know that, you should be able to convert it into a format you can use.

I hope this helps.

like image 24
rrostt Avatar answered Oct 22 '22 18:10

rrostt