Merge changes I0af0ad24,I942c92fd,Ibc8920dd,Icfce9c1e,I1c6cc616, ...
* changes: DO NOT MERGE: Split 'generating' and 'pulling' in 2 messages. DO NOT MERGE: Ignore bugreportz output when it's not supported. DO NOT MERGE: Deprecated 'adb bugreport' with flat files. DO NOT MERGE: Minor improvements on bugreport generation. DO NOT MERGE: Removed extra 'pulling file' message. DO NOT MERGE: Show bugreport progress. DO NOT MERGE: Refactored functions that copy sdout and stderr to strings to use a callback. DO NOT MERGE: Split bugreport() into its own file and added unit tests. DO NOT MERGE: use 'cmd' command for install-multiple DO NOT MERGE: Ensure the target sees a proper EOD marker during restore
This commit is contained in:
commit
5b136369ca
9 changed files with 972 additions and 135 deletions
|
|
@ -184,6 +184,10 @@ LOCAL_CFLAGS_linux := $(LIBADB_linux_CFLAGS)
|
|||
LOCAL_CFLAGS_darwin := $(LIBADB_darwin_CFLAGS)
|
||||
LOCAL_SRC_FILES := \
|
||||
$(LIBADB_TEST_SRCS) \
|
||||
adb_client.cpp \
|
||||
bugreport.cpp \
|
||||
bugreport_test.cpp \
|
||||
line_printer.cpp \
|
||||
services.cpp \
|
||||
shell_service_protocol.cpp \
|
||||
shell_service_protocol_test.cpp \
|
||||
|
|
@ -199,6 +203,7 @@ LOCAL_STATIC_LIBRARIES := \
|
|||
libcrypto \
|
||||
libcutils \
|
||||
libdiagnose_usb \
|
||||
libgmock_host \
|
||||
|
||||
# Set entrypoint to wmain from sysdeps_win32.cpp instead of main
|
||||
LOCAL_LDFLAGS_windows := -municode
|
||||
|
|
@ -227,6 +232,7 @@ LOCAL_REQUIRED_MODULES_windows := AdbWinApi AdbWinUsbApi
|
|||
|
||||
LOCAL_SRC_FILES := \
|
||||
adb_client.cpp \
|
||||
bugreport.cpp \
|
||||
client/main.cpp \
|
||||
console.cpp \
|
||||
commandline.cpp \
|
||||
|
|
|
|||
|
|
@ -203,8 +203,6 @@ void usb_kick(usb_handle *h);
|
|||
int is_adb_interface(int vid, int pid, int usb_class, int usb_subclass, int usb_protocol);
|
||||
#endif
|
||||
|
||||
int adb_commandline(int argc, const char **argv);
|
||||
|
||||
ConnectionState connection_state(atransport *t);
|
||||
|
||||
extern const char* adb_device_banner;
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
#define _ADB_CLIENT_H_
|
||||
|
||||
#include "adb.h"
|
||||
#include "sysdeps.h"
|
||||
#include "transport.h"
|
||||
|
||||
#include <string>
|
||||
|
|
|
|||
278
adb/bugreport.cpp
Normal file
278
adb/bugreport.cpp
Normal file
|
|
@ -0,0 +1,278 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#define TRACE_TAG ADB
|
||||
|
||||
#include "bugreport.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/strings.h>
|
||||
|
||||
#include "sysdeps.h"
|
||||
#include "adb_utils.h"
|
||||
#include "file_sync_service.h"
|
||||
|
||||
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_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_(),
|
||||
invalid_lines_(),
|
||||
show_progress_(show_progress),
|
||||
status_(0),
|
||||
line_() {
|
||||
SetLineMessage("generating");
|
||||
}
|
||||
|
||||
void OnStdout(const char* buffer, int length) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
char c = buffer[i];
|
||||
if (c == '\n') {
|
||||
ProcessLine(line_);
|
||||
line_.clear();
|
||||
} else {
|
||||
line_.append(1, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnStderr(const char* buffer, int length) {
|
||||
OnStream(nullptr, stderr, buffer, length);
|
||||
}
|
||||
int Done(int unused_) {
|
||||
// Process remaining line, if any.
|
||||
ProcessLine(line_);
|
||||
|
||||
// Warn about invalid lines, if any.
|
||||
if (!invalid_lines_.empty()) {
|
||||
fprintf(stderr,
|
||||
"WARNING: bugreportz generated %zu line(s) with unknown commands, "
|
||||
"device might not support zipped bugreports:\n",
|
||||
invalid_lines_.size());
|
||||
for (const auto& line : invalid_lines_) {
|
||||
fprintf(stderr, "\t%s\n", line.c_str());
|
||||
}
|
||||
fprintf(stderr,
|
||||
"If the zipped bugreport was not generated, try 'adb bugreport' instead.\n");
|
||||
}
|
||||
|
||||
// Pull the generated bug report.
|
||||
if (status_ == 0) {
|
||||
if (src_file_.empty()) {
|
||||
fprintf(stderr, "bugreportz did not return a '%s' or '%s' line\n", BUGZ_OK_PREFIX,
|
||||
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()};
|
||||
SetLineMessage("pulling");
|
||||
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",
|
||||
destination.c_str(), src_file_.c_str());
|
||||
}
|
||||
}
|
||||
return status_;
|
||||
}
|
||||
|
||||
private:
|
||||
void SetLineMessage(const std::string& action) {
|
||||
line_message_ = action + " " + adb_basename(dest_file_);
|
||||
}
|
||||
|
||||
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("generating");
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessLine(const std::string& line) {
|
||||
if (line.empty()) return;
|
||||
|
||||
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);
|
||||
status_ = -1;
|
||||
} else if (show_progress_ && android::base::StartsWith(line, BUGZ_PROGRESS_PREFIX)) {
|
||||
// progress_line should have the following format:
|
||||
//
|
||||
// BUGZ_PROGRESS_PREFIX:PROGRESS/TOTAL
|
||||
//
|
||||
size_t idx1 = line.rfind(BUGZ_PROGRESS_PREFIX) + strlen(BUGZ_PROGRESS_PREFIX);
|
||||
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(line_message_, progress, total);
|
||||
} else {
|
||||
invalid_lines_.push_back(line);
|
||||
}
|
||||
}
|
||||
|
||||
Bugreport* br_;
|
||||
|
||||
// Path of bugreport on device.
|
||||
std::string src_file_;
|
||||
|
||||
// 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).
|
||||
std::string line_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(BugreportStandardStreamsCallback);
|
||||
};
|
||||
|
||||
// Implemented in commandline.cpp
|
||||
int usage();
|
||||
|
||||
int Bugreport::DoIt(TransportType transport_type, const char* serial, int argc, const char** argv) {
|
||||
if (argc > 2) return usage();
|
||||
|
||||
// Gets bugreportz version.
|
||||
std::string bugz_stdout, bugz_stderr;
|
||||
DefaultStandardStreamsCallback version_callback(&bugz_stdout, &bugz_stderr);
|
||||
int status = SendShellCommand(transport_type, serial, "bugreportz -v", false, &version_callback);
|
||||
std::string bugz_version = android::base::Trim(bugz_stderr);
|
||||
std::string bugz_output = android::base::Trim(bugz_stdout);
|
||||
|
||||
if (status != 0 || bugz_version.empty()) {
|
||||
D("'bugreportz' -v results: status=%d, stdout='%s', stderr='%s'", status,
|
||||
bugz_output.c_str(), bugz_version.c_str());
|
||||
if (argc == 1) {
|
||||
// Device does not support bugreportz: if called as 'adb bugreport', just falls out to
|
||||
// the flat-file version.
|
||||
fprintf(stderr,
|
||||
"Failed to get bugreportz version, which is only available on devices "
|
||||
"running Android 7.0 or later.\nTrying a plain-text bug report instead.\n");
|
||||
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 does not run Android 7.0 or above, try 'adb bugreport' instead.\n",
|
||||
bugz_output.c_str(), status);
|
||||
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") {
|
||||
// 1.0 does not support progress notifications, so print a disclaimer
|
||||
// message instead.
|
||||
fprintf(stderr,
|
||||
"Bugreport is in progress and it could take minutes to complete.\n"
|
||||
"Please be patient and do not cancel or disconnect your device "
|
||||
"until it completes.\n");
|
||||
show_progress = false;
|
||||
bugz_command = "bugreportz";
|
||||
}
|
||||
BugreportStandardStreamsCallback bugz_callback(dest_dir, dest_file, show_progress, this);
|
||||
return SendShellCommand(transport_type, serial, bugz_command, false, &bugz_callback);
|
||||
}
|
||||
|
||||
void Bugreport::UpdateProgress(const std::string& message, int progress, int total) {
|
||||
int progress_percentage = (progress * 100 / total);
|
||||
line_printer_.Print(
|
||||
android::base::StringPrintf("[%3d%%] %s", progress_percentage, message.c_str()),
|
||||
LinePrinter::INFO);
|
||||
}
|
||||
|
||||
int Bugreport::SendShellCommand(TransportType transport_type, const char* serial,
|
||||
const std::string& command, bool disable_shell_protocol,
|
||||
StandardStreamsCallbackInterface* callback) {
|
||||
return send_shell_command(transport_type, serial, command, disable_shell_protocol, callback);
|
||||
}
|
||||
|
||||
bool Bugreport::DoSyncPull(const std::vector<const char*>& srcs, const char* dst, bool copy_attrs,
|
||||
const char* name) {
|
||||
return do_sync_pull(srcs, dst, copy_attrs, name);
|
||||
}
|
||||
51
adb/bugreport.h
Normal file
51
adb/bugreport.h
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef BUGREPORT_H
|
||||
#define BUGREPORT_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "adb.h"
|
||||
#include "commandline.h"
|
||||
#include "line_printer.h"
|
||||
|
||||
class Bugreport {
|
||||
friend class BugreportStandardStreamsCallback;
|
||||
|
||||
public:
|
||||
Bugreport() : line_printer_() {
|
||||
}
|
||||
int DoIt(TransportType transport_type, const char* serial, int argc, const char** argv);
|
||||
|
||||
protected:
|
||||
// Functions below are abstractions of external functions so they can be
|
||||
// mocked on tests.
|
||||
virtual int SendShellCommand(
|
||||
TransportType transport_type, const char* serial, const std::string& command,
|
||||
bool disable_shell_protocol,
|
||||
StandardStreamsCallbackInterface* callback = &DEFAULT_STANDARD_STREAMS_CALLBACK);
|
||||
|
||||
virtual bool DoSyncPull(const std::vector<const char*>& srcs, const char* dst, bool copy_attrs,
|
||||
const char* name);
|
||||
|
||||
private:
|
||||
virtual void UpdateProgress(const std::string& file_name, int progress, int total);
|
||||
LinePrinter line_printer_;
|
||||
DISALLOW_COPY_AND_ASSIGN(Bugreport);
|
||||
};
|
||||
|
||||
#endif // BUGREPORT_H
|
||||
418
adb/bugreport_test.cpp
Normal file
418
adb/bugreport_test.cpp
Normal file
|
|
@ -0,0 +1,418 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "bugreport.h"
|
||||
|
||||
#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;
|
||||
using ::testing::DoAll;
|
||||
using ::testing::ElementsAre;
|
||||
using ::testing::HasSubstr;
|
||||
using ::testing::MakeAction;
|
||||
using ::testing::Return;
|
||||
using ::testing::StrEq;
|
||||
using ::testing::WithArg;
|
||||
using ::testing::internal::CaptureStderr;
|
||||
using ::testing::internal::CaptureStdout;
|
||||
using ::testing::internal::GetCapturedStderr;
|
||||
using ::testing::internal::GetCapturedStdout;
|
||||
|
||||
// Empty function so tests don't need to be linked against file_sync_service.cpp, which requires
|
||||
// SELinux and its transitive dependencies...
|
||||
bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst, bool copy_attrs,
|
||||
const char* name) {
|
||||
ADD_FAILURE() << "do_sync_pull() should have been mocked";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Empty functions so tests don't need to be linked against commandline.cpp
|
||||
DefaultStandardStreamsCallback DEFAULT_STANDARD_STREAMS_CALLBACK(nullptr, nullptr);
|
||||
int usage() {
|
||||
return -42;
|
||||
}
|
||||
int send_shell_command(TransportType transport_type, const char* serial, const std::string& command,
|
||||
bool disable_shell_protocol, StandardStreamsCallbackInterface* callback) {
|
||||
ADD_FAILURE() << "send_shell_command() should have been mocked";
|
||||
return -42;
|
||||
}
|
||||
|
||||
enum StreamType {
|
||||
kStreamStdout,
|
||||
kStreamStderr,
|
||||
};
|
||||
|
||||
// gmock black magic to provide a WithArg<4>(WriteOnStdout(output)) matcher
|
||||
typedef void OnStandardStreamsCallbackFunction(StandardStreamsCallbackInterface*);
|
||||
|
||||
class OnStandardStreamsCallbackAction : public ActionInterface<OnStandardStreamsCallbackFunction> {
|
||||
public:
|
||||
explicit OnStandardStreamsCallbackAction(StreamType type, const std::string& output)
|
||||
: type_(type), output_(output) {
|
||||
}
|
||||
virtual Result Perform(const ArgumentTuple& args) {
|
||||
if (type_ == kStreamStdout) {
|
||||
::std::tr1::get<0>(args)->OnStdout(output_.c_str(), output_.size());
|
||||
}
|
||||
if (type_ == kStreamStderr) {
|
||||
::std::tr1::get<0>(args)->OnStderr(output_.c_str(), output_.size());
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
StreamType type_;
|
||||
std::string output_;
|
||||
};
|
||||
|
||||
// Matcher used to emulated StandardStreamsCallbackInterface.OnStdout(buffer,
|
||||
// length)
|
||||
Action<OnStandardStreamsCallbackFunction> WriteOnStdout(const std::string& output) {
|
||||
return MakeAction(new OnStandardStreamsCallbackAction(kStreamStdout, output));
|
||||
}
|
||||
|
||||
// Matcher used to emulated StandardStreamsCallbackInterface.OnStderr(buffer,
|
||||
// length)
|
||||
Action<OnStandardStreamsCallbackFunction> WriteOnStderr(const std::string& output) {
|
||||
return MakeAction(new OnStandardStreamsCallbackAction(kStreamStderr, output));
|
||||
}
|
||||
|
||||
typedef int CallbackDoneFunction(StandardStreamsCallbackInterface*);
|
||||
|
||||
class CallbackDoneAction : public ActionInterface<CallbackDoneFunction> {
|
||||
public:
|
||||
explicit CallbackDoneAction(int status) : status_(status) {
|
||||
}
|
||||
virtual Result Perform(const ArgumentTuple& args) {
|
||||
int status = ::std::tr1::get<0>(args)->Done(status_);
|
||||
return status;
|
||||
}
|
||||
|
||||
private:
|
||||
int status_;
|
||||
};
|
||||
|
||||
// Matcher used to emulated StandardStreamsCallbackInterface.Done(status)
|
||||
Action<CallbackDoneFunction> ReturnCallbackDone(int status = -1337) {
|
||||
return MakeAction(new CallbackDoneAction(status));
|
||||
}
|
||||
|
||||
class BugreportMock : public Bugreport {
|
||||
public:
|
||||
MOCK_METHOD5(SendShellCommand,
|
||||
int(TransportType transport_type, const char* serial, const std::string& command,
|
||||
bool disable_shell_protocol, StandardStreamsCallbackInterface* callback));
|
||||
MOCK_METHOD4(DoSyncPull, bool(const std::vector<const char*>& srcs, const char* dst,
|
||||
bool copy_attrs, const char* name));
|
||||
MOCK_METHOD3(UpdateProgress, void(const std::string&, int, int));
|
||||
};
|
||||
|
||||
class BugreportTest : public ::testing::Test {
|
||||
public:
|
||||
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, 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 arguments
|
||||
TEST_F(BugreportTest, InvalidNumberArgs) {
|
||||
const char* args[] = {"bugreport", "to", "principal"};
|
||||
ASSERT_EQ(-42, br_.DoIt(kTransportLocal, "HannibalLecter", 3, args));
|
||||
}
|
||||
|
||||
// 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) {
|
||||
// clang-format off
|
||||
EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -v", false, _))
|
||||
.WillOnce(DoAll(WithArg<4>(WriteOnStderr("")),
|
||||
// Write some bogus output on stdout to make sure it's ignored
|
||||
WithArg<4>(WriteOnStdout("Dude, where is my bugreportz?")),
|
||||
WithArg<4>(ReturnCallbackDone(0))));
|
||||
// clang-format on
|
||||
std::string bugreport = "Reported the bug was.";
|
||||
CaptureStdout();
|
||||
EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreport", false, _))
|
||||
.WillOnce(DoAll(WithArg<4>(WriteOnStdout(bugreport)), Return(0)));
|
||||
|
||||
const char* args[] = {"bugreport"};
|
||||
ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 1, args));
|
||||
ASSERT_THAT(GetCapturedStdout(), StrEq(bugreport));
|
||||
}
|
||||
|
||||
// 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("pulling da_bugreport.zip")))
|
||||
.WillOnce(Return(true));
|
||||
|
||||
const char* args[] = {"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("pulling da_bugreport.zip")))
|
||||
.WillOnce(Return(true));
|
||||
|
||||
const char* args[] = {"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, StrEq("pulling file.zip")))
|
||||
.WillOnce(Return(true));
|
||||
|
||||
const char* args[] = {"bugreport", "file.zip"};
|
||||
ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
|
||||
}
|
||||
|
||||
// Tests 'adb bugreport file.zip' when it succeeds but response was sent in
|
||||
// multiple buffer writers and without progress updates.
|
||||
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, StrEq("pulling file.zip")))
|
||||
.WillOnce(Return(true));
|
||||
|
||||
const char* args[] = {"bugreport", "file.zip"};
|
||||
ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
|
||||
}
|
||||
|
||||
// Tests 'adb bugreport file.zip' when it succeeds and displays progress.
|
||||
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'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
|
||||
WithArg<4>(WriteOnStdout("\nDUDE:SWEET\n\nBLA\n\nBLA\nBLA\n\n")),
|
||||
// Multiple progress lines in one write
|
||||
WithArg<4>(WriteOnStdout("PROGRESS:10/100\nPROGRESS:50/100\n")),
|
||||
// Progress line in multiple writes
|
||||
WithArg<4>(WriteOnStdout("PROG")),
|
||||
WithArg<4>(WriteOnStdout("RESS:99")),
|
||||
WithArg<4>(WriteOnStdout("/100\n")),
|
||||
// Split last message as well, just in case
|
||||
WithArg<4>(WriteOnStdout("OK:/device/bugreport")),
|
||||
WithArg<4>(WriteOnStdout(".zip")),
|
||||
WithArg<4>(ReturnCallbackDone())));
|
||||
// clang-format on
|
||||
EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
|
||||
true, StrEq("pulling file.zip")))
|
||||
.WillOnce(Return(true));
|
||||
|
||||
const char* args[] = {"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("pulling da_bugreport.zip")))
|
||||
.WillOnce(Return(true));
|
||||
|
||||
const char* args[] = {"bugreport", td.path};
|
||||
ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
|
||||
}
|
||||
|
||||
// Tests 'adb bugreport file' when it succeeds
|
||||
TEST_F(BugreportTest, OkNoExtension) {
|
||||
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, StrEq("pulling file.zip")))
|
||||
.WillOnce(Return(true));
|
||||
|
||||
const char* args[] = {"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("pulling da_bugreport.zip")))
|
||||
.WillOnce(Return(true));
|
||||
|
||||
const char* args[] = {"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) {
|
||||
ExpectBugreportzVersion("1.1");
|
||||
EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
|
||||
.WillOnce(
|
||||
DoAll(WithArg<4>(WriteOnStdout("FAIL:D'OH!\n")), WithArg<4>(ReturnCallbackDone())));
|
||||
|
||||
CaptureStderr();
|
||||
const char* args[] = {"bugreport", "file.zip"};
|
||||
ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
|
||||
ASSERT_THAT(GetCapturedStderr(), HasSubstr("D'OH!"));
|
||||
}
|
||||
|
||||
// Tests 'adb bugreport file.zip' when the bugreport itself failed but response
|
||||
// was sent in
|
||||
// multiple buffer writes
|
||||
TEST_F(BugreportTest, BugreportzReturnedFailSplitBuffer) {
|
||||
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())));
|
||||
|
||||
CaptureStderr();
|
||||
const char* args[] = {"bugreport", "file.zip"};
|
||||
ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
|
||||
ASSERT_THAT(GetCapturedStderr(), HasSubstr("D'OH!"));
|
||||
}
|
||||
|
||||
// Tests 'adb bugreport file.zip' when the bugreportz returned an unsupported
|
||||
// response.
|
||||
TEST_F(BugreportTest, BugreportzReturnedUnsupported) {
|
||||
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())));
|
||||
|
||||
CaptureStderr();
|
||||
const char* args[] = {"bugreport", "file.zip"};
|
||||
ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
|
||||
ASSERT_THAT(GetCapturedStderr(), HasSubstr("bugreportz? What am I, a zombie?"));
|
||||
}
|
||||
|
||||
// Tests 'adb bugreport file.zip' when the bugreportz -v command failed
|
||||
TEST_F(BugreportTest, BugreportzVersionFailed) {
|
||||
EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -v", false, _))
|
||||
.WillOnce(Return(666));
|
||||
|
||||
const char* args[] = {"bugreport", "file.zip"};
|
||||
ASSERT_EQ(666, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
|
||||
}
|
||||
|
||||
// Tests 'adb bugreport file.zip' when the bugreportz -v returns status 0 but with no output.
|
||||
TEST_F(BugreportTest, BugreportzVersionEmpty) {
|
||||
ExpectBugreportzVersion("");
|
||||
|
||||
const char* args[] = {"bugreport", "file.zip"};
|
||||
ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
|
||||
}
|
||||
|
||||
// Tests 'adb bugreport file.zip' when the main bugreportz command failed
|
||||
TEST_F(BugreportTest, BugreportzFailed) {
|
||||
ExpectBugreportzVersion("1.1");
|
||||
EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
|
||||
.WillOnce(Return(666));
|
||||
|
||||
const char* args[] = {"bugreport", "file.zip"};
|
||||
ASSERT_EQ(666, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
|
||||
}
|
||||
|
||||
// Tests 'adb bugreport file.zip' when the bugreport could not be pulled
|
||||
TEST_F(BugreportTest, PullFails) {
|
||||
ExpectBugreportzVersion("1.1");
|
||||
EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", 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")))
|
||||
.WillOnce(Return(false));
|
||||
|
||||
const char* args[] = {"bugreport", "file.zip"};
|
||||
ASSERT_EQ(1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
|
||||
}
|
||||
|
|
@ -32,6 +32,7 @@
|
|||
#include "adb_auth.h"
|
||||
#include "adb_listeners.h"
|
||||
#include "adb_utils.h"
|
||||
#include "commandline.h"
|
||||
#include "transport.h"
|
||||
|
||||
static std::string GetLogFilePath() {
|
||||
|
|
|
|||
|
|
@ -52,10 +52,11 @@
|
|||
#include "adb_client.h"
|
||||
#include "adb_io.h"
|
||||
#include "adb_utils.h"
|
||||
#include "bugreport.h"
|
||||
#include "commandline.h"
|
||||
#include "file_sync_service.h"
|
||||
#include "services.h"
|
||||
#include "shell_service.h"
|
||||
#include "transport.h"
|
||||
|
||||
static int install_app(TransportType t, const char* serial, int argc, const char** argv);
|
||||
static int install_multiple_app(TransportType t, const char* serial, int argc, const char** argv);
|
||||
|
|
@ -66,8 +67,7 @@ static int uninstall_app_legacy(TransportType t, const char* serial, int argc, c
|
|||
static auto& gProductOutPath = *new std::string();
|
||||
extern int gListenAll;
|
||||
|
||||
static constexpr char BUGZ_OK_PREFIX[] = "OK:";
|
||||
static constexpr char BUGZ_FAIL_PREFIX[] = "FAIL:";
|
||||
DefaultStandardStreamsCallback DEFAULT_STANDARD_STREAMS_CALLBACK(nullptr, nullptr);
|
||||
|
||||
static std::string product_file(const char *extra) {
|
||||
if (gProductOutPath.empty()) {
|
||||
|
|
@ -82,6 +82,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"
|
||||
|
|
@ -174,9 +175,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"
|
||||
|
|
@ -250,11 +253,11 @@ 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
|
||||
}
|
||||
|
||||
static int usage() {
|
||||
int usage() {
|
||||
help();
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -292,17 +295,14 @@ static void stdin_raw_restore() {
|
|||
// this expects that incoming data will use the shell protocol, in which case
|
||||
// stdout/stderr are routed independently and the remote exit code will be
|
||||
// returned.
|
||||
// if |output| is non-null, stdout will be appended to it instead.
|
||||
// if |err| is non-null, stderr will be appended to it instead.
|
||||
static int read_and_dump(int fd, bool use_shell_protocol=false, std::string* output=nullptr,
|
||||
std::string* err=nullptr) {
|
||||
// if |callback| is non-null, stdout/stderr output will be handled by it.
|
||||
int read_and_dump(int fd, bool use_shell_protocol = false,
|
||||
StandardStreamsCallbackInterface* callback = &DEFAULT_STANDARD_STREAMS_CALLBACK) {
|
||||
int exit_code = 0;
|
||||
if (fd < 0) return exit_code;
|
||||
|
||||
std::unique_ptr<ShellProtocol> protocol;
|
||||
int length = 0;
|
||||
FILE* outfile = stdout;
|
||||
std::string* outstring = output;
|
||||
|
||||
char raw_buffer[BUFSIZ];
|
||||
char* buffer_ptr = raw_buffer;
|
||||
|
|
@ -320,14 +320,13 @@ static int read_and_dump(int fd, bool use_shell_protocol=false, std::string* out
|
|||
if (!protocol->Read()) {
|
||||
break;
|
||||
}
|
||||
length = protocol->data_length();
|
||||
switch (protocol->id()) {
|
||||
case ShellProtocol::kIdStdout:
|
||||
outfile = stdout;
|
||||
outstring = output;
|
||||
callback->OnStdout(buffer_ptr, length);
|
||||
break;
|
||||
case ShellProtocol::kIdStderr:
|
||||
outfile = stderr;
|
||||
outstring = err;
|
||||
callback->OnStderr(buffer_ptr, length);
|
||||
break;
|
||||
case ShellProtocol::kIdExit:
|
||||
exit_code = protocol->data()[0];
|
||||
|
|
@ -343,17 +342,11 @@ static int read_and_dump(int fd, bool use_shell_protocol=false, std::string* out
|
|||
if (length <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (outstring == nullptr) {
|
||||
fwrite(buffer_ptr, 1, length, outfile);
|
||||
fflush(outfile);
|
||||
} else {
|
||||
outstring->append(buffer_ptr, length);
|
||||
callback->OnStdout(buffer_ptr, length);
|
||||
}
|
||||
}
|
||||
|
||||
return exit_code;
|
||||
return callback->Done(exit_code);
|
||||
}
|
||||
|
||||
static void read_status_line(int fd, char* buf, size_t count)
|
||||
|
|
@ -371,19 +364,7 @@ static void read_status_line(int fd, char* buf, size_t count)
|
|||
*buf = '\0';
|
||||
}
|
||||
|
||||
static void copy_to_file(int inFd, int outFd) {
|
||||
const size_t BUFSIZE = 32 * 1024;
|
||||
char* buf = (char*) malloc(BUFSIZE);
|
||||
if (buf == nullptr) fatal("couldn't allocate buffer for copy_to_file");
|
||||
int len;
|
||||
long total = 0;
|
||||
#ifdef _WIN32
|
||||
int old_stdin_mode = -1;
|
||||
int old_stdout_mode = -1;
|
||||
#endif
|
||||
|
||||
D("copy_to_file(%d -> %d)", inFd, outFd);
|
||||
|
||||
static void stdinout_raw_prologue(int inFd, int outFd, int& old_stdin_mode, int& old_stdout_mode) {
|
||||
if (inFd == STDIN_FILENO) {
|
||||
stdin_raw_init();
|
||||
#ifdef _WIN32
|
||||
|
|
@ -402,6 +383,39 @@ static void copy_to_file(int inFd, int outFd) {
|
|||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void stdinout_raw_epilogue(int inFd, int outFd, int old_stdin_mode, int old_stdout_mode) {
|
||||
if (inFd == STDIN_FILENO) {
|
||||
stdin_raw_restore();
|
||||
#ifdef _WIN32
|
||||
if (_setmode(STDIN_FILENO, old_stdin_mode) == -1) {
|
||||
fatal_errno("could not restore stdin mode");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
if (outFd == STDOUT_FILENO) {
|
||||
if (_setmode(STDOUT_FILENO, old_stdout_mode) == -1) {
|
||||
fatal_errno("could not restore stdout mode");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void copy_to_file(int inFd, int outFd) {
|
||||
const size_t BUFSIZE = 32 * 1024;
|
||||
char* buf = (char*) malloc(BUFSIZE);
|
||||
if (buf == nullptr) fatal("couldn't allocate buffer for copy_to_file");
|
||||
int len;
|
||||
long total = 0;
|
||||
int old_stdin_mode = -1;
|
||||
int old_stdout_mode = -1;
|
||||
|
||||
D("copy_to_file(%d -> %d)", inFd, outFd);
|
||||
|
||||
stdinout_raw_prologue(inFd, outFd, old_stdin_mode, old_stdout_mode);
|
||||
|
||||
while (true) {
|
||||
if (inFd == STDIN_FILENO) {
|
||||
|
|
@ -426,22 +440,7 @@ static void copy_to_file(int inFd, int outFd) {
|
|||
total += len;
|
||||
}
|
||||
|
||||
if (inFd == STDIN_FILENO) {
|
||||
stdin_raw_restore();
|
||||
#ifdef _WIN32
|
||||
if (_setmode(STDIN_FILENO, old_stdin_mode) == -1) {
|
||||
fatal_errno("could not restore stdin mode");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
if (outFd == STDOUT_FILENO) {
|
||||
if (_setmode(STDOUT_FILENO, old_stdout_mode) == -1) {
|
||||
fatal_errno("could not restore stdout mode");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
stdinout_raw_epilogue(inFd, outFd, old_stdin_mode, old_stdout_mode);
|
||||
|
||||
D("copy_to_file() finished after %lu bytes", total);
|
||||
free(buf);
|
||||
|
|
@ -1113,20 +1112,16 @@ static bool adb_root(const char* command) {
|
|||
return true;
|
||||
}
|
||||
|
||||
// Connects to the device "shell" service with |command| and prints the
|
||||
// resulting output.
|
||||
static int send_shell_command(TransportType transport_type, const char* serial,
|
||||
const std::string& command,
|
||||
bool disable_shell_protocol,
|
||||
std::string* output=nullptr,
|
||||
std::string* err=nullptr) {
|
||||
int send_shell_command(TransportType transport_type, const char* serial, const std::string& command,
|
||||
bool disable_shell_protocol, StandardStreamsCallbackInterface* callback) {
|
||||
int fd;
|
||||
bool use_shell_protocol = false;
|
||||
|
||||
while (true) {
|
||||
bool attempt_connection = true;
|
||||
|
||||
// Use shell protocol if it's supported and the caller doesn't explicitly disable it.
|
||||
// Use shell protocol if it's supported and the caller doesn't explicitly
|
||||
// disable it.
|
||||
if (!disable_shell_protocol) {
|
||||
FeatureSet features;
|
||||
std::string error;
|
||||
|
|
@ -1148,13 +1143,13 @@ static int send_shell_command(TransportType transport_type, const char* serial,
|
|||
}
|
||||
}
|
||||
|
||||
fprintf(stderr,"- waiting for device -\n");
|
||||
fprintf(stderr, "- waiting for device -\n");
|
||||
if (!wait_for_device("wait-for-device", transport_type, serial)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
int exit_code = read_and_dump(fd, use_shell_protocol, output, err);
|
||||
int exit_code = read_and_dump(fd, use_shell_protocol, callback);
|
||||
|
||||
if (adb_close(fd) < 0) {
|
||||
PLOG(ERROR) << "failure closing FD " << fd;
|
||||
|
|
@ -1163,45 +1158,6 @@ static int send_shell_command(TransportType transport_type, const char* serial,
|
|||
return exit_code;
|
||||
}
|
||||
|
||||
static int bugreport(TransportType transport_type, const char* serial, int argc,
|
||||
const char** argv) {
|
||||
if (argc == 1) return send_shell_command(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";
|
||||
}
|
||||
std::string output;
|
||||
|
||||
fprintf(stderr, "Bugreport is in progress and it could take minutes to complete.\n"
|
||||
"Please be patient and do not cancel or disconnect your device until it completes.\n");
|
||||
int status = send_shell_command(transport_type, serial, "bugreportz", false, &output, nullptr);
|
||||
if (status != 0 || output.empty()) return status;
|
||||
output = android::base::Trim(output);
|
||||
|
||||
if (android::base::StartsWith(output, BUGZ_OK_PREFIX)) {
|
||||
const char* zip_file = &output[strlen(BUGZ_OK_PREFIX)];
|
||||
std::vector<const char*> srcs{zip_file};
|
||||
status = do_sync_pull(srcs, dest_file.c_str(), true, dest_file.c_str()) ? 0 : 1;
|
||||
if (status != 0) {
|
||||
fprintf(stderr, "Could not copy file '%s' to '%s'\n", zip_file, dest_file.c_str());
|
||||
}
|
||||
return status;
|
||||
}
|
||||
if (android::base::StartsWith(output, BUGZ_FAIL_PREFIX)) {
|
||||
const char* error_message = &output[strlen(BUGZ_FAIL_PREFIX)];
|
||||
fprintf(stderr, "Device failed to take a zipped bugreport: %s\n", error_message);
|
||||
return -1;
|
||||
}
|
||||
fprintf(stderr, "Unexpected string (%s) returned by bugreportz, "
|
||||
"device probably does not support -z option\n", output.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int logcat(TransportType transport, const char* serial, int argc, const char** argv) {
|
||||
char* log_tags = getenv("ANDROID_LOG_TAGS");
|
||||
std::string quoted = escape_arg(log_tags == nullptr ? "" : log_tags);
|
||||
|
|
@ -1222,6 +1178,29 @@ static int logcat(TransportType transport, const char* serial, int argc, const c
|
|||
return send_shell_command(transport, serial, cmd, true);
|
||||
}
|
||||
|
||||
static void write_zeros(int bytes, int fd) {
|
||||
int old_stdin_mode = -1;
|
||||
int old_stdout_mode = -1;
|
||||
char* buf = (char*) calloc(1, bytes);
|
||||
if (buf == nullptr) fatal("couldn't allocate buffer for write_zeros");
|
||||
|
||||
D("write_zeros(%d) -> %d", bytes, fd);
|
||||
|
||||
stdinout_raw_prologue(-1, fd, old_stdin_mode, old_stdout_mode);
|
||||
|
||||
if (fd == STDOUT_FILENO) {
|
||||
fwrite(buf, 1, bytes, stdout);
|
||||
fflush(stdout);
|
||||
} else {
|
||||
adb_write(fd, buf, bytes);
|
||||
}
|
||||
|
||||
stdinout_raw_prologue(-1, fd, old_stdin_mode, old_stdout_mode);
|
||||
|
||||
D("write_zeros() finished");
|
||||
free(buf);
|
||||
}
|
||||
|
||||
static int backup(int argc, const char** argv) {
|
||||
const char* filename = "backup.ab";
|
||||
|
||||
|
|
@ -1302,6 +1281,9 @@ static int restore(int argc, const char** argv) {
|
|||
printf("Now unlock your device and confirm the restore operation.\n");
|
||||
copy_to_file(tarFd, fd);
|
||||
|
||||
// Provide an in-band EOD marker in case the archive file is malformed
|
||||
write_zeros(512*2, fd);
|
||||
|
||||
// Wait until the other side finishes, or it'll get sent SIGHUP.
|
||||
copy_to_file(fd, STDOUT_FILENO);
|
||||
|
||||
|
|
@ -1337,7 +1319,7 @@ static std::string find_product_out_path(const std::string& hint) {
|
|||
if (hint.find_first_of(OS_PATH_SEPARATORS) != std::string::npos) { // NOLINT
|
||||
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());
|
||||
|
|
@ -1437,6 +1419,16 @@ static bool _is_valid_ack_reply_fd(const int ack_reply_fd) {
|
|||
#endif
|
||||
}
|
||||
|
||||
static bool _use_legacy_install() {
|
||||
FeatureSet features;
|
||||
std::string error;
|
||||
if (!adb_get_feature_set(&features, &error)) {
|
||||
fprintf(stderr, "error: %s\n", error.c_str());
|
||||
return true;
|
||||
}
|
||||
return !CanUseFeature(features, kFeatureCmd);
|
||||
}
|
||||
|
||||
int adb_commandline(int argc, const char **argv) {
|
||||
int no_daemon = 0;
|
||||
int is_daemon = 0;
|
||||
|
|
@ -1737,7 +1729,8 @@ int adb_commandline(int argc, const char **argv) {
|
|||
} else if (!strcmp(argv[0], "root") || !strcmp(argv[0], "unroot")) {
|
||||
return adb_root(argv[0]) ? 0 : 1;
|
||||
} else if (!strcmp(argv[0], "bugreport")) {
|
||||
return bugreport(transport_type, serial, argc, argv);
|
||||
Bugreport bugreport;
|
||||
return bugreport.DoIt(transport_type, serial, argc, argv);
|
||||
} else if (!strcmp(argv[0], "forward") || !strcmp(argv[0], "reverse")) {
|
||||
bool reverse = !strcmp(argv[0], "reverse");
|
||||
++argv;
|
||||
|
|
@ -1831,17 +1824,10 @@ int adb_commandline(int argc, const char **argv) {
|
|||
}
|
||||
else if (!strcmp(argv[0], "install")) {
|
||||
if (argc < 2) return usage();
|
||||
FeatureSet features;
|
||||
std::string error;
|
||||
if (!adb_get_feature_set(&features, &error)) {
|
||||
fprintf(stderr, "error: %s\n", error.c_str());
|
||||
return 1;
|
||||
if (_use_legacy_install()) {
|
||||
return install_app_legacy(transport_type, serial, argc, argv);
|
||||
}
|
||||
|
||||
if (CanUseFeature(features, kFeatureCmd)) {
|
||||
return install_app(transport_type, serial, argc, argv);
|
||||
}
|
||||
return install_app_legacy(transport_type, serial, argc, argv);
|
||||
return install_app(transport_type, serial, argc, argv);
|
||||
}
|
||||
else if (!strcmp(argv[0], "install-multiple")) {
|
||||
if (argc < 2) return usage();
|
||||
|
|
@ -1849,17 +1835,10 @@ int adb_commandline(int argc, const char **argv) {
|
|||
}
|
||||
else if (!strcmp(argv[0], "uninstall")) {
|
||||
if (argc < 2) return usage();
|
||||
FeatureSet features;
|
||||
std::string error;
|
||||
if (!adb_get_feature_set(&features, &error)) {
|
||||
fprintf(stderr, "error: %s\n", error.c_str());
|
||||
return 1;
|
||||
if (_use_legacy_install()) {
|
||||
return uninstall_app_legacy(transport_type, serial, argc, argv);
|
||||
}
|
||||
|
||||
if (CanUseFeature(features, kFeatureCmd)) {
|
||||
return uninstall_app(transport_type, serial, argc, argv);
|
||||
}
|
||||
return uninstall_app_legacy(transport_type, serial, argc, argv);
|
||||
return uninstall_app(transport_type, serial, argc, argv);
|
||||
}
|
||||
else if (!strcmp(argv[0], "sync")) {
|
||||
std::string src;
|
||||
|
|
@ -2073,7 +2052,6 @@ static int install_multiple_app(TransportType transport, const char* serial, int
|
|||
int i;
|
||||
struct stat sb;
|
||||
uint64_t total_size = 0;
|
||||
|
||||
// Find all APK arguments starting at end.
|
||||
// All other arguments passed through verbatim.
|
||||
int first_apk = -1;
|
||||
|
|
@ -2098,7 +2076,14 @@ static int install_multiple_app(TransportType transport, const char* serial, int
|
|||
return 1;
|
||||
}
|
||||
|
||||
std::string cmd = android::base::StringPrintf("exec:pm install-create -S %" PRIu64, total_size);
|
||||
std::string install_cmd;
|
||||
if (_use_legacy_install()) {
|
||||
install_cmd = "exec:pm";
|
||||
} else {
|
||||
install_cmd = "exec:cmd package";
|
||||
}
|
||||
|
||||
std::string cmd = android::base::StringPrintf("%s install-create -S %" PRIu64, install_cmd.c_str(), total_size);
|
||||
for (i = 1; i < first_apk; i++) {
|
||||
cmd += " " + escape_arg(argv[i]);
|
||||
}
|
||||
|
|
@ -2140,8 +2125,8 @@ static int install_multiple_app(TransportType transport, const char* serial, int
|
|||
}
|
||||
|
||||
std::string cmd = android::base::StringPrintf(
|
||||
"exec:pm install-write -S %" PRIu64 " %d %d_%s -",
|
||||
static_cast<uint64_t>(sb.st_size), session_id, i, adb_basename(file).c_str());
|
||||
"%s install-write -S %" PRIu64 " %d %d_%s -",
|
||||
install_cmd.c_str(), static_cast<uint64_t>(sb.st_size), session_id, i, adb_basename(file).c_str());
|
||||
|
||||
int localFd = adb_open(file, O_RDONLY);
|
||||
if (localFd < 0) {
|
||||
|
|
@ -2176,8 +2161,8 @@ static int install_multiple_app(TransportType transport, const char* serial, int
|
|||
finalize_session:
|
||||
// Commit session if we streamed everything okay; otherwise abandon
|
||||
std::string service =
|
||||
android::base::StringPrintf("exec:pm install-%s %d",
|
||||
success ? "commit" : "abandon", session_id);
|
||||
android::base::StringPrintf("%s install-%s %d",
|
||||
install_cmd.c_str(), success ? "commit" : "abandon", session_id);
|
||||
fd = adb_connect(service, &error);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "Connect error for finalize: %s\n", error.c_str());
|
||||
|
|
|
|||
99
adb/commandline.h
Normal file
99
adb/commandline.h
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef COMMANDLINE_H
|
||||
#define COMMANDLINE_H
|
||||
|
||||
#include "adb.h"
|
||||
|
||||
// Callback used to handle the standard streams (stdout and stderr) sent by the
|
||||
// device's upon receiving a command.
|
||||
//
|
||||
class StandardStreamsCallbackInterface {
|
||||
public:
|
||||
StandardStreamsCallbackInterface() {
|
||||
}
|
||||
// Handles the stdout output from devices supporting the Shell protocol.
|
||||
virtual void OnStdout(const char* buffer, int length) = 0;
|
||||
|
||||
// Handles the stderr output from devices supporting the Shell protocol.
|
||||
virtual void OnStderr(const char* buffer, int length) = 0;
|
||||
|
||||
// Indicates the communication is finished and returns the appropriate error
|
||||
// code.
|
||||
//
|
||||
// |status| has the status code returning by the underlying communication
|
||||
// channels
|
||||
virtual int Done(int status) = 0;
|
||||
|
||||
protected:
|
||||
static void OnStream(std::string* string, FILE* stream, const char* buffer, int length) {
|
||||
if (string != nullptr) {
|
||||
string->append(buffer, length);
|
||||
} else {
|
||||
fwrite(buffer, 1, length, stream);
|
||||
fflush(stream);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(StandardStreamsCallbackInterface);
|
||||
};
|
||||
|
||||
// Default implementation that redirects the streams to the equilavent host
|
||||
// stream or to a string
|
||||
// passed to the constructor.
|
||||
class DefaultStandardStreamsCallback : public StandardStreamsCallbackInterface {
|
||||
public:
|
||||
// If |stdout_str| is non-null, OnStdout will append to it.
|
||||
// If |stderr_str| is non-null, OnStderr will append to it.
|
||||
DefaultStandardStreamsCallback(std::string* stdout_str, std::string* stderr_str)
|
||||
: stdout_str_(stdout_str), stderr_str_(stderr_str) {
|
||||
}
|
||||
|
||||
void OnStdout(const char* buffer, int length) {
|
||||
OnStream(stdout_str_, stdout, buffer, length);
|
||||
}
|
||||
|
||||
void OnStderr(const char* buffer, int length) {
|
||||
OnStream(stderr_str_, stderr, buffer, length);
|
||||
}
|
||||
|
||||
int Done(int status) {
|
||||
return status;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string* stdout_str_;
|
||||
std::string* stderr_str_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(DefaultStandardStreamsCallback);
|
||||
};
|
||||
|
||||
// Singleton.
|
||||
extern DefaultStandardStreamsCallback DEFAULT_STANDARD_STREAMS_CALLBACK;
|
||||
|
||||
int adb_commandline(int argc, const char** argv);
|
||||
int usage();
|
||||
|
||||
// Connects to the device "shell" service with |command| and prints the
|
||||
// resulting output.
|
||||
// if |callback| is non-null, stdout/stderr output will be handled by it.
|
||||
int send_shell_command(TransportType transport_type, const char* serial, const std::string& command,
|
||||
bool disable_shell_protocol, StandardStreamsCallbackInterface* callback =
|
||||
&DEFAULT_STANDARD_STREAMS_CALLBACK);
|
||||
|
||||
#endif // COMMANDLINE_H
|
||||
Loading…
Add table
Reference in a new issue