Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5f85bb49 authored by Chris Wren's avatar Chris Wren Committed by Android (Google) Code Review
Browse files

Merge "support multiple files and fix logcat smippets for launcher_protoutil" into jb-ub-now-kermit

parents 20e45a8e 2fd31824
Loading
Loading
Loading
Loading
+167 −132
Original line number Diff line number Diff line
@@ -15,7 +15,6 @@
 */
package com.android.launcher3;

import com.android.launcher3.backup.BackupProtos;
import com.android.launcher3.backup.BackupProtos.CheckedMessage;
import com.android.launcher3.backup.BackupProtos.Favorite;
import com.android.launcher3.backup.BackupProtos.Key;
@@ -35,6 +34,8 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.System;
import java.util.LinkedList;
import java.util.List;
import java.util.zip.CRC32;

import javax.xml.bind.DatatypeConverter;
@@ -46,7 +47,7 @@ import javax.xml.bind.DatatypeConverter;
 * <P>When using com.android.internal.backup.LocalTransport, the file names are base64-encoded Key
 * protocol buffers with a prefix, that have been base64-encoded again by the transport:
 * <pre>
 *     echo TDpDQUlnL0pxVTVnOD0= | base64 -D | dd bs=1 skip=2 | base64 -D | launcher_protoutil -k
 *     echo "TDpDQUlnL0pxVTVnOD0=" | launcher_protoutil -k
 * </pre>
 *
 * <P>This tool understands these file names and will use the embedded Key to detect the type and
@@ -56,13 +57,19 @@ import javax.xml.bind.DatatypeConverter;
 * </pre>
 *
 * <P>With payload debugging enabled, base64-encoded protocol buffers will be written to the logs.
 * Copy the encoded log snippet into a file, and specify the type explicitly:
 * Copy the encoded snippet from the log, and specify the type explicitly, with the Logs flags:
 * <pre>
 *    base64 -D icon.log > icon.bin
 *    launcher_protoutil -i icon.bin
 *    echo "CAEYLiCJ9JKsDw==" | launcher_protoutil -L -k
 * </pre>
 * For backup payloads it is more convenient to copy the log snippet to a file:
 * <pre>
 *    launcher_protoutil -L -f favorite.log
 * </pre>
 */
class DecoderRing {

    public static final String STANDARD_IN = "**stdin**";

