web-dev-qa-db-fra.com

Lire les données binaires stdout à partir du shell adb?

Est-il possible de lire la sortie standard binaire à partir d'une commande adb Shell? Par exemple, tous les exemples d'utilisation de screencap incluent deux étapes:

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

Cependant, le service prend en charge l'écriture sur stdout. Vous pouvez par exemple procéder comme suit:

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

Et ça marche aussi bien. Mais qu'en est-il de la lecture de la sortie sur ADB? Ce que je veux faire est le suivant:

adb Shell screencap -p > foo3.png

Et évitez l’écriture intermédiaire sur la carte SD. Cela génère quelque chose qui ressemble à un fichier PNG (exécuter strings foo3.png génère quelque chose avec un IHDR, IEND, etc.) et est approximativement de la même taille, mais le fichier est corrompu en ce qui concerne les lecteurs d’images.

J'ai également essayé de le faire en utilisant ddmlib en Java et les résultats sont les mêmes. Je serais heureux d'utiliser n'importe quelle bibliothèque nécessaire. Mon objectif est de réduire le temps total nécessaire à la capture. Sur mon appareil, en utilisant la solution à deux commandes, il faut environ 3 secondes pour obtenir l’image. Utiliser ddmlib et capturer stdout prend moins de 900 ms, mais cela ne fonctionne pas!

Est-il possible de faire cela?

EDIT: Voici l'hexdump de deux fichiers. Le premier, screen.png vient de stdout et est corrompu. La seconde, xscreen, provient de la solution à deux commandes et fonctionne. Les images doivent être visuellement identiques.

$ 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  |.,......#*.....@|
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"..@.|
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].|

Un simple coup d’œil suffit pour ajouter quelques octets supplémentaires 0x0d (13). Retour en calèche ?? Est-ce que ça vous dit quelque chose? Est-ce que ça mélange dans certaines lignes vierges?

53
Eric Lange

Désolé de poster une réponse à une vieille question, mais je viens de constater ce problème moi-même et je voulais le faire uniquement via Shell. Cela a bien fonctionné pour moi:

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

Ce ^M est un caractère obtenu en appuyant sur ctrl + v -> ctrl + m, je viens de remarquer que cela ne fonctionne pas lors du copier-coller.

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

a fait le tour pour moi aussi.

45
Tomas

Contrairement à adb Shell, la commande adb exec-out n'utilise pas pty qui modifie la sortie binaire. Donc tu peux faire

adb exec-out screencap -p > test.png

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

Notez que si vous utilisez cette technique pour une commande produisant une sortie sur STDERR, vous devez la rediriger vers /dev/null, sinon adb inclura STDERR dans son STDOUT en altérant votre sortie. Par exemple, si vous essayez de sauvegarder et de compresser un répertoire:

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

Comme indiqué, "adb Shell" effectue une conversion de saut de ligne (0x0a) en retour-chariot + saut de ligne (0x0d 0x0a). Ceci est effectué par la discipline de ligne pseudo-tty. Comme il n’existe pas de commande "stty" pour le shell, il n’existe pas de moyen facile de jouer avec les paramètres du terminal.

C'est possible de faire ce que vous voulez avec ddmlib. Vous devez écrire du code qui exécute des commandes sur le périphérique, capture la sortie et l'envoie par le fil. C'est plus ou moins ce que DDMS fait pour certaines fonctionnalités. Cela peut être plus de problèmes que sa valeur.

La solution repair() - convertir tous les CRLF en LF - semble fragile mais est en réalité fiable, car la conversion "corrompue" de LF à CRLF est déterministe. J'avais l'habitude de faire la même chose pour réparer par inadvertance les transferts FTP en mode ASCII.

Il convient de noter que le format de fichier PNG est explicitement conçu pour résoudre exactement ce problème (et les problèmes connexes). Le nombre magique commence par 0x89 pour intercepter tout ce qui supprime les bits forts, suivi de "PNG" afin que vous puissiez facilement savoir ce qu'il ya dans le fichier, suivi de CR LF pour intercepter divers convertisseurs de ligne ASCII, puis 0x1a pour capturer les anciens programmes MS-DOS qui utilisaient Ctrl-Z comme marqueur spécial de fin de fichier, puis un fichier LF isolé. En regardant les premiers octets du fichier, vous pouvez dire exactement ce qui lui a été fait.

... ce qui signifie que votre fonction repair() peut accepter les entrées "corrompue" et "pure" et déterminer de manière fiable si elle doit faire quoi que ce soit.

Edit: une note supplémentaire: il est possible pour le binaire côté périphérique de configurer le terminal pour éviter la conversion, en utilisant cfmakeraw(). Consultez la fonction prepareRawOutput() dans la commande screenrecord sous Android 5.0, qui peut envoyer une vidéo brute à partir de la capture d'écran en direct via la connexion ADB Shell.

11
fadden

Après avoir creusé plus profondément dans les vidages hexadécimaux, il est devenu évident que chaque fois que le caractère 0x0A était émis, le shell émettait 0x0D 0x0A. J'ai réparé le flux avec le code suivant et maintenant les données binaires sont correctes. Maintenant, bien sûr, la question est de savoir pourquoi adb Shell fait-il cela? Mais dans tous les cas, cela corrige le problème.

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: Je me suis rendu compte pourquoi il fait cela. Il est en train de convertir LF en CR/LF comme le DOS d’ancienne école. Je me demande s'il y a un réglage quelque part pour désactiver cela?

7
Eric Lange

