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

Commit 307951e1 authored by Felipe Leme's avatar Felipe Leme
Browse files

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
parent 80a65d03
Loading
Loading
Loading
Loading
+96 −26
Original line number Diff line number Diff line
@@ -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 @@ class BugreportStandardStreamsCallback : public StandardStreamsCallbackInterface
                        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 @@ class BugreportStandardStreamsCallback : public StandardStreamsCallbackInterface
            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 @@ class BugreportStandardStreamsCallback : public StandardStreamsCallbackInterface
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 @@ int Bugreport::DoIt(TransportType transport_type, const char* serial, int argc,
    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 @@ int Bugreport::DoIt(TransportType transport_type, const char* serial, int argc,
        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 @@ int Bugreport::DoIt(TransportType transport_type, const char* serial, int argc,
        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);
}

+119 −26
Original line number Diff line number Diff line
@@ -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 BugreportMock : public Bugreport {

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 @@ TEST_F(BugreportTest, FlatFileFormat) {
    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 @@ TEST_F(BugreportTest, OkLegacy) {

// 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 @@ TEST_F(BugreportTest, OkLegacySplitBuffer) {
}

// 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 @@ TEST_F(BugreportTest, Ok) {
            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 @@ TEST_F(BugreportTest, BugreportzReturnedFail) {
// 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 @@ TEST_F(BugreportTest, BugreportzReturnedFailSplitBuffer) {
// 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 @@ TEST_F(BugreportTest, BugreportzVersionFailed) {

// 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 @@ TEST_F(BugreportTest, BugreportzVersionEmpty) {

// 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 @@ TEST_F(BugreportTest, BugreportzFailed) {

// 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())));
+9 −6
Original line number Diff line number Diff line
@@ -81,6 +81,7 @@ static std::string product_file(const char *extra) {

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 @@ static void help() {
        "                                 (-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 @@ static void help() {
        "  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 @@ static std::string find_product_out_path(const std::string& hint) {
    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());