Deprecated 'adb bugreport' with flat files.

Starting on Android N, zipped bugreports contain more information than
flat-file, text bugreports. On N, calls to 'adb bugreport' would still
generate a flat-file bugreport, but with a warning.

With this change, 'adb bugreport' will generate a zipped bugreport in
the current directory, using the bugreport name provided by the
device. Similarly, calling 'adb bugreport dir' will generate a bugreport
with a device-provided name, but in such directory.

BUG: 30451114
BUG: 29448020

Change-Id: Ibc8920dd44a5f62feb15bf3fefdcb0bdbf389a90
diff --git a/adb/bugreport.cpp b/adb/bugreport.cpp
index 91aa44b..213fa2e 100644
--- a/adb/bugreport.cpp
+++ b/adb/bugreport.cpp
@@ -14,31 +14,38 @@
  * limitations under the License.
  */
 
+#include "bugreport.h"
+
 #include <string>
 #include <vector>
 
 #include <android-base/strings.h>
 
-#include "bugreport.h"
+#include "sysdeps.h"
+#include "adb_utils.h"
 #include "file_sync_service.h"
 
-static constexpr char BUGZ_OK_PREFIX[] = "OK:";
-static constexpr char BUGZ_FAIL_PREFIX[] = "FAIL:";
+static constexpr char BUGZ_BEGIN_PREFIX[] = "BEGIN:";
 static constexpr char BUGZ_PROGRESS_PREFIX[] = "PROGRESS:";
 static constexpr char BUGZ_PROGRESS_SEPARATOR[] = "/";
+static constexpr char BUGZ_OK_PREFIX[] = "OK:";
+static constexpr char BUGZ_FAIL_PREFIX[] = "FAIL:";
 
 // Custom callback used to handle the output of zipped bugreports.
 class BugreportStandardStreamsCallback : public StandardStreamsCallbackInterface {
   public:
-    BugreportStandardStreamsCallback(const std::string& dest_file, bool show_progress, Bugreport* br)
+    BugreportStandardStreamsCallback(const std::string& dest_dir, const std::string& dest_file,
+                                     bool show_progress, Bugreport* br)
         : br_(br),
           src_file_(),
+          dest_dir_(dest_dir),
           dest_file_(dest_file),
-          line_message_(android::base::StringPrintf("generating %s", dest_file_.c_str())),
+          line_message_(),
           invalid_lines_(),
           show_progress_(show_progress),
           status_(0),
           line_() {
+        SetLineMessage();
     }
 
     void OnStdout(const char* buffer, int length) {
@@ -80,25 +87,49 @@
                         BUGZ_FAIL_PREFIX);
                 return -1;
             }
+            std::string destination;
+            if (dest_dir_.empty()) {
+                destination = dest_file_;
+            } else {
+                destination = android::base::StringPrintf("%s%c%s", dest_dir_.c_str(),
+                                                          OS_PATH_SEPARATOR, dest_file_.c_str());
+            }
             std::vector<const char*> srcs{src_file_.c_str()};
-            status_ = br_->DoSyncPull(srcs, dest_file_.c_str(), true, line_message_.c_str()) ? 0 : 1;
+            status_ =
+                br_->DoSyncPull(srcs, destination.c_str(), true, line_message_.c_str()) ? 0 : 1;
             if (status_ != 0) {
                 fprintf(stderr,
                         "Bug report finished but could not be copied to '%s'.\n"
                         "Try to run 'adb pull %s <directory>'\n"
                         "to copy it to a directory that can be written.\n",
-                        dest_file_.c_str(), src_file_.c_str());
+                        destination.c_str(), src_file_.c_str());
             }
         }
         return status_;
     }
 
   private:
