Merge "use 'cmd' command for install-multiple" into nyc-mr1-dev
diff --git a/adb/Android.mk b/adb/Android.mk
index f1d3bee..8f56d74 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -62,6 +62,7 @@
fdevent_test.cpp \
socket_test.cpp \
sysdeps_test.cpp \
+ sysdeps/stat_test.cpp \
transport_test.cpp \
LIBADB_CFLAGS := \
@@ -89,6 +90,7 @@
LIBADB_windows_SRC_FILES := \
sysdeps_win32.cpp \
+ sysdeps/win32/stat.cpp \
usb_windows.cpp \
LIBADB_TEST_windows_SRCS := \
@@ -180,6 +182,10 @@
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 \
@@ -194,6 +200,7 @@
libcrypto_static \
libcutils \
libdiagnose_usb \
+ libgmock_host \
# Set entrypoint to wmain from sysdeps_win32.cpp instead of main
LOCAL_LDFLAGS_windows := -municode
@@ -240,6 +247,7 @@
LOCAL_SRC_FILES := \
adb_client.cpp \
+ bugreport.cpp \
client/main.cpp \
console.cpp \
commandline.cpp \
diff --git a/adb/adb.h b/adb/adb.h
index ea20800..971b8da 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -226,8 +226,6 @@
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;
diff --git a/adb/adb_client.h b/adb/adb_client.h
index d5cd922..9f9eb1f 100644
--- a/adb/adb_client.h
+++ b/adb/adb_client.h
@@ -18,6 +18,7 @@
#define _ADB_CLIENT_H_
#include "adb.h"
+#include "sysdeps.h"
#include "transport.h"
#include <string>
diff --git a/adb/adb_utils_test.cpp b/adb/adb_utils_test.cpp
index f1ebaa1..a78f8d3 100644
--- a/adb/adb_utils_test.cpp
+++ b/adb/adb_utils_test.cpp
@@ -52,18 +52,6 @@
ASSERT_TRUE(directory_exists(profiles_dir));
- // On modern (English?) Windows, this is a directory symbolic link to
- // C:\ProgramData. Symbolic links are rare on Windows and the user requires
- // a special permission (by default granted to Administrative users) to
- // create symbolic links.
- ASSERT_FALSE(directory_exists(subdir(profiles_dir, "All Users")));
-
- // On modern (English?) Windows, this is a directory junction to
- // C:\Users\Default. Junctions are used throughout user profile directories
- // for backwards compatibility and they don't require any special permissions
- // to create.
- ASSERT_FALSE(directory_exists(subdir(profiles_dir, "Default User")));
-
ASSERT_FALSE(directory_exists(subdir(profiles_dir, "does-not-exist")));
#else
ASSERT_TRUE(directory_exists("/proc"));
@@ -72,6 +60,28 @@
#endif
}
+#if defined(_WIN32)
+TEST(adb_utils, directory_exists_win32_symlink_junction) {
+ char profiles_dir[MAX_PATH];
+ DWORD cch = arraysize(profiles_dir);
+
+ // On typical Windows 7, returns C:\Users
+ ASSERT_TRUE(GetProfilesDirectoryA(profiles_dir, &cch));
+
+ // On modern (English?) Windows, this is a directory symbolic link to
+ // C:\ProgramData. Symbolic links are rare on Windows and the user requires
+ // a special permission (by default granted to Administrative users) to
+ // create symbolic links.
+ EXPECT_FALSE(directory_exists(subdir(profiles_dir, "All Users")));
+
+ // On modern (English?) Windows, this is a directory junction to
+ // C:\Users\Default. Junctions are used throughout user profile directories
+ // for backwards compatibility and they don't require any special permissions
+ // to create.
+ EXPECT_FALSE(directory_exists(subdir(profiles_dir, "Default User")));
+}
+#endif
+
TEST(adb_utils, escape_arg) {
ASSERT_EQ(R"('')", escape_arg(""));
@@ -113,14 +123,20 @@
void test_mkdirs(const std::string basepath) {
// Test creating a directory hierarchy.
- EXPECT_TRUE(mkdirs(basepath));
+ ASSERT_TRUE(mkdirs(basepath));
// Test finding an existing directory hierarchy.
- EXPECT_TRUE(mkdirs(basepath));
+ ASSERT_TRUE(mkdirs(basepath));
+ // Test mkdirs on an existing hierarchy with a trailing slash.
+ ASSERT_TRUE(mkdirs(basepath + '/'));
+#if defined(_WIN32)
+ ASSERT_TRUE(mkdirs(basepath + '\\'));
+#endif
+
const std::string filepath = basepath + "/file";
// Verify that the hierarchy was created by trying to create a file in it.
- EXPECT_NE(-1, adb_creat(filepath.c_str(), 0600));
+ ASSERT_NE(-1, adb_creat(filepath.c_str(), 0600));
// If a file exists where we want a directory, the operation should fail.
- EXPECT_FALSE(mkdirs(filepath));
+ ASSERT_FALSE(mkdirs(filepath));
}
TEST(adb_utils, mkdirs) {
diff --git a/adb/bugreport.cpp b/adb/bugreport.cpp
new file mode 100644
index 0000000..c348dd5
--- /dev/null
+++ b/adb/bugreport.cpp
@@ -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);
+}
diff --git a/adb/bugreport.h b/adb/bugreport.h
new file mode 100644
index 0000000..ee99cbc
--- /dev/null
+++ b/adb/bugreport.h
@@ -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
diff --git a/adb/bugreport_test.cpp b/adb/bugreport_test.cpp
new file mode 100644
index 0000000..1129285
--- /dev/null
+++ b/adb/bugreport_test.cpp
@@ -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));
+}
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index b0722ef..ba4737f 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -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() {
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index b07cb63..51d828a 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -51,10 +51,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);
@@ -65,8 +66,7 @@
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()) {
@@ -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,11 +252,11 @@
" 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;
}
@@ -291,17 +294,14 @@
// 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;
@@ -319,14 +319,13 @@
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];
@@ -342,17 +341,11 @@
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)
@@ -1131,20 +1124,16 @@
return wait_for_device("wait-for-any", type, serial);
}
-// 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;
@@ -1166,13 +1155,13 @@
}
}
- 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;
@@ -1181,45 +1170,6 @@
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);
@@ -1380,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());
@@ -1785,7 +1735,8 @@
} 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;
diff --git a/adb/commandline.h b/adb/commandline.h
new file mode 100644
index 0000000..0cf655c
--- /dev/null
+++ b/adb/commandline.h
@@ -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
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 75dcc86..bd19f88 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -34,6 +34,8 @@
#include <android-base/unique_fd.h>
#include <android-base/utf8.h>
+#include "sysdeps/stat.h"
+
/*
* TEMP_FAILURE_RETRY is defined by some, but not all, versions of
* <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's
@@ -199,8 +201,6 @@
/* nothing really */
}
-#define lstat stat /* no symlinks on Win32 */
-
#define S_ISLNK(m) 0 /* no symlinks on Win32 */
extern int adb_unlink(const char* path);
@@ -307,27 +307,6 @@
return isalpha(path[0]) && path[1] == ':' && path[2] == '\\';
}
-// We later define a macro mapping 'stat' to 'adb_stat'. This causes:
-// struct stat s;
-// stat(filename, &s);
-// To turn into the following:
-// struct adb_stat s;
-// adb_stat(filename, &s);
-// To get this to work, we need to make 'struct adb_stat' the same as
-// 'struct stat'. Note that this definition of 'struct adb_stat' uses the
-// *current* macro definition of stat, so it may actually be inheriting from
-// struct _stat32i64 (or some other remapping).
-struct adb_stat : public stat {};
-
-static_assert(sizeof(struct adb_stat) == sizeof(struct stat),
- "structures should be the same");
-
-extern int adb_stat(const char* f, struct adb_stat* s);
-
-// stat is already a macro, undefine it so we can redefine it.
-#undef stat
-#define stat adb_stat
-
// UTF-8 versions of POSIX APIs.
extern DIR* adb_opendir(const char* dirname);
extern struct dirent* adb_readdir(DIR* dir);
diff --git a/adb/sysdeps/stat.h b/adb/sysdeps/stat.h
new file mode 100644
index 0000000..5953595
--- /dev/null
+++ b/adb/sysdeps/stat.h
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#if defined(_WIN32)
+// stat is broken on Win32: stat on a path with a trailing slash or backslash will always fail with
+// ENOENT.
+int adb_stat(const char* path, struct adb_stat* buf);
+
+// We later define a macro mapping 'stat' to 'adb_stat'. This causes:
+// struct stat s;
+// stat(filename, &s);
+// To turn into the following:
+// struct adb_stat s;
+// adb_stat(filename, &s);
+// To get this to work, we need to make 'struct adb_stat' the same as
+// 'struct stat'. Note that this definition of 'struct adb_stat' uses the
+// *current* macro definition of stat, so it may actually be inheriting from
+// struct _stat32i64 (or some other remapping).
+struct adb_stat : public stat {};
+
+#undef stat
+#define stat adb_stat
+
+// Windows doesn't have lstat.
+#define lstat adb_stat
+
+#endif
diff --git a/adb/sysdeps/stat_test.cpp b/adb/sysdeps/stat_test.cpp
new file mode 100644
index 0000000..2c2e0ee
--- /dev/null
+++ b/adb/sysdeps/stat_test.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2015 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 <string>
+
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include "adb_utils.h"
+#include "sysdeps.h"
+
+TEST(sysdeps, stat) {
+ TemporaryDir td;
+ TemporaryFile tf;
+
+ struct stat st;
+ ASSERT_EQ(0, stat(td.path, &st));
+ ASSERT_FALSE(S_ISREG(st.st_mode));
+ ASSERT_TRUE(S_ISDIR(st.st_mode));
+
+ ASSERT_EQ(0, stat((std::string(td.path) + '/').c_str(), &st));
+ ASSERT_TRUE(S_ISDIR(st.st_mode));
+
+#if defined(_WIN32)
+ ASSERT_EQ(0, stat((std::string(td.path) + '\\').c_str(), &st));
+ ASSERT_TRUE(S_ISDIR(st.st_mode));
+#endif
+
+ std::string nonexistent_path = std::string(td.path) + "/nonexistent";
+ ASSERT_EQ(-1, stat(nonexistent_path.c_str(), &st));
+ ASSERT_EQ(ENOENT, errno);
+
+ ASSERT_EQ(-1, stat((nonexistent_path + "/").c_str(), &st));
+ ASSERT_EQ(ENOENT, errno);
+
+#if defined(_WIN32)
+ ASSERT_EQ(-1, stat((nonexistent_path + "\\").c_str(), &st));
+ ASSERT_EQ(ENOENT, errno);
+#endif
+
+ ASSERT_EQ(0, stat(tf.path, &st));
+ ASSERT_TRUE(S_ISREG(st.st_mode));
+ ASSERT_FALSE(S_ISDIR(st.st_mode));
+
+ ASSERT_EQ(-1, stat((std::string(tf.path) + '/').c_str(), &st));
+ ASSERT_EQ(ENOTDIR, errno);
+
+#if defined(_WIN32)
+ ASSERT_EQ(-1, stat((std::string(tf.path) + '\\').c_str(), &st));
+ ASSERT_EQ(ENOTDIR, errno);
+#endif
+}
diff --git a/adb/sysdeps/win32/stat.cpp b/adb/sysdeps/win32/stat.cpp
new file mode 100644
index 0000000..844c1ce
--- /dev/null
+++ b/adb/sysdeps/win32/stat.cpp
@@ -0,0 +1,66 @@
+/*
+ * 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 "sysdeps/stat.h"
+
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/utf8.h>
+
+// Version of stat() that takes a UTF-8 path.
+int adb_stat(const char* path, struct adb_stat* s) {
+// This definition of wstat seems to be missing from <sys/stat.h>.
+#if defined(_FILE_OFFSET_BITS) && (_FILE_OFFSET_BITS == 64)
+#ifdef _USE_32BIT_TIME_T
+#define wstat _wstat32i64
+#else
+#define wstat _wstat64
+#endif
+#else
+// <sys/stat.h> has a function prototype for wstat() that should be available.
+#endif
+
+ std::wstring path_wide;
+ if (!android::base::UTF8ToWide(path, &path_wide)) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ // If the path has a trailing slash, stat will fail with ENOENT regardless of whether the path
+ // is a directory or not.
+ bool expected_directory = false;
+ while (*path_wide.rbegin() == u'/' || *path_wide.rbegin() == u'\\') {
+ path_wide.pop_back();
+ expected_directory = true;
+ }
+
+ struct adb_stat st;
+ int result = wstat(path_wide.c_str(), &st);
+ if (result == 0 && expected_directory) {
+ if (!S_ISDIR(st.st_mode)) {
+ errno = ENOTDIR;
+ return -1;
+ }
+ }
+
+ memcpy(s, &st, sizeof(st));
+ return result;
+}
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index bc09fdc..e641680 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -2298,30 +2298,6 @@
}
}
-// Version of stat() that takes a UTF-8 path.
-int adb_stat(const char* path, struct adb_stat* s) {
-#pragma push_macro("wstat")
-// This definition of wstat seems to be missing from <sys/stat.h>.
-#if defined(_FILE_OFFSET_BITS) && (_FILE_OFFSET_BITS == 64)
-#ifdef _USE_32BIT_TIME_T
-#define wstat _wstat32i64
-#else
-#define wstat _wstat64
-#endif
-#else
-// <sys/stat.h> has a function prototype for wstat() that should be available.
-#endif
-
- std::wstring path_wide;
- if (!android::base::UTF8ToWide(path, &path_wide)) {
- return -1;
- }
-
- return wstat(path_wide.c_str(), s);
-
-#pragma pop_macro("wstat")
-}
-
// Version of opendir() that takes a UTF-8 path.
DIR* adb_opendir(const char* path) {
std::wstring path_wide;
diff --git a/bootstat/boot_event_record_store.cpp b/bootstat/boot_event_record_store.cpp
index ef4f68e..346eada 100644
--- a/bootstat/boot_event_record_store.cpp
+++ b/bootstat/boot_event_record_store.cpp
@@ -25,6 +25,7 @@
#include <utility>
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/parseint.h>
#include "histogram_logger.h"
#include "uptime_parser.h"
@@ -57,8 +58,10 @@
// Ignore existing bootstat records (which do not contain file content).
if (!content.empty()) {
- int32_t value = std::stoi(content);
- bootstat::LogHistogram("bootstat_mtime_matches_content", value == *uptime);
+ int32_t value;
+ if (android::base::ParseInt(content.c_str(), &value)) {
+ bootstat::LogHistogram("bootstat_mtime_matches_content", value == *uptime);
+ }
}
return true;
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 08fc5af..d547c19 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -28,6 +28,7 @@
#include <memory>
#include <string>
#include <android-base/logging.h>
+#include <android-base/parseint.h>
#include <cutils/properties.h>
#include <log/log.h>
#include "boot_event_record_store.h"
@@ -147,7 +148,10 @@
std::string boot_complete_prefix = "boot_complete";
std::string build_date_str = GetProperty("ro.build.date.utc");
- int32_t build_date = std::stoi(build_date_str);
+ int32_t build_date;
+ if (!android::base::ParseInt(build_date_str.c_str(), &build_date)) {
+ return std::string();
+ }
BootEventRecordStore boot_event_store;
BootEventRecordStore::BootEventRecord record;
@@ -165,12 +169,28 @@
void RecordBootComplete() {
BootEventRecordStore boot_event_store;
BootEventRecordStore::BootEventRecord record;
+
time_t uptime = bootstat::ParseUptime();
+ time_t current_time_utc = time(nullptr);
+
+ if (boot_event_store.GetBootEvent("last_boot_time_utc", &record)) {
+ time_t last_boot_time_utc = record.second;
+ time_t time_since_last_boot = difftime(current_time_utc,
+ last_boot_time_utc);
+ boot_event_store.AddBootEventWithValue("time_since_last_boot",
+ time_since_last_boot);
+ }
+
+ boot_event_store.AddBootEventWithValue("last_boot_time_utc", current_time_utc);
// The boot_complete metric has two variants: boot_complete and
// ota_boot_complete. The latter signifies that the device is booting after
// a system update.
std::string boot_complete_prefix = CalculateBootCompletePrefix();
+ if (boot_complete_prefix.empty()) {
+ // The system is hosed because the build date property could not be read.
+ return;
+ }
// post_decrypt_time_elapsed is only logged on encrypted devices.
if (boot_event_store.GetBootEvent("post_decrypt_time_elapsed", &record)) {
diff --git a/include/sysutils/FrameworkListener.h b/include/sysutils/FrameworkListener.h
index 18049cd..2137069 100644
--- a/include/sysutils/FrameworkListener.h
+++ b/include/sysutils/FrameworkListener.h
@@ -32,6 +32,7 @@
int mCommandCount;
bool mWithSeq;
FrameworkCommandCollection *mCommands;
+ bool mSkipToNextNullByte;
public:
FrameworkListener(const char *socketName);
diff --git a/init/keychords.cpp b/init/keychords.cpp
index 7a7838d..1468c57 100644
--- a/init/keychords.cpp
+++ b/init/keychords.cpp
@@ -78,11 +78,13 @@
if (adb_enabled == "running") {
Service* svc = ServiceManager::GetInstance().FindServiceByKeychord(id);
if (svc) {
- INFO("Starting service %s from keychord\n", svc->name().c_str());
+ NOTICE("Starting service '%s' from keychord %d\n", svc->name().c_str(), id);
svc->Start();
} else {
- ERROR("service for keychord %d not found\n", id);
+ ERROR("Service for keychord %d not found\n", id);
}
+ } else {
+ WARNING("Not starting service for keychord %d because ADB is disabled\n", id);
}
}
diff --git a/liblog/pmsg_writer.c b/liblog/pmsg_writer.c
index 944feba..b338dca 100644
--- a/liblog/pmsg_writer.c
+++ b/liblog/pmsg_writer.c
@@ -31,6 +31,8 @@
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
+#include <sys/system_properties.h>
+
#include "config_write.h"
#include "log_portability.h"
#include "logger.h"
@@ -51,8 +53,25 @@
.write = pmsgWrite,
};
+static bool pmsgShouldUse = false;
+
+// Only use pmsg on eng builds
+static bool pmsgIsEng() {
+ char buf[PROP_VALUE_MAX];
+
+ if (__system_property_get("ro.build.type", buf) == 0) {
+ return false;
+ }
+
+ if (!strncmp(buf, "eng", sizeof("eng"))) {
+ return true;
+ }
+ return false;
+}
+
static int pmsgOpen()
{
+ pmsgShouldUse = pmsgIsEng();
if (pmsgLoggerWrite.context.fd < 0) {
pmsgLoggerWrite.context.fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
}
@@ -75,7 +94,7 @@
}
if ((logId != LOG_ID_SECURITY) &&
(logId != LOG_ID_EVENTS) &&
- !__android_log_is_debuggable()) {
+ (!pmsgShouldUse || !__android_log_is_debuggable())) {
return -EINVAL;
}
if (pmsgLoggerWrite.context.fd < 0) {
@@ -105,7 +124,7 @@
size_t i, payloadSize;
ssize_t ret;
- if ((logId == LOG_ID_EVENTS) && !__android_log_is_debuggable()) {
+ if ((logId == LOG_ID_EVENTS) && (!pmsgShouldUse || !__android_log_is_debuggable())) {
if (vec[0].iov_len < 4) {
return -EINVAL;
}
diff --git a/libsysutils/src/FrameworkListener.cpp b/libsysutils/src/FrameworkListener.cpp
index e7b3dd6..579ead9 100644
--- a/libsysutils/src/FrameworkListener.cpp
+++ b/libsysutils/src/FrameworkListener.cpp
@@ -49,6 +49,7 @@
errorRate = 0;
mCommandCount = 0;
mWithSeq = withSeq;
+ mSkipToNextNullByte = false;
}
bool FrameworkListener::onDataAvailable(SocketClient *c) {
@@ -59,10 +60,15 @@
if (len < 0) {
SLOGE("read() failed (%s)", strerror(errno));
return false;
- } else if (!len)
+ } else if (!len) {
return false;
- if(buffer[len-1] != '\0')
+ } else if (buffer[len-1] != '\0') {
SLOGW("String is not zero-terminated");
+ android_errorWriteLog(0x534e4554, "29831647");
+ c->sendMsg(500, "Command too large for buffer", false);
+ mSkipToNextNullByte = true;
+ return false;
+ }
int offset = 0;
int i;
@@ -70,11 +76,16 @@
for (i = 0; i < len; i++) {
if (buffer[i] == '\0') {
/* IMPORTANT: dispatchCommand() expects a zero-terminated string */
- dispatchCommand(c, buffer + offset);
+ if (mSkipToNextNullByte) {
+ mSkipToNextNullByte = false;
+ } else {
+ dispatchCommand(c, buffer + offset);
+ }
offset = i + 1;
}
}
+ mSkipToNextNullByte = false;
return true;
}
diff --git a/logcat/logcatd.rc b/logcat/logcatd.rc
index ce1a451..4d5c80c 100644
--- a/logcat/logcatd.rc
+++ b/logcat/logcatd.rc
@@ -35,7 +35,8 @@
# all exec/services are called with umask(077), so no gain beyond 0700
mkdir /data/misc/logd 0700 logd log
# logd for write to /data/misc/logd, log group for read from pstore (-L)
- exec - logd log -- /system/bin/logcat -L -b ${logd.logpersistd.buffer:-all} -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 1024 -n ${logd.logpersistd.size:-256}
+ # b/28788401 b/30041146 b/30612424
+ # exec - logd log -- /system/bin/logcat -L -b ${logd.logpersistd.buffer:-all} -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 1024 -n ${logd.logpersistd.size:-256}
start logcatd
# stop logcatd service and clear data
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
index a0b7499..c7506ef 100644
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -26,6 +26,7 @@
#include <syslog.h>
#include <log/logger.h>
+#include <private/android_filesystem_config.h>
#include "LogBuffer.h"
#include "LogKlog.h"
@@ -614,7 +615,12 @@
// Parse pid, tid and uid
const pid_t pid = sniffPid(&p, len - (p - buf));
const pid_t tid = pid;
- const uid_t uid = pid ? logbuf->pidToUid(pid) : 0;
+ uid_t uid = AID_ROOT;
+ if (pid) {
+ logbuf->lock();
+ uid = logbuf->pidToUid(pid);
+ logbuf->unlock();
+ }
// Parse (rules at top) to pull out a tag from the incoming kernel message.
// Some may view the following as an ugly heuristic, the desire is to
@@ -630,126 +636,124 @@
const char *tag = "";
const char *etag = tag;
size_t taglen = len - (p - buf);
- if (!isspace(*p) && *p) {
- const char *bt, *et, *cp;
+ const char *bt = p;
- bt = p;
- if ((taglen >= 6) && !fast<strncmp>(p, "[INFO]", 6)) {
- // <PRI>[<TIME>] "[INFO]"<tag> ":" message
- bt = p + 6;
- taglen -= 6;
- }
- for(et = bt; taglen && *et && (*et != ':') && !isspace(*et); ++et, --taglen) {
- // skip ':' within [ ... ]
- if (*et == '[') {
- while (taglen && *et && *et != ']') {
- ++et;
- --taglen;
- }
- }
- }
- for(cp = et; taglen && isspace(*cp); ++cp, --taglen);
- size_t size;
+ static const char infoBrace[] = "[INFO]";
+ static const size_t infoBraceLen = strlen(infoBrace);
+ if ((taglen >= infoBraceLen) && !fast<strncmp>(p, infoBrace, infoBraceLen)) {
+ // <PRI>[<TIME>] "[INFO]"<tag> ":" message
+ bt = p + infoBraceLen;
+ taglen -= infoBraceLen;
+ }
+ const char *et;
+ for (et = bt; taglen && *et && (*et != ':') && !isspace(*et); ++et, --taglen) {
+ // skip ':' within [ ... ]
+ if (*et == '[') {
+ while (taglen && *et && *et != ']') {
+ ++et;
+ --taglen;
+ }
+ if (!taglen) {
+ break;
+ }
+ }
+ }
+ const char *cp;
+ for (cp = et; taglen && isspace(*cp); ++cp, --taglen);
+
+ // Validate tag
+ size_t size = et - bt;
+ if (taglen && size) {
if (*cp == ':') {
+ // ToDo: handle case insensitive colon separated logging stutter:
+ // <tag> : <tag>: ...
+
// One Word
tag = bt;
etag = et;
p = cp + 1;
- } else if (taglen) {
- size = et - bt;
- if ((taglen > size) && // enough space for match plus trailing :
- (*bt == *cp) && // ubber fast<strncmp> pair
- fast<strncmp>(bt + 1, cp + 1, size - 1)) {
- // <PRI>[<TIME>] <tag>_host '<tag>.<num>' : message
- if (!fast<strncmp>(bt + size - 5, "_host", 5)
- && !fast<strncmp>(bt + 1, cp + 1, size - 6)) {
+ } else if ((taglen > size) && (tolower(*bt) == tolower(*cp))) {
+ // clean up any tag stutter
+ if (!fast<strncasecmp>(bt + 1, cp + 1, size - 1)) { // no match
+ // <PRI>[<TIME>] <tag> <tag> : message
+ // <PRI>[<TIME>] <tag> <tag>: message
+ // <PRI>[<TIME>] <tag> '<tag>.<num>' : message
+ // <PRI>[<TIME>] <tag> '<tag><num>' : message
+ // <PRI>[<TIME>] <tag> '<tag><stuff>' : message
+ const char *b = cp;
+ cp += size;
+ taglen -= size;
+ while (--taglen && !isspace(*++cp) && (*cp != ':'));
+ const char *e;
+ for (e = cp; taglen && isspace(*cp); ++cp, --taglen);
+ if (taglen && (*cp == ':')) {
+ tag = b;
+ etag = e;
+ p = cp + 1;
+ }
+ } else {
+ // what about <PRI>[<TIME>] <tag>_host '<tag><stuff>' : message
+ static const char host[] = "_host";
+ static const size_t hostlen = strlen(host);
+ if ((size > hostlen) &&
+ !fast<strncmp>(bt + size - hostlen, host, hostlen) &&
+ !fast<strncmp>(bt + 1, cp + 1, size - hostlen - 1)) {
const char *b = cp;
- cp += size - 5;
- taglen -= size - 5;
+ cp += size - hostlen;
+ taglen -= size - hostlen;
if (*cp == '.') {
while (--taglen && !isspace(*++cp) && (*cp != ':'));
const char *e;
- for(e = cp; taglen && isspace(*cp); ++cp, --taglen);
- if (*cp == ':') {
+ for (e = cp; taglen && isspace(*cp); ++cp, --taglen);
+ if (taglen && (*cp == ':')) {
tag = b;
etag = e;
p = cp + 1;
}
}
} else {
- while (--taglen && !isspace(*++cp) && (*cp != ':'));
- const char *e;
- for(e = cp; taglen && isspace(*cp); ++cp, --taglen);
- // Two words
- if (*cp == ':') {
- tag = bt;
- etag = e;
- p = cp + 1;
- }
- }
- } else if (isspace(cp[size])) {
- cp += size;
- taglen -= size;
- while (--taglen && isspace(*++cp));
- // <PRI>[<TIME>] <tag> <tag> : message
- if (*cp == ':') {
- tag = bt;
- etag = et;
- p = cp + 1;
- }
- } else if (cp[size] == ':') {
- // <PRI>[<TIME>] <tag> <tag> : message
- tag = bt;
- etag = et;
- p = cp + size + 1;
- } else if ((cp[size] == '.') || isdigit(cp[size])) {
- // <PRI>[<TIME>] <tag> '<tag>.<num>' : message
- // <PRI>[<TIME>] <tag> '<tag><num>' : message
- const char *b = cp;
- cp += size;
- taglen -= size;
- while (--taglen && !isspace(*++cp) && (*cp != ':'));
- const char *e = cp;
- while (taglen && isspace(*cp)) {
- ++cp;
- --taglen;
- }
- if (*cp == ':') {
- tag = b;
- etag = e;
- p = cp + 1;
- }
- } else {
- while (--taglen && !isspace(*++cp) && (*cp != ':'));
- const char *e = cp;
- while (taglen && isspace(*cp)) {
- ++cp;
- --taglen;
- }
- // Two words
- if (*cp == ':') {
- tag = bt;
- etag = e;
- p = cp + 1;
+ goto twoWord;
}
}
- } /* else no tag */
- size = etag - tag;
- if ((size <= 1)
- // register names like x9
- || ((size == 2) && (isdigit(tag[0]) || isdigit(tag[1])))
- // register names like x18 but not driver names like en0
- || ((size == 3) && (isdigit(tag[1]) && isdigit(tag[2])))
- // blacklist
- || ((size == 3) && !fast<strncmp>(tag, "CPU", 3))
- || ((size == 7) && !fast<strncasecmp>(tag, "WARNING", 7))
- || ((size == 5) && !fast<strncasecmp>(tag, "ERROR", 5))
- || ((size == 4) && !fast<strncasecmp>(tag, "INFO", 4))) {
- p = start;
- etag = tag = "";
+ } else {
+ // <PRI>[<TIME>] <tag> <stuff>' : message
+twoWord: while (--taglen && !isspace(*++cp) && (*cp != ':'));
+ const char *e;
+ for (e = cp; taglen && isspace(*cp); ++cp, --taglen);
+ // Two words
+ if (taglen && (*cp == ':')) {
+ tag = bt;
+ etag = e;
+ p = cp + 1;
+ }
}
+ } // else no tag
+
+ static const char cpu[] = "CPU";
+ static const size_t cpuLen = strlen(cpu);
+ static const char warning[] = "WARNING";
+ static const size_t warningLen = strlen(warning);
+ static const char error[] = "ERROR";
+ static const size_t errorLen = strlen(error);
+ static const char info[] = "INFO";
+ static const size_t infoLen = strlen(info);
+
+ size = etag - tag;
+ if ((size <= 1)
+ // register names like x9
+ || ((size == 2) && (isdigit(tag[0]) || isdigit(tag[1])))
+ // register names like x18 but not driver names like en0
+ || ((size == 3) && (isdigit(tag[1]) && isdigit(tag[2])))
+ // blacklist
+ || ((size == cpuLen) && !fast<strncmp>(tag, cpu, cpuLen))
+ || ((size == warningLen) && !fast<strncasecmp>(tag, warning, warningLen))
+ || ((size == errorLen) && !fast<strncasecmp>(tag, error, errorLen))
+ || ((size == infoLen) && !fast<strncasecmp>(tag, info, infoLen))) {
+ p = start;
+ etag = tag = "";
}
+
// Suppress additional stutter in tag:
// eg: [143:healthd]healthd -> [143:healthd]
taglen = etag - tag;
diff --git a/rootdir/asan.options b/rootdir/asan.options
index 70e0eca..d728f12 100644
--- a/rootdir/asan.options
+++ b/rootdir/asan.options
@@ -3,4 +3,5 @@
alloc_dealloc_mismatch=0
allocator_may_return_null=1
detect_container_overflow=0
+abort_on_error=1
include_if_exists=/system/asan.options.%b
diff --git a/rootdir/init.usb.rc b/rootdir/init.usb.rc
index 1fd1e2a..915d159 100644
--- a/rootdir/init.usb.rc
+++ b/rootdir/init.usb.rc
@@ -103,7 +103,7 @@
# Used to set USB configuration at boot and to switch the configuration
# when changing the default configuration
-on property:persist.sys.usb.config=*
+on boot && property:persist.sys.usb.config=*
setprop sys.usb.config ${persist.sys.usb.config}
#