    private static Class[] TYPES = {
            Key.class,
            Favorite.class,
@@ -74,24 +81,25 @@ class DecoderRing {

    public static void main(String[ ] args)
            throws Exception {
        File source = null;
        Class type = null;
        Class defaultType = null;
        boolean extractImages = false;
        boolean fromLogs = false;
        int skip = 0;
        List<File> files = new LinkedList<File>();

        for (int i = 0; i < args.length; i++) {
            if ("-k".equals(args[i])) {
                type = Key.class;
                defaultType = Key.class;
            } else if ("-f".equals(args[i])) {
                type = Favorite.class;
                defaultType = Favorite.class;
            } else if ("-j".equals(args[i])) {
                type = Journal.class;
                defaultType = Journal.class;
            } else if ("-i".equals(args[i])) {
                type = Resource.class;
                defaultType = Resource.class;
            } else if ("-s".equals(args[i])) {
                type = Screen.class;
                defaultType = Screen.class;
            } else if ("-w".equals(args[i])) {
                type = Widget.class;
                defaultType = Widget.class;
            } else if ("-S".equals(args[i])) {
                if ((i + 1) < args.length) {
                    skip = Integer.valueOf(args[++i]);
@@ -100,50 +108,44 @@ class DecoderRing {
                }
            } else if ("-x".equals(args[i])) {
                extractImages = true;
            } else if ("-L".equals(args[i])) {
                fromLogs = true;
            } else if (args[i] != null && !args[i].startsWith("-")) {
                source = new File(args[i]);
                files.add(new File(args[i]));
            } else {
                System.err.println("Unsupported flag: " + args[i]);
                usage(args);
            }
        }

        if (type == null) {
            if (source == null) {
        if (defaultType == null && files.isEmpty()) {
            // can't infer file type without the key
            usage(args);
            } else {
                Key key = new Key();
                try {
                    byte[] rawKey = DatatypeConverter.parseBase64Binary(source.getName());
                    if (rawKey[0] != 'L' || rawKey[1] != ':') {
                        System.err.println("you must specify the payload type. " +
                                source.getName() + " is not a launcher backup key.");
                        System.exit(1);
        }
                    String encodedPayload = new String(rawKey, 2, rawKey.length - 2);
                    byte[] keyProtoData = DatatypeConverter.parseBase64Binary(encodedPayload);
                    key = Key.parseFrom(keyProtoData);
                } catch (InvalidProtocolBufferNanoException protoException) {
                    System.err.println("failed to extract key from filename: " + protoException);
                    System.exit(1);
                } catch (IllegalArgumentException base64Exception) {
                    System.err.println("failed to extract key from filename: " + base64Exception);
                    System.exit(1);

        if (files.size() > 1) {
            System.err.println("Explicit type ignored for multiple files.");
            defaultType = null;
        }
                // keys are self-checked
                if (key.checksum != checkKey(key)) {
                    System.err.println("key ckecksum failed");
                    System.exit(1);

        if (files.isEmpty()) {
            files.add(new File(STANDARD_IN));
        }

        for (File source : files) {
            Class type = null;
            if (defaultType == null) {
                Key key = decodeKey(source.getName().getBytes(), fromLogs);
                type = TYPES[key.type];
                System.err.println("This is a " + type.getSimpleName() + " backup");
            }
            } else {
                type = defaultType;
            }

            // read in the bytes
            ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
            BufferedInputStream input = null;
        if (source == null) {
            if (source.getName() == STANDARD_IN) {
                input = new BufferedInputStream(System.in);
            } else {
                try {
@@ -173,43 +175,11 @@ class DecoderRing {
            }

            MessageNano proto = null;
            byte[] payload = byteStream.toByteArray();
            if (type == Key.class) {
            Key key = new Key();
            try {
                key = Key.parseFrom(byteStream.toByteArray());
            } catch (InvalidProtocolBufferNanoException e) {
                System.err.println("failed to parse proto: " + e);
                System.exit(1);
            }
            // keys are self-checked
            if (key.checksum != checkKey(key)) {
                System.err.println("key checksum failed");
                System.exit(1);
            }
            proto = key;
                proto = decodeKey(payload, fromLogs);
            } else {
            // other types are wrapped in a checksum message
            CheckedMessage wrapper = new CheckedMessage();
            try {
                MessageNano.mergeFrom(wrapper, byteStream.toByteArray());
            } catch (InvalidProtocolBufferNanoException e) {
                System.err.println("failed to parse wrapper: " + e);
                System.exit(1);
            }
            CRC32 checksum = new CRC32();
            checksum.update(wrapper.payload);
            if (wrapper.checksum != checksum.getValue()) {
                System.err.println("wrapper checksum failed");
                System.exit(1);
            }
            // decode the actual message
            proto = (MessageNano) type.newInstance();
            try {
                MessageNano.mergeFrom(proto, wrapper.payload);
            } catch (InvalidProtocolBufferNanoException e) {
                System.err.println("failed to parse proto: " + e);
                System.exit(1);
            }
                proto = decodeBackupData(payload, type, fromLogs);
            }

            // Generic string output
@@ -245,11 +215,75 @@ class DecoderRing {
                    }
                }
            }

        // success
        }
        System.exit(0);
    }

    // In logcat, backup data is base64 encoded, but in localtransport files it is raw
    private static MessageNano decodeBackupData(byte[] payload, Class type, boolean fromLogs)
            throws InstantiationException, IllegalAccessException {
        MessageNano proto;// other types are wrapped in a checksum message
        CheckedMessage wrapper = new CheckedMessage();
        try {
            if (fromLogs) {
                payload = DatatypeConverter.parseBase64Binary(new String(payload));
            }
            MessageNano.mergeFrom(wrapper, payload);
        } catch (InvalidProtocolBufferNanoException e) {
            System.err.println("failed to parse wrapper: " + e);
            System.exit(1);
        }

        CRC32 checksum = new CRC32();
        checksum.update(wrapper.payload);
        if (wrapper.checksum != checksum.getValue()) {
            System.err.println("wrapper checksum failed");
            System.exit(1);
        }

        // decode the actual message
        proto = (MessageNano) type.newInstance();
        try {
            MessageNano.mergeFrom(proto, wrapper.payload);
        } catch (InvalidProtocolBufferNanoException e) {
            System.err.println("failed to parse proto: " + e);
            System.exit(1);
        }
        return proto;
    }

    // In logcat, keys are base64 encoded with no prefix.
    // The localtransport adds a prefix and the base64 encodes the whole thing again.
    private static Key decodeKey(byte[] payload, boolean fromLogs) {
        Key key = new Key();
        try {
            String encodedKey = new String(payload);
            if (!fromLogs) {
                byte[] rawKey = DatatypeConverter.parseBase64Binary(encodedKey);
                if (rawKey[0] != 'L' || rawKey[1] != ':') {
                    System.err.println(encodedKey + " is not a launcher backup key.");
                    System.exit(1);
                }
                encodedKey = new String(rawKey, 2, rawKey.length - 2);
            }
            byte[] keyProtoData = DatatypeConverter.parseBase64Binary(encodedKey);
            key = Key.parseFrom(keyProtoData);
        } catch (InvalidProtocolBufferNanoException protoException) {
            System.err.println("failed to extract key from filename: " + protoException);
            System.exit(1);
        } catch (IllegalArgumentException base64Exception) {
            System.err.println("failed to extract key from filename: " + base64Exception);
            System.exit(1);
        }

        // keys are self-checked
        if (key.checksum != checkKey(key)) {
            System.err.println("key ckecksum failed");
            System.exit(1);
        }
        return key;
    }

    private static void writeImageData(byte[] data, String path) {
        FileOutputStream iconFile = null;
        try {
@@ -289,6 +323,7 @@ class DecoderRing {
        System.err.println("\t-w\tdecode a widget");
        System.err.println("\t-S b\tskip b bytes");
        System.err.println("\t-x\textract image data to files");
        System.err.println("\t-l\texpect data from logcat, instead of the local transport");
        System.err.println("\tfilename\tread from filename, not stdin");
        System.exit(1);
    }