web-dev-qa-db-de.com

Binäre stdout-Daten aus der Adb-Shell lesen?

Kann man binäres stdout von einem adb Shell-Befehl lesen? Zum Beispiel umfassen alle Beispiele für die Verwendung von screencap zwei Schritte:

adb Shell screencap -p /sdcard/foo.png
adb pull /sdcard/foo.png

Der Dienst unterstützt jedoch das Schreiben in stdout. Sie können zum Beispiel Folgendes tun:

adb Shell "screencap -p > /sdcard/foo2.png"
adb pull /sdcard/foo2.png

Und das funktioniert genauso gut. Aber was ist mit dem Lesen der Ausgabe über ADB? Was ich tun möchte, ist folgendes:

adb Shell screencap -p > foo3.png

Und vermeiden Sie das Zwischenschreiben auf die SD-Karte. Dies erzeugt etwas, das aussieht wie eine PNG-Datei (wobei strings foo3.png etwas mit einem IHDR, IEND usw. generiert) und ungefähr die gleiche Größe hat, aber die Datei ist hinsichtlich Bildlesern beschädigt.

Ich habe auch versucht, dies mit ddmlib in Java zu tun, und die Ergebnisse sind die gleichen. Gerne verwende ich jede benötigte Bibliothek. Mein Ziel ist es, die Gesamtzeit für die Aufnahme zu reduzieren. Bei Verwendung der Zwei-Befehls-Lösung dauert es auf meinem Gerät etwa 3 Sekunden, um das Image abzurufen. Die Verwendung von ddmlib und das Aufnehmen von stdout dauert weniger als 900 ms, funktioniert aber nicht!

Kann man das machen?

BEARBEITEN: Hier ist der Hexdump von zwei Dateien. Das erste, screen.png, stammt von stdout und ist beschädigt. Der zweite, xscreen, stammt von der Zwei-Befehls-Lösung und funktioniert. Die Bilder sollten visuell identisch sein.

$ hexdump -C screen.png | head
00000000  89 50 4e 47 0d 0d 0a 1a  0d 0a 00 00 00 0d 49 48  |.PNG..........IH|
00000010  44 52 00 00 02 d0 00 00  05 00 08 06 00 00 00 6e  |DR.............n|
00000020  ce 65 3d 00 00 00 04 73  42 49 54 08 08 08 08 7c  |.e=....sBIT....||
00000030  08 64 88 00 00 20 00 49  44 41 54 78 9c ec bd 79  |.d... .IDATx...y|
00000040  9c 1d 55 9d f7 ff 3e 55  75 f7 de b7 74 77 d2 d9  |..U...>Uu...tw..|
00000050  bb b3 27 10 48 42 16 c0  20 01 86 5d 14 04 11 dc  |..'.HB.. ..]....|
00000060  78 44 9d c7 d1 d1 11 78  70 7e 23 33 8e 1b 38 33  |xD.....xp~#3..83|
00000070  ea 2c 8c 8e 0d 0a 08 a8  23 2a 0e 10 82 ac c1 40  |.,......#*[email protected]|
00000080  12 02 81 24 64 ef ec 5b  ef fb 5d 6b 3b bf 3f ea  |...$d..[..]k;.?.|
00000090  de db dd 49 27 e9 ee 74  77 3a e3 79 bf 5e 37 e7  |...I'..tw:.y.^7.|

