Android take screenshot on rooted device

10,220

Solution 1

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.

Solution 2

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();

Solution 3

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.

Share:
10,220

Related videos on Youtube

cgolden
Author by

cgolden

Updated on September 16, 2022

Comments

  • cgolden
    cgolden over 1 year

    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...

  • cgolden
    cgolden over 11 years
    This question wasn't related to working from a separate machine. This was purely for an on-device, in the field situation. ADB is nice, but doesn't do me any good in this situation.
  • ashish
    ashish almost 10 years
    Its not working .. Its returning NULL . even I have set the permission <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  • Viswanath Lekshmanan
    Viswanath Lekshmanan almost 10 years
    Which android version are you using
  • ashish
    ashish almost 10 years
    its KitKat (4.4) @ Viswanath
  • Viswanath Lekshmanan
    Viswanath Lekshmanan almost 10 years
    This will work only in rooted phone. Can you check whether, you have such a file at location "/system/bin/screencap"
  • ashish
    ashish almost 10 years
    My device is rooted one .. It has "system/bin/screencap" file and when I am doing this with adb shell I am getting the snapshot , but when i am trying to do this programatically, its not working and returning NULL.
  • Viswanath Lekshmanan
    Viswanath Lekshmanan almost 10 years
    Ok, That you didn't explain,Actually kitkat has a problem with <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> can you please refer to the issues regarding this permission on kitkat
  • ashish
    ashish almost 10 years
    @ Viswanath L - I think I found the problem : KNOX is installed in my device , which is not allowing any app to get root access ..
  • henderso
    henderso about 9 years
    Works great for GoogleTango
  • Susheel
    Susheel about 9 years
    what is the purpose of using sh.waitFor(): ? Also, this code worked for me, but I want to take another screenshot after say 10 seconds or so, but when I try again, it does not work.
  • Susheel
    Susheel about 9 years
    Nevermind: It works perfectly on my rooted device. Thank you!
  • darw
    darw over 3 years
    It's not working when "taking screenshots is not allowed by the app or your organization"