La meilleure solution consiste à utiliser la commande adb exec-out telle que @AjeetKhadke suggérée.

Laissez-moi illustrer la différence entre les sorties adb Shell et adb exec-out:

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

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

Cela fonctionne sous Windows (j'utilise hexdump à partir de GNUWin32 Hextools pour la démonstration):

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

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

L'inconvénient est que pour pouvoir utiliser la commande adb exec-out, le périphérique et le PC hôte doivent prendre en charge le protocole adb Shell V2. 

Il est plutôt facile de s’occuper du côté PC: il suffit de mettre à jour le paquet platform-tools (qui contient le binaire adb) avec la dernière version. La version du démon adbd sur le périphérique est liée à la version d'Android. Le protocole adb Shell V2 a été introduit dans Android 5.0 avec la refonte complète de adb (passant de c à C++ code). Mais il y avait quelques régressions (aka bugs), donc l'utilité de adb exec-out dans Android 5.x était encore limitée. Enfin, Android 4.x et les appareils plus anciens ne sont pas pris en charge. Heureusement, la part de ces anciens appareils encore utilisés pour le développement diminue rapidement.

6
Alex P.

Oui, sous Unix/Linux/Mac OS X, vous pouvez recevoir une sortie binaire de adb Shell en ajoutant "stty -onlcr;" à votre commande ( NON~ ~ besoin d'être un Android enraciné). 

1.Téléchargez le fichier exécutable "stty".
http://www.busybox.net/downloads/binaries/latest/
Pour le vieil Android, utilisez busybox-armv5l, les autres utilisent busybox-armv7l.
renommer le fichier en "stty"

2.Fichier Uploda "stty" sur Android et définissez les autorisations appropriées.

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

3.Préfixe "stty -onlcr;" à votre commande comme ça;

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

Terminé! 

Mais pour Windows, par défaut, LF d’Android sera converti en CR CR LF.
Même si vous avez fait l'étape précédente, vous obtenez toujours CR LF.
Cela "semble" parce que adb.exe local utilise fwrite, ce qui provoque l'ajout de CR.
Je n’ai aucun moyen à ce sujet, sauf de convertir manuellement CR LF en LF sous Windows. 

4
osexp2003

Autrement: 

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

MAIS, comme @ osexp2003 l’a dit, cela ne fonctionne pas pour Windows.

2
shao hongsheng

Voici une solution qui fonctionne partout (Linux et Windows inclus).

Vous aurez besoin de l'utilitaire netcat, souvent nommé nc.
Si nc et busybox nc échouent sur votre appareil, vous avez besoin d'une nouvelle busybox. Vous pouvez utiliser le programme d'installation de busybox à partir de Play Market (racine obligatoire) ou utiliser solution de osexp2003 (télécharger busybox à partir de site officiel , le mettre dans /data/local/tmp/ sur le périphérique et ajouter une autorisation d'exécution).

L'idée est d'utiliser netcat comme serveur HTTP primitif.
Bien, même pas un serveur approprié en fait. Il enverra simplement son entrée en réponse à la connexion toute _ TCP (requête HTTP du navigateur, connexion telnet ou simplement netcat) et se terminera.

Exécuter la commande dont vous voulez obtenir le résultat comme ceci:

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

Dans l'exemple ci-dessus, screencap -p prend une capture d'écran (image PNG) et la redirige vers netcat.
-l indique à netcat d'agir en tant que serveur (écoute de la connexion), et -p 8080 lui dit d'utiliser le port TCP 8080 . Omettre >/dev/null imprimera simplement, par exemple. demande HTTP GET entrante à votre terminal.
L'exemple ci-dessus attend que quelqu'un se connecte, envoie une capture d'écran et ne se termine que par la suite.
Bien sûr, vous pouvez l’exécuter sans adb Shell, par exemple. de l'émulateur de terminal sur votre appareil.

Après avoir exécuté votre commande comme ci-dessus, vous pouvez télécharger sa sortie depuis votre téléphone en ouvrant http://ip.of.your.phone:8080 dans un navigateur ou par tout autre moyen, par exemple, à l'aide de netcat:

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

Si vous souhaitez utiliser le câble USB pour le téléchargement, vous devez transférer la connexion à l'aide d'ADB comme ceci:

adb forward tcp:7080 tcp:8080

Après cela, vous pouvez utiliser localhost:7080 au lieu de ip.of.your.phone:8080.
Vous pouvez supprimer ce transfert avec la commande suivante:

adb forward --remove tcp:7080
2
EvgEnZh

essayez ceci les gars:

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

Il est également possible d'utiliser base64 pour cela, il suffit donc de l'encoder avec:

base64 foo3.png>foo3.png.base64

puis sur Windows en utilisant un utilitaire base64 ou peut-être notepad ++ pour déchiffrer le fichier.

Ou sous linux/cygwin:

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

Je mets la méthode à utiliser python get octets d'image en utilisant adb ici, peut-être que cela sera utile à quelqu'un qui a rencontré ce problème Le code est le suivant:

 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

Vous pouvez également utiliser la commande standard dos2unix si elle est disponible.

(apt-get install dos2unix si vous êtes sur Debian/Ubuntu. Il y a probablement des versions pour Windows, OS X, etc. quelque part si vous êtes sur Google).

dos2unix convertit CRLF en LF de la même manière que la fonction repair() d'Eric Lange.

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

ou, corrigez un fichier corrompu (sur place):

dos2unix -f screenshot.png

Vous avez besoin du -f pour le forcer à traiter les fichiers binaires.

0
ejm