Android robi zrzut ekranu na zakorzenionym urządzeniu

AKTUALIZACJA Istnieje wiele innych postów z pytaniem, jak uzyskać zrzut ekranu w Androidzie, ale nikt nie miał pełnej odpowiedzi na pytanie, jak to zrobić. Pierwotnie opublikowałem to jako pytanie z powodu szczególnego problemu, w którym brałem udział podczas próby otwarcia strumienia do bufora ramki. Teraz zamieniłem bufor ramki na plik, więc zaktualizowałem swój post, aby pokazać, jak tam dotarłem. Dla odniesienia (i potwierdzenia) znalazłem polecenie, aby wysłać FrameBuffera do pliku zten post (niestety nie zapewnił, w jaki sposób dotarł do tego punktu). Tęsknię za tym, jak zamienić surowe dane, które wyciągnąłem z bufora ramki, na rzeczywisty plik obrazu.

Moim zamiarem było zrobienie pełnego zrzutu rzeczywistego ekranu na urządzeniu z Androidem. Thetylko sposób, w jaki mogłem to zrobić bez użycia mostu adb, polegał na bezpośrednim dostępie do bufora ramki systemu. Oczywiście takie podejście będzie wymagało uprawnień roota na urządzeniu i na uruchamianej aplikacji! Na szczęście dla moich celów mam kontrolę nad tym, jak urządzenie jest skonfigurowane, a posiadanie urządzenia z uprawnieniami roota do mojej aplikacji jest możliwe. Moje testy są obecnie wykonywane na starym Droidie działającym w wersji 2.2.3.

Znalazłem moje pierwsze wskazówki, jak się do tego zbliżyćhttps://stackoverflow.com/a/6970338/1446554. Po nieco dalszych badaniach znalazłem inny artykuł, który opisuje, jak prawidłowouruchamiaj polecenia powłoki jako root. Używali go do ponownego uruchomienia, używam go do wysłania bieżącego bufora ramki do rzeczywistego pliku. Moje obecne testowanie uzyskało tylko tyle, co za pomocą ADB i podstawowego działania (każde z nich zostało dostarczone jako root). Będę przeprowadzał dalsze testy z poziomu usługi uruchomionej w tle, nadchodzące aktualizacje! Oto cała moja aktywność testowa, która może wyeksportować bieżący ekran do pliku:

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;
        }
    }
}

Wymaga to również dodaniaandroid.permission.WRITE_EXTERNAL_STORAGE pozwolenie na manifest. Jak sugerowano wten post. W przeciwnym razie działa, nie narzeka, nie tworzy katalogów ani pliku.

Pierwotnie nie mogłem uzyskać użytecznych danych z bufora ramki, ponieważ nie rozumiałem, jak poprawnie uruchamiać polecenia powłoki. Teraz, gdy użyłem strumieni do wykonywania poleceń, mogę użyć „>”, aby wysłać bieżące dane bufora ramki do rzeczywistego pliku ...

questionAnswers(3)

yourAnswerToTheQuestion