+    void SetLineMessage() {
+        line_message_ =
+            android::base::StringPrintf("generating %s", adb_basename(dest_file_).c_str());
+    }
+
+    void SetSrcFile(const std::string path) {
+        src_file_ = path;
+        if (!dest_dir_.empty()) {
+            // Only uses device-provided name when user passed a directory.
+            dest_file_ = adb_basename(path);
+            SetLineMessage();
+        }
+    }
+
     void ProcessLine(const std::string& line) {
         if (line.empty()) return;
 
-        if (android::base::StartsWith(line, BUGZ_OK_PREFIX)) {
-            src_file_ = &line[strlen(BUGZ_OK_PREFIX)];
+        if (android::base::StartsWith(line, BUGZ_BEGIN_PREFIX)) {
+            SetSrcFile(&line[strlen(BUGZ_BEGIN_PREFIX)]);
+        } else if (android::base::StartsWith(line, BUGZ_OK_PREFIX)) {
+            SetSrcFile(&line[strlen(BUGZ_OK_PREFIX)]);
         } else if (android::base::StartsWith(line, BUGZ_FAIL_PREFIX)) {
             const char* error_message = &line[strlen(BUGZ_FAIL_PREFIX)];
             fprintf(stderr, "Device failed to take a zipped bugreport: %s\n", error_message);
@@ -112,22 +143,38 @@
             size_t idx2 = line.rfind(BUGZ_PROGRESS_SEPARATOR);
             int progress = std::stoi(line.substr(idx1, (idx2 - idx1)));
             int total = std::stoi(line.substr(idx2 + 1));
-            br_->UpdateProgress(dest_file_, progress, total);
+            br_->UpdateProgress(line_message_, progress, total);
         } else {
             invalid_lines_.push_back(line);
         }
     }
 
     Bugreport* br_;
+
+    // Path of bugreport on device.
     std::string src_file_;
-    const std::string dest_file_;
-    const std::string line_message_;
+
+    // Bugreport destination on host, depending on argument passed on constructor:
+    // - if argument is a directory, dest_dir_ is set with it and dest_file_ will be the name
+    //   of the bugreport reported by the device.
+    // - if argument is empty, dest_dir is set as the current directory and dest_file_ will be the
+    //   name of the bugreport reported by the device.
+    // - otherwise, dest_dir_ is not set and dest_file_ is set with the value passed on constructor.
+    std::string dest_dir_, dest_file_;
+
+    // Message displayed on LinePrinter, it's updated every time the destination above change.
+    std::string line_message_;
+
+    // Lines sent by bugreportz that contain invalid commands; will be displayed at the end.
     std::vector<std::string> invalid_lines_;
+
+    // Whether PROGRESS_LINES should be interpreted as progress.
     bool show_progress_;
+
+    // Overall process of the operation, as returned by Done().
     int status_;
 
-    // Temporary buffer containing the characters read since the last newline
-    // (\n).
+    // Temporary buffer containing the characters read since the last newline (\n).
     std::string line_;
 
     DISALLOW_COPY_AND_ASSIGN(BugreportStandardStreamsCallback);
@@ -137,17 +184,7 @@
 int usage();
 
 int Bugreport::DoIt(TransportType transport_type, const char* serial, int argc, const char** argv) {
-    if (argc == 1) return SendShellCommand(transport_type, serial, "bugreport", false);
-    if (argc != 2) return usage();
-
-    // Zipped bugreport option - will call 'bugreportz', which prints the location
-    // of the generated
-    // file, then pull it to the destination file provided by the user.
-    std::string dest_file = argv[1];
-    if (!android::base::EndsWith(argv[1], ".zip")) {
-        // TODO: use a case-insensitive comparison (like EndsWithIgnoreCase
-        dest_file += ".zip";
-    }
+    if (argc > 2) return usage();
 
     // Gets bugreportz version.
     std::string bugz_stderr;
@@ -156,6 +193,12 @@
     std::string bugz_version = android::base::Trim(bugz_stderr);
 
     if (status != 0 || bugz_version.empty()) {
+        // Device does not support bugreportz: if called as 'adb bugreport', just falls out to the
+        // flat-file version
+        if (argc == 1) return SendShellCommand(transport_type, serial, "bugreport", false);
+
+        // But if user explicitly asked for a zipped bug report, fails instead (otherwise calling
+        // 'bugreport' would generate a lot of output the user might not be prepared to handle)
         fprintf(stderr,
                 "Failed to get bugreportz version: 'bugreportz -v' returned '%s' (code %d).\n"
                 "If the device runs Android M or below, try 'adb bugreport' instead.\n",
@@ -163,6 +206,33 @@
         return status != 0 ? status : -1;
     }
 
+    std::string dest_file, dest_dir;
+
+    if (argc == 1) {
+        // No args - use current directory
+        if (!getcwd(&dest_dir)) {
+            perror("adb: getcwd failed");
+            return 1;
+        }
+    } else {
+        // Check whether argument is a directory or file
+        if (directory_exists(argv[1])) {
+            dest_dir = argv[1];
+        } else {
+            dest_file = argv[1];
+        }
+    }
+
+    if (dest_file.empty()) {
+        // Uses a default value until device provides the proper name
+        dest_file = "bugreport.zip";
+    } else {
+        if (!android::base::EndsWith(dest_file, ".zip")) {
+            // TODO: use a case-insensitive comparison (like EndsWithIgnoreCase
+            dest_file += ".zip";
+        }
+    }
+
     bool show_progress = true;
     std::string bugz_command = "bugreportz -p";
     if (bugz_version == "1.0") {
@@ -175,7 +245,7 @@
         show_progress = false;
         bugz_command = "bugreportz";
     }
-    BugreportStandardStreamsCallback bugz_callback(dest_file, show_progress, this);
+    BugreportStandardStreamsCallback bugz_callback(dest_dir, dest_file, show_progress, this);
     return SendShellCommand(transport_type, serial, bugz_command, false, &bugz_callback);
 }
 
diff --git a/adb/bugreport_test.cpp b/adb/bugreport_test.cpp
index 4089485..f9a0a5e 100644
--- a/adb/bugreport_test.cpp
+++ b/adb/bugreport_test.cpp
@@ -19,6 +19,12 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
+#include <android-base/strings.h>
+#include <android-base/test_utils.h>
+
+#include "sysdeps.h"
+#include "adb_utils.h"
+
 using ::testing::_;
 using ::testing::Action;
 using ::testing::ActionInterface;
@@ -122,28 +128,38 @@
 
 class BugreportTest : public ::testing::Test {
   public:
-    void SetBugreportzVersion(const std::string& version) {
+    void SetUp() {
+        if (!getcwd(&cwd_)) {
+            ADD_FAILURE() << "getcwd failed: " << strerror(errno);
+            return;
+        }
+    }
+
+    void ExpectBugreportzVersion(const std::string& version) {
         EXPECT_CALL(br_,
                     SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -v", false, _))
             .WillOnce(DoAll(WithArg<4>(WriteOnStderr(version.c_str())),
                             WithArg<4>(ReturnCallbackDone(0))));
     }
 
-    void ExpectProgress(int progress, int total) {
-        EXPECT_CALL(br_, UpdateProgress(HasSubstr("file.zip"), progress, total));
+    void ExpectProgress(int progress, int total, const std::string& file = "file.zip") {
+        EXPECT_CALL(br_, UpdateProgress(StrEq("generating " + file), progress, total));
     }
 
     BugreportMock br_;
+    std::string cwd_;  // TODO: make it static
 };
 
-// Tests when called with invalid number of argumnts
+// Tests when called with invalid number of arguments
 TEST_F(BugreportTest, InvalidNumberArgs) {
     const char* args[1024] = {"bugreport", "to", "principal"};
     ASSERT_EQ(-42, br_.DoIt(kTransportLocal, "HannibalLecter", 3, args));
 }
 
-// Tests the legacy 'adb bugreport' option
-TEST_F(BugreportTest, FlatFileFormat) {
+// Tests the 'adb bugreport' option when the device does not support 'bugreportz' - it falls back
+// to the flat-file format ('bugreport' binary on device)
+TEST_F(BugreportTest, NoArgumentsPreNDevice) {
+    ExpectBugreportzVersion("");
     EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreport", false, _))
         .WillOnce(Return(0));
 
@@ -151,15 +167,52 @@
     ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 1, args));
 }
 
-// Tests 'adb bugreport file.zip' when it succeeds and device does not support
-// progress.
-TEST_F(BugreportTest, OkLegacy) {
-    SetBugreportzVersion("1.0");
+// Tests the 'adb bugreport' option when the device supports 'bugreportz' version 1.0 - it will
+// save the bugreport in the current directory with the name provided by the device.
+TEST_F(BugreportTest, NoArgumentsNDevice) {
+    ExpectBugreportzVersion("1.0");
+
+    std::string dest_file =
+        android::base::StringPrintf("%s%cda_bugreport.zip", cwd_.c_str(), OS_PATH_SEPARATOR);
+    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _))
+        .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip")),
+                        WithArg<4>(ReturnCallbackDone())));
+    EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file),
+                                true, StrEq("generating da_bugreport.zip")))
+        .WillOnce(Return(true));
+
+    const char* args[1024] = {"bugreport"};
+    ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 1, args));
+}
+
+// Tests the 'adb bugreport' option when the device supports 'bugreportz' version 1.1 - it will
+// save the bugreport in the current directory with the name provided by the device.
+TEST_F(BugreportTest, NoArgumentsPostNDevice) {
+    ExpectBugreportzVersion("1.1");
+    std::string dest_file =
+        android::base::StringPrintf("%s%cda_bugreport.zip", cwd_.c_str(), OS_PATH_SEPARATOR);
+    ExpectProgress(50, 100, "da_bugreport.zip");
+    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
+        .WillOnce(DoAll(WithArg<4>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")),
+                        WithArg<4>(WriteOnStdout("PROGRESS:50/100\n")),
+                        WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip\n")),
+                        WithArg<4>(ReturnCallbackDone())));
+    EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file),
+                                true, StrEq("generating da_bugreport.zip")))
+        .WillOnce(Return(true));
+
+    const char* args[1024] = {"bugreport"};
+    ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 1, args));
+}
+
+// Tests 'adb bugreport file.zip' when it succeeds and device does not support progress.
+TEST_F(BugreportTest, OkNDevice) {
+    ExpectBugreportzVersion("1.0");
     EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _))
         .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip")),
                         WithArg<4>(ReturnCallbackDone())));
     EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