$ hexdump -C xscreen.png | head
00000000  89 50 4e 47 0d 0a 1a 0a  00 00 00 0d 49 48 44 52  |.PNG........IHDR|
00000010  00 00 02 d0 00 00 05 00  08 06 00 00 00 6e ce 65  |.............n.e|
00000020  3d 00 00 00 04 73 42 49  54 08 08 08 08 7c 08 64  |=....sBIT....|.d|
00000030  88 00 00 20 00 49 44 41  54 78 9c ec 9d 77 98 1c  |... .IDATx...w..|
00000040  c5 99 ff 3f d5 dd 93 37  27 69 57 5a e5 55 4e 08  |...?...7'iWZ.UN.|
00000050  24 a1 00 58 18 04 26 08  8c 01 83 31 38 c0 19 9f  |$..X..&....18...|
00000060  ef 7c c6 3e 1f 70 f8 7e  67 ee 71 e2 b0 ef ce f6  |.|.>.p.~g.q.....|
00000070  f9 ec 73 04 1b 1c 31 60  23 84 30 22 88 a0 40 10  |..s...1`#.0"[email protected]|
00000080  08 65 69 95 d3 4a 9b c3  c4 4e f5 fb a3 67 66 77  |.ei..J...N...gfw|
00000090  a5 95 b4 bb da a4 73 7d  9e 67 55 f3 ed 50 5d dd  |......s}.gU..P].|

Auf den ersten Blick scheint es, als würden ein paar zusätzliche 0x0d (13) Bytes hinzugefügt. Wagenrückkehr ?? Läutet das Glocken? Mischt es in einigen leeren Zeilen?

53
Eric Lange

Es tut mir leid, auf eine alte Frage eine Antwort zu posten, aber ich bin selbst auf dieses Problem gestoßen und wollte es nur über die Shell tun. Das hat gut für mich funktioniert:

adb Shell screencap -p | sed 's/^M$//' > screenshot.png

Das ^M ist ein Zeichen, das ich durch Drücken von Strg + V -> Strg + M erhalten habe.

adb Shell screencap -p | sed 's/\r$//' > screenshot.png

hat den Trick auch für mich gemacht.

45
Tomas

Im Gegensatz zu adb Shell verwendet der adb exec-out-Befehl keine pty, wodurch die binäre Ausgabe beeinträchtigt wird. Das kannst du tun

adb exec-out screencap -p > test.png

https://Android.googlesource.com/platform/system/core/+/5d9d434efadf1c535c7fea634d5306e18c68ef1f

Wenn Sie diese Technik für einen Befehl verwenden, der eine Ausgabe in STDERR erzeugt, sollten Sie sie nach /dev/null umleiten. Andernfalls wird adb STDERR in seinen STDOUT einschließen, wodurch Ihre Ausgabe beschädigt wird. Wenn Sie beispielsweise versuchen, ein Verzeichnis zu sichern und zu komprimieren:

adb exec-out "tar -zcf - /system 2>/dev/null" > system.tar.gz
75
Ajeet Khadke

Wie bereits erwähnt, führt "adb Shell" eine Konvertierung von Linefeed (0x0a) in Wagenrücklauf + Linefeed (0x0d 0x0a) durch. Dies wird von der Pseudo-tty-Zeilendisziplin durchgeführt. Da der Shell kein "stty" -Befehl zur Verfügung steht, gibt es keine einfache Möglichkeit, sich mit den Terminaleinstellungen auseinanderzusetzen.

Es ist möglich, mit ddmlib das zu tun, was Sie wollen. Sie müssen Code schreiben, der Befehle auf dem Gerät ausführt, die Ausgabe erfasst und über das Kabel sendet. Dies ist mehr oder weniger das, was DDMS für bestimmte Funktionen tut. Dies kann mehr Ärger als sein Wert sein.

Die repair()-Lösung, bei der alle CRLF in LF konvertiert werden, fühlt sich wackelig an, ist jedoch tatsächlich zuverlässig, da die "beschädigte" Konvertierung von LF in CRLF deterministisch ist. Ich habe das Gleiche getan, um versehentliche FTP-Übertragungen im ASCII-Modus zu reparieren.

Es ist erwähnenswert, dass das PNG-Dateiformat explizit darauf ausgelegt ist, genau diese (und verwandten) Probleme zu erfassen. Die magische Zahl beginnt mit 0x89, um alles abzufangen, das hohe Bits entfernt, gefolgt von "PNG", sodass Sie leicht erkennen können, was sich in der Datei befindet, gefolgt von CR LF, um verschiedene ASCII -Zeilenkonverter abzufangen, dann 0x1a alte MS-DOS-Programme, die Ctrl-Z als spezielle Dateiende-Markierung verwendet haben, und dann einen einsamen LF. Wenn Sie sich die ersten Bytes der Datei ansehen, können Sie genau erkennen, was mit der Datei gemacht wurde.

... was bedeutet, dass Ihre repair()-Funktion sowohl "beschädigte" als auch "reine" Eingaben akzeptieren und zuverlässig feststellen kann, ob sie etwas tun muss.

Edit: Eine weitere Anmerkung: Es ist möglich, dass die geräteseitige Binärdatei das tty konfiguriert, um die Konvertierung mit cfmakeraw() zu vermeiden. Siehe prepareRawOutput() im Befehl screenrecord in Android 5.0, mit dem Rohvideos von der Live-Bildschirmaufnahme über die ADB-Shell-Verbindung gesendet werden können.

11
fadden

Nach tieferem Eintauchen in die Hex-Dumps wurde klar, dass die Shell bei jeder Ausgabe des Zeichens 0x0A 0x0D 0x0A emittierte. Ich habe den Stream mit dem folgenden Code repariert und nun sind die Binärdaten korrekt. Nun stellt sich natürlich die Frage, warum adb Shell das tut. In jedem Fall wird dadurch das Problem behoben.

static byte[] repair(byte[] encoded) {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    for (int i=0; i<encoded.length; i++) {
        if (encoded.length > i+1 && encoded[i] == 0x0d && encoded[i+1] == 0x0a) {
            baos.write(0x0a);
            i++;
        } else {
            baos.write(encoded[i]);
        }
    }
    try {
        baos.close();
    } catch (IOException ioe) {

    }

    return baos.toByteArray();      
}

EDIT: Mir wurde klar, warum das so ist. Es konvertiert LF in CR/LF wie alte DOS. Ich frage mich, ob es irgendwo eine Einstellung gibt, um das auszuschalten?

7
Eric Lange

Die beste Lösung ist, den adb exec-out-Befehl wie @AjeetKhadke vorgeschlagen zu verwenden.

Lassen Sie mich den Unterschied zwischen der Ausgabe von adb Shell und adb exec-out erläutern:

~$ adb Shell "echo -n '\x0a'" | xxd -g1
00000000: 0d 0a

~$ adb exec-out "echo -n '\x0a'" | xxd -g1
00000000: 0a

Es funktioniert auch unter Windows (ich verwende hexdump aus GNUWin32 Hextools für die Demo):

C:\>adb Shell "echo -n '\x0a'" | hexdump
00000000: 0D 0A

C:\>adb exec-out "echo -n '\x0a'" | hexdump
00000000: 0A

Der Nachteil ist, dass das Gerät und der Host-PC das adb exec-out V2-Protokoll unterstützen müssen, um von der Verwendung des adb Shell-Befehls profitieren zu können. 

Es ist ziemlich trivial, auf die PC-Seite zu achten - aktualisieren Sie einfach das platform-tools-Paket (das die adb-Binärdatei enthält) auf die neueste Version. Die Version des Daemon adbd auf dem Gerät ist mit der Android-Version verknüpft. Das adb Shell V2-Protokoll wurde in Android 5.0 zusammen mit der vollständigen adb-Überholung (von c bis C++-Code) eingeführt. Es gab jedoch einige Regressionen (sog. Bugs), so dass die adb exec-out-Funktionalität in Android 5.x noch begrenzt war. Und schließlich gibt es keine Unterstützung für Android 4.x und ältere Geräte. Glücklicherweise sinkt der Anteil dieser älteren Geräte, die noch für die Entwicklung verwendet werden, rapide.

6
Alex P.

Ja, unter Unix/Linux/Mac OS X können Sie die binäre Ausgabe von adb Shell erhalten, indem Sie "stty -onlcr;" zu deinem Befehl ( NEIN~~ müssen ein verwurzeltes Android sein). 

1.Laden Sie die ausführbare Datei "stty" herunter.
http://www.busybox.net/downloads/binaries/latest/
Verwenden Sie für altes Android busybox-armv5l, andere verwenden busybox-armv7l.
Datei umbenennen in "stty"

2.Uploda-Datei "stty" auf Android und die entsprechende Berechtigung festlegen.

adb Push somelocaldir/stty /data/local/tmp/   
adb Shell chmod 777 /data/local/tmp/stty 

3."Stty -onlcr" voranstellen zu deinem Befehl so;

adb Shell /data/local/tmp/stty -onlcr\; screencap -p  > somelocaldir/output.png
or:
adb Shell "/data/local/tmp/stty -onlcr; screencap -p"  > somelocaldir/output.png
or (Only for Windows):
adb Shell /data/local/tmp/stty -onlcr; screencap -p  > somelocaldir/output.png

Erledigt! 

Unter Windows wird jedoch standardmäßig LF von Android in CR CR LF konvertiert.
Selbst wenn Sie den obigen Schritt ausgeführt haben, erhalten Sie immer noch CR LF.
Dies scheint, weil local adb.exe fwrite verwendet, wodurch CR vorangestellt wird.
Ich habe keine andere Möglichkeit dazu, CR LF in LF manuell unter Windows-Betriebssystemen zu konvertieren. 

4
osexp2003

Ein anderer Weg: 

adb Shell "busybox stty raw; screencap -p "> foo3.png 

ABER, wie @ osexp2003 sagte, das funktioniert nicht unter Windows.

2
shao hongsheng

Hier ist eine Lösung, die überall funktioniert (einschließlich Linux und Windows).

Sie benötigen das Dienstprogramm netcat, das häufig als nc bezeichnet wird.
Wenn sowohl nc als auch busybox nc Auf Ihrem Gerät fehlschlagen, benötigen Sie busybox. Sie können entweder den busybox-Installer von Play Market (root erforderlich) verwenden oder Lösung von osexp20 (laden Sie die busybox von offizielle Seite herunter, setzen Sie sie auf /data/local/tmp/ Gerät hinzufügen und Ausführungsberechtigung hinzufügen).

Die Idee ist, netcat als primitiven HTTP-Server zu verwenden.
Nun, nicht einmal ein richtiger Server. Es wird nur seine Eingabe als Antwort auf any TCP Verbindung (sei es HTTP-Anfrage vom Browser, Telnet-Verbindung oder nur netcat) und kündigen.

Führen Sie den Befehl, von dem Sie eine Ausgabe erhalten möchten, folgendermaßen aus:

adb Shell 'screencap -p | busybox nc -p 8080 -l >/dev/null'

Im obigen Beispiel erstellt screencap -p Einen Screenshot (PNG-Bild) und leitet ihn an netcat weiter.
-l Weist netcat an, als Server zu fungieren (auf Verbindung zu warten), und -p 8080 Weist es an, TCP zu verwenden Port 8080. Wenn Sie >/dev/null weglassen, wird einfach eine eingehende HTTP-GET-Anfrage an Ihr Terminal gesendet.
Das obige Beispiel wartet darauf, dass jemand eine Verbindung herstellt, einen Screenshot sendet und erst dann beendet.
Natürlich können Sie es auch ohne adb Shell Ausführen, z. vom Terminal-Emulator auf Ihrem Gerät.

Nachdem Sie Ihren Befehl wie oben ausgeführt haben , können Sie die Ausgabe von Ihrem Telefon herunterladen, indem Sie http://ip.of.your.phone:8080 im Browser oder auf andere Weise, zum Beispiel mit netcat:

busybox nc ip.of.your.phone:8080 >screenshot.png

Wenn Sie ein USB-Kabel zum Herunterladen verwenden möchten , müssen Sie die Verbindung mit ADB wie folgt weiterleiten:

adb forward tcp:7080 tcp:8080

Danach können Sie localhost:7080 Anstelle von ip.of.your.phone:8080 Verwenden.
Sie können diese Weiterleitung mit folgendem Befehl entfernen:

adb forward --remove tcp:7080
2
EvgEnZh

versuche diese Jungs:

adb Shell screencap -p | Perl -pe 's/\x0D\x0A/\x0A/g' > screen.png
1
MrG

Es ist auch möglich, base64 dafür zu verwenden, also kodiere es einfach mit:

base64 foo3.png>foo3.png.base64

und dann unter Windows mit einem base64-Dienstprogramm oder möglicherweise notepad ++, um die Datei zu entschlüsseln.

Oder in Linux/Cygwin:

base64 -d foo3.png.base64>foo3.png
1
xdevs23

Ich stelle die Methode zur Verwendung von Python-Get-Image-Bytes mit Adb hier. Vielleicht ist dies für jemanden hilfreich, der auf dieses Problem gestoßen ist. Der Code lautet wie folgt:

 pipe = subprocess.Popen("adb Shell screencap -p",
                      stdin=subprocess.PIPE,
                      stdout=subprocess.PIPE, Shell=True)
 image_bytes = pipe.stdout.read().replace(b'\r\n', b'\n')
 gray_image = cv2.imdecode(np.fromstring(image_bytes, np.uint8), cv2.IMREAD_GRAYSCALE)
0
Wenmin-Wu

Sie können auch den Standardbefehl dos2unix verwenden, sofern verfügbar.

(apt-get install dos2unix wenn Sie Debian/Ubuntu verwenden. Es gibt wahrscheinlich Builds für Windows, OS X usw., wenn Sie googeln.

dos2unix konvertiert CRLF in LF genauso wie die Funktion repair() von Eric Lange.

adb Shell screencap -p | dos2unix -f > screenshot.png

oder reparieren Sie eine beschädigte Datei (direkt):

dos2unix -f screenshot.png

Sie benötigen den -f, um die Verarbeitung von Binärdateien zu erzwingen.

0
ejm