adb: add interfaces for Encoder/Decoder.

More groundwork to support more compression algorithms.

Bug: https://issuetracker.google.com/150827486
Test: python3 -m unittest test_device.FileOperationsTest{Uncompressed,Brotli}
Change-Id: I638493083b83e3f6c6854b631471e9d6b50bd79f
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index 04b250d..9078ae9 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -129,20 +129,20 @@
         " reverse --remove-all     remove all reverse socket connections from device\n"
         "\n"
         "file transfer:\n"
-        " push [--sync] [-zZ] LOCAL... REMOTE\n"
+        " push [--sync] [-z ALGORITHM] [-Z] LOCAL... REMOTE\n"
         "     copy local files/directories to device\n"
         "     --sync: only push files that are newer on the host than the device\n"
-        "     -z: enable compression\n"
+        "     -z: enable compression with a specified algorithm (any, none, brotli)\n"
         "     -Z: disable compression\n"
-        " pull [-azZ] REMOTE... LOCAL\n"
+        " pull [-a] [-z ALGORITHM] [-Z] REMOTE... LOCAL\n"
         "     copy files/dirs from device\n"
         "     -a: preserve file timestamp and mode\n"
-        "     -z: enable compression\n"
+        "     -z: enable compression with a specified algorithm (any, none, brotli)\n"
         "     -Z: disable compression\n"
-        " sync [-lzZ] [all|data|odm|oem|product|system|system_ext|vendor]\n"
+        " sync [-l] [-z ALGORITHM] [-Z] [all|data|odm|oem|product|system|system_ext|vendor]\n"
         "     sync a local build from $ANDROID_PRODUCT_OUT to the device (default all)\n"
         "     -l: list files that would be copied, but don't copy them\n"
-        "     -z: enable compression\n"
+        "     -z: enable compression with a specified algorithm (any, none, brotli)\n"
         "     -Z: disable compression\n"
         "\n"
         "shell:\n"
@@ -1314,12 +1314,34 @@
     return 0;
 }
 
+static CompressionType parse_compression_type(const std::string& str, bool allow_numbers) {
+    if (allow_numbers) {
+        if (str == "0") {
+            return CompressionType::None;
+        } else if (str == "1") {
+            return CompressionType::Any;
+        }
+    }
+
+    if (str == "any") {
+        return CompressionType::Any;
+    } else if (str == "none") {
+        return CompressionType::None;
+    }
+
+    if (str == "brotli") {
+        return CompressionType::Brotli;
+    }
+
+    error_exit("unexpected compression type %s", str.c_str());
+}
+
 static void parse_push_pull_args(const char** arg, int narg, std::vector<const char*>* srcs,
-                                 const char** dst, bool* copy_attrs, bool* sync, bool* compressed) {
+                                 const char** dst, bool* copy_attrs, bool* sync,
+                                 CompressionType* compression) {
     *copy_attrs = false;
-    const char* adb_compression = getenv("ADB_COMPRESSION");
-    if (adb_compression && strcmp(adb_compression, "0") == 0) {
-        *compressed = false;
+    if (const char* adb_compression = getenv("ADB_COMPRESSION")) {
+        *compression = parse_compression_type(adb_compression, true);
     }
 
     srcs->clear();
@@ -1333,13 +1355,13 @@
             } else if (!strcmp(*arg, "-a")) {
                 *copy_attrs = true;
             } else if (!strcmp(*arg, "-z")) {
-                if (compressed != nullptr) {
-                    *compressed = true;
+                if (narg < 2) {
+                    error_exit("-z requires an argument");
                 }
+                *compression = parse_compression_type(*++arg, false);
+                --narg;
             } else if (!strcmp(*arg, "-Z")) {
-                if (compressed != nullptr) {
-                    *compressed = false;
-                }
+                *compression = CompressionType::None;
             } else if (!strcmp(*arg, "--sync")) {
                 if (sync != nullptr) {
                     *sync = true;
@@ -1894,22 +1916,22 @@
     } else if (!strcmp(argv[0], "push")) {
         bool copy_attrs = false;
         bool sync = false;
-        bool compressed = true;
+        CompressionType compression = CompressionType::Any;
         std::vector<const char*> srcs;
         const char* dst = nullptr;
 
-        parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, &copy_attrs, &sync, &compressed);
+        parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, &copy_attrs, &sync, &compression);
         if (srcs.empty() || !dst) error_exit("push requires an argument");
-        return do_sync_push(srcs, dst, sync, compressed) ? 0 : 1;
+        return do_sync_push(srcs, dst, sync, compression) ? 0 : 1;
     } else if (!strcmp(argv[0], "pull")) {
         bool copy_attrs = false;
-        bool compressed = true;
+        CompressionType compression = CompressionType::Any;
         std::vector<const char*> srcs;
         const char* dst = ".";
 
-        parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, &copy_attrs, nullptr, &compressed);
+        parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, &copy_attrs, nullptr, &compression);
         if (srcs.empty()) error_exit("pull requires an argument");
-        return do_sync_pull(srcs, dst, copy_attrs, compressed) ? 0 : 1;
+        return do_sync_pull(srcs, dst, copy_attrs, compression) ? 0 : 1;
     } else if (!strcmp(argv[0], "install")) {
         if (argc < 2) error_exit("install requires an argument");
         return install_app(argc, argv);
@@ -1925,27 +1947,26 @@
     } else if (!strcmp(argv[0], "sync")) {
         std::string src;
         bool list_only = false;
-        bool compressed = true;
+        CompressionType compression = CompressionType::Any;
 
-        const char* adb_compression = getenv("ADB_COMPRESSION");
-        if (adb_compression && strcmp(adb_compression, "0") == 0) {
-            compressed = false;
+        if (const char* adb_compression = getenv("ADB_COMPRESSION"); adb_compression) {
+            compression = parse_compression_type(adb_compression, true);
         }
 
         int opt;
-        while ((opt = getopt(argc, const_cast<char**>(argv), "lzZ")) != -1) {
+        while ((opt = getopt(argc, const_cast<char**>(argv), "lz:Z")) != -1) {
             switch (opt) {
                 case 'l':
                     list_only = true;
                     break;
                 case 'z':
-                    compressed = true;
+                    compression = parse_compression_type(optarg, false);
                     break;
                 case 'Z':
-                    compressed = false;
+                    compression = CompressionType::None;
                     break;
                 default:
-                    error_exit("usage: adb sync [-lzZ] [PARTITION]");
+                    error_exit("usage: adb sync [-l] [-z ALGORITHM] [-Z] [PARTITION]");
             }
         }
 
@@ -1954,7 +1975,7 @@
         } else if (optind + 1 == argc) {
             src = argv[optind];
         } else {
-            error_exit("usage: adb sync [-lzZ] [PARTITION]");
+            error_exit("usage: adb sync [-l] [-z ALGORITHM] [-Z] [PARTITION]");
         }
 
         std::vector<std::string> partitions{"data",   "odm",        "oem",   "product",
@@ -1965,7 +1986,7 @@
                 std::string src_dir{product_file(partition)};
                 if (!directory_exists(src_dir)) continue;
                 found = true;
-                if (!do_sync_sync(src_dir, "/" + partition, list_only, compressed)) return 1;
+                if (!do_sync_sync(src_dir, "/" + partition, list_only, compression)) return 1;
             }
         }
         if (!found) error_exit("don't know how to sync %s partition", src.c_str());