-                                true, HasSubstr("file.zip")))
+                                true, StrEq("generating file.zip")))
         .WillOnce(Return(true));
 
     const char* args[1024] = {"bugreport", "file.zip"};
@@ -168,14 +221,14 @@
 
 // Tests 'adb bugreport file.zip' when it succeeds but response was sent in
 // multiple buffer writers and without progress updates.
-TEST_F(BugreportTest, OkLegacySplitBuffer) {
-    SetBugreportzVersion("1.0");
+TEST_F(BugreportTest, OkNDeviceSplitBuffer) {
+    ExpectBugreportzVersion("1.0");
     EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _))
         .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device")),
                         WithArg<4>(WriteOnStdout("/bugreport.zip")),
                         WithArg<4>(ReturnCallbackDone())));
     EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
-                                true, HasSubstr("file.zip")))
+                                true, StrEq("generating file.zip")))
         .WillOnce(Return(true));
 
     const char* args[1024] = {"bugreport", "file.zip"};
@@ -183,16 +236,18 @@
 }
 
 // Tests 'adb bugreport file.zip' when it succeeds and displays progress.
-TEST_F(BugreportTest, Ok) {
-    SetBugreportzVersion("1.1");
+TEST_F(BugreportTest, OkProgress) {
+    ExpectBugreportzVersion("1.1");
     ExpectProgress(1, 100);
     ExpectProgress(10, 100);
     ExpectProgress(50, 100);
     ExpectProgress(99, 100);
     // clang-format off
     EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
-        // NOTE: DoAll accepts at most 10 arguments, and we have reached that limit...
+        // NOTE: DoAll accepts at most 10 arguments, and we're almost reached that limit...
         .WillOnce(DoAll(
+            // Name might change on OK, so make sure the right one is picked.
+            WithArg<4>(WriteOnStdout("BEGIN:/device/bugreport___NOT.zip\n")),
             // Progress line in one write
             WithArg<4>(WriteOnStdout("PROGRESS:1/100\n")),
             // Add some bogus lines
@@ -209,30 +264,68 @@
             WithArg<4>(ReturnCallbackDone())));
     // clang-format on
     EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
-                                true, HasSubstr("file.zip")))
+                                true, StrEq("generating file.zip")))
         .WillOnce(Return(true));
 
     const char* args[1024] = {"bugreport", "file.zip"};
     ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
 }
 
+// Tests 'adb bugreport dir' when it succeeds and destination is a directory.
+TEST_F(BugreportTest, OkDirectory) {
+    ExpectBugreportzVersion("1.1");
+    TemporaryDir td;
+    std::string dest_file =
+        android::base::StringPrintf("%s%cda_bugreport.zip", td.path, OS_PATH_SEPARATOR);
+
+    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
+        .WillOnce(DoAll(WithArg<4>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")),
+                        WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip")),
+                        WithArg<4>(ReturnCallbackDone())));
+    EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file),
+                                true, StrEq("generating da_bugreport.zip")))
+        .WillOnce(Return(true));
+
+    const char* args[1024] = {"bugreport", td.path};
+    ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+}
+
 // Tests 'adb bugreport file' when it succeeds
 TEST_F(BugreportTest, OkNoExtension) {
-    SetBugreportzVersion("1.1");
+    ExpectBugreportzVersion("1.1");
     EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
         .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip\n")),
                         WithArg<4>(ReturnCallbackDone())));
     EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
-                                true, HasSubstr("file.zip")))
+                                true, StrEq("generating file.zip")))
         .WillOnce(Return(true));
 
     const char* args[1024] = {"bugreport", "file"};
     ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
 }
 
+// Tests 'adb bugreport dir' when it succeeds and destination is a directory and device runs N.
+TEST_F(BugreportTest, OkNDeviceDirectory) {
+    ExpectBugreportzVersion("1.0");
+    TemporaryDir td;
+    std::string dest_file =
+        android::base::StringPrintf("%s%cda_bugreport.zip", td.path, OS_PATH_SEPARATOR);
+
+    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _))
+        .WillOnce(DoAll(WithArg<4>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")),
+                        WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip")),
+                        WithArg<4>(ReturnCallbackDone())));
+    EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file),
+                                true, StrEq("generating da_bugreport.zip")))
+        .WillOnce(Return(true));
+
+    const char* args[1024] = {"bugreport", td.path};
+    ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+}
+
 // Tests 'adb bugreport file.zip' when the bugreport itself failed
 TEST_F(BugreportTest, BugreportzReturnedFail) {
-    SetBugreportzVersion("1.1");
+    ExpectBugreportzVersion("1.1");
     EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
         .WillOnce(
             DoAll(WithArg<4>(WriteOnStdout("FAIL:D'OH!\n")), WithArg<4>(ReturnCallbackDone())));
@@ -247,7 +340,7 @@
 // was sent in
 // multiple buffer writes
 TEST_F(BugreportTest, BugreportzReturnedFailSplitBuffer) {
-    SetBugreportzVersion("1.1");
+    ExpectBugreportzVersion("1.1");
     EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
         .WillOnce(DoAll(WithArg<4>(WriteOnStdout("FAIL")), WithArg<4>(WriteOnStdout(":D'OH!\n")),
                         WithArg<4>(ReturnCallbackDone())));
@@ -261,7 +354,7 @@
 // Tests 'adb bugreport file.zip' when the bugreportz returned an unsupported
 // response.
 TEST_F(BugreportTest, BugreportzReturnedUnsupported) {
-    SetBugreportzVersion("1.1");
+    ExpectBugreportzVersion("1.1");
     EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
         .WillOnce(DoAll(WithArg<4>(WriteOnStdout("bugreportz? What am I, a zombie?")),
                         WithArg<4>(ReturnCallbackDone())));
@@ -283,7 +376,7 @@
 
 // Tests 'adb bugreport file.zip' when the bugreportz -v returns status 0 but with no output.
 TEST_F(BugreportTest, BugreportzVersionEmpty) {
-    SetBugreportzVersion("");
+    ExpectBugreportzVersion("");
 
     const char* args[1024] = {"bugreport", "file.zip"};
     ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
@@ -291,7 +384,7 @@
 
 // Tests 'adb bugreport file.zip' when the main bugreportz command failed
 TEST_F(BugreportTest, BugreportzFailed) {
-    SetBugreportzVersion("1.1");
+    ExpectBugreportzVersion("1.1");
     EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
         .WillOnce(Return(666));
 
@@ -301,7 +394,7 @@
 
 // Tests 'adb bugreport file.zip' when the bugreport could not be pulled
 TEST_F(BugreportTest, PullFails) {
-    SetBugreportzVersion("1.1");
+    ExpectBugreportzVersion("1.1");
     EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
         .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip")),
                         WithArg<4>(ReturnCallbackDone())));
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index 0685b70..23827de 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -81,6 +81,7 @@
 
 static void help() {
     fprintf(stderr, "%s\n", adb_version().c_str());
+    // clang-format off
     fprintf(stderr,
         " -a                            - directs adb to listen on all interfaces for a connection\n"
         " -d                            - directs command to the only connected USB device\n"
@@ -173,9 +174,11 @@
         "                                 (-g: grant all runtime permissions)\n"
         "  adb uninstall [-k] <package> - remove this app package from the device\n"
         "                                 ('-k' means keep the data and cache directories)\n"
-        "  adb bugreport [<zip_file>]   - return all information from the device\n"
-        "                                 that should be included in a bug report.\n"
-        "\n"
+        "  adb bugreport [<path>]       - return all information from the device that should be included in a zipped bug report.\n"
+        "                                 If <path> is a file, the bug report will be saved as that file.\n"
+        "                                 If <path> is a directory, the bug report will be saved in that directory with the name provided by the device.\n"
+        "                                 If <path> is omitted, the bug report will be saved in the current directory with the name provided by the device.\n"
+        "                                 NOTE: if the device does not support zipped bug reports, the bug report will be output on stdout.\n"
         "  adb backup [-f <file>] [-apk|-noapk] [-obb|-noobb] [-shared|-noshared] [-all] [-system|-nosystem] [<packages...>]\n"
         "                               - write an archive of the device's data to <file>.\n"
         "                                 If no -f option is supplied then the data is written\n"
@@ -249,8 +252,8 @@
         "  ADB_TRACE                    - Print debug information. A comma separated list of the following values\n"
         "                                 1 or all, adb, sockets, packets, rwx, usb, sync, sysdeps, transport, jdwp\n"
         "  ANDROID_SERIAL               - The serial number to connect to. -s takes priority over this if given.\n"
-        "  ANDROID_LOG_TAGS             - When used with the logcat option, only these debug tags are printed.\n"
-        );
+        "  ANDROID_LOG_TAGS             - When used with the logcat option, only these debug tags are printed.\n");
+    // clang-format on
 }
 
 int usage() {
@@ -1327,7 +1330,7 @@
     if (hint.find_first_of(OS_PATH_SEPARATORS) != std::string::npos) {
         std::string cwd;
         if (!getcwd(&cwd)) {
-            fprintf(stderr, "adb: getcwd failed: %s\n", strerror(errno));
+            perror("adb: getcwd failed");
             return "";
         }
         return android::base::StringPrintf("%s%c%s", cwd.c_str(), OS_PATH_SEPARATOR, hint.c_str());