Merge "Add android::base::Readlink."
diff --git a/adb/Android.mk b/adb/Android.mk
index 3512323..0a9c454 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -52,6 +52,7 @@
adb_utils.cpp \
fdevent.cpp \
sockets.cpp \
+ socket_spec.cpp \
transport.cpp \
transport_local.cpp \
transport_usb.cpp \
@@ -183,6 +184,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 \
@@ -198,6 +203,7 @@
libcrypto \
libcutils \
libdiagnose_usb \
+ libgmock_host \
# Set entrypoint to wmain from sysdeps_win32.cpp instead of main
LOCAL_LDFLAGS_windows := -municode
@@ -226,6 +232,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 9227eb1..cd6b7bd 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -203,8 +203,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_listeners.cpp b/adb/adb_listeners.cpp
index 0299be3..18b1492 100644
--- a/adb/adb_listeners.cpp
+++ b/adb/adb_listeners.cpp
@@ -23,12 +23,10 @@
#include <android-base/strings.h>
#include <cutils/sockets.h>
+#include "socket_spec.h"
#include "sysdeps.h"
#include "transport.h"
-// Not static because it is used in commandline.c.
-int gListenAll = 0;
-
// A listener is an entity which binds to a local port and, upon receiving a connection on that
// port, creates an asocket to connect the new local connection to a specific remote service.
//
@@ -120,48 +118,6 @@
}
}
-int local_name_to_fd(alistener* listener, int* resolved_tcp_port, std::string* error) {
- if (android::base::StartsWith(listener->local_name, "tcp:")) {
- int requested_port = atoi(&listener->local_name[4]);
- int sock = -1;
- if (gListenAll > 0) {
- sock = network_inaddr_any_server(requested_port, SOCK_STREAM, error);
- } else {
- sock = network_loopback_server(requested_port, SOCK_STREAM, error);
- }
-
- // If the caller requested port 0, update the listener name with the resolved port.
- if (sock >= 0 && requested_port == 0) {
- int local_port = adb_socket_get_local_port(sock);
- if (local_port > 0) {
- listener->local_name = android::base::StringPrintf("tcp:%d", local_port);
- if (resolved_tcp_port != nullptr) {
- *resolved_tcp_port = local_port;
- }
- }
- }
-
- return sock;
- }
-#if !defined(_WIN32) // No Unix-domain sockets on Windows.
- // It's nonsensical to support the "reserved" space on the adb host side.
- if (android::base::StartsWith(listener->local_name, "local:")) {
- return network_local_server(&listener->local_name[6], ANDROID_SOCKET_NAMESPACE_ABSTRACT,
- SOCK_STREAM, error);
- } else if (android::base::StartsWith(listener->local_name, "localabstract:")) {
- return network_local_server(&listener->local_name[14], ANDROID_SOCKET_NAMESPACE_ABSTRACT,
- SOCK_STREAM, error);
- } else if (android::base::StartsWith(listener->local_name, "localfilesystem:")) {
- return network_local_server(&listener->local_name[16], ANDROID_SOCKET_NAMESPACE_FILESYSTEM,
- SOCK_STREAM, error);
- }
-
-#endif
- *error = android::base::StringPrintf("unknown local portname '%s'",
- listener->local_name.c_str());
- return -1;
-}
-
// Write the list of current listeners (network redirections) into a string.
std::string format_listeners() {
std::string result;
@@ -230,11 +186,20 @@
std::unique_ptr<alistener> listener(new alistener(local_name, connect_to));
- listener->fd = local_name_to_fd(listener.get(), resolved_tcp_port, error);
+ int resolved = 0;
+ listener->fd = socket_spec_listen(listener->local_name, error, &resolved);
if (listener->fd < 0) {
return INSTALL_STATUS_CANNOT_BIND;
}
+ // If the caller requested port 0, update the listener name with the resolved port.
+ if (resolved != 0) {
+ listener->local_name = android::base::StringPrintf("tcp:%d", resolved);
+ if (resolved_tcp_port) {
+ *resolved_tcp_port = resolved;
+ }
+ }
+
close_on_exec(listener->fd);
if (listener->connect_to == "*smartsocket*") {
fdevent_install(&listener->fde, listener->fd, ss_listener_event_func, listener.get());
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 e160169..0c85fe5 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 8aab389..c69fe19 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -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 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 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 @@
" (-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 @@
" 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 @@
// 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 @@
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 @@
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 @@
*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 @@
}
}
#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 @@
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 @@
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 @@
}
}
- 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 @@
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 @@
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 @@
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 @@
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 @@
#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 @@
} 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 @@
}
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 @@
}
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 @@
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 @@
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 @@
}
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 @@
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());
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/services.cpp b/adb/services.cpp
index 3b212e9..2207a3e 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -49,6 +49,7 @@
#include "remount_service.h"
#include "services.h"
#include "shell_service.h"
+#include "socket_spec.h"
#include "sysdeps.h"
#include "transport.h"
@@ -278,36 +279,12 @@
int service_to_fd(const char* name, const atransport* transport) {
int ret = -1;
- if(!strncmp(name, "tcp:", 4)) {
- int port = atoi(name + 4);
- name = strchr(name + 4, ':');
- if(name == 0) {
- std::string error;
- ret = network_loopback_client(port, SOCK_STREAM, &error);
- if (ret >= 0)
- disable_tcp_nagle(ret);
- } else {
-#if ADB_HOST
- std::string error;
- ret = network_connect(name + 1, port, SOCK_STREAM, 0, &error);
-#else
- return -1;
-#endif
+ if (is_socket_spec(name)) {
+ std::string error;
+ ret = socket_spec_connect(name, &error);
+ if (ret < 0) {
+ LOG(ERROR) << "failed to connect to socket '" << name << "': " << error;
}
-#if !defined(_WIN32) /* winsock doesn't implement unix domain sockets */
- } else if(!strncmp(name, "local:", 6)) {
- ret = socket_local_client(name + 6,
- ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
- } else if(!strncmp(name, "localreserved:", 14)) {
- ret = socket_local_client(name + 14,
- ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
- } else if(!strncmp(name, "localabstract:", 14)) {
- ret = socket_local_client(name + 14,
- ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
- } else if(!strncmp(name, "localfilesystem:", 16)) {
- ret = socket_local_client(name + 16,
- ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM);
-#endif
#if !ADB_HOST
} else if(!strncmp("dev:", name, 4)) {
ret = unix_open(name + 4, O_RDWR | O_CLOEXEC);
diff --git a/adb/socket_spec.cpp b/adb/socket_spec.cpp
new file mode 100644
index 0000000..f8bbbb3
--- /dev/null
+++ b/adb/socket_spec.cpp
@@ -0,0 +1,220 @@
+/*
+ * 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 "socket_spec.h"
+
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <cutils/sockets.h>
+
+#include "adb.h"
+#include "sysdeps.h"
+
+using android::base::StartsWith;
+using android::base::StringPrintf;
+
+#if defined(__linux__)
+#define ADB_LINUX 1
+#else
+#define ADB_LINUX 0
+#endif
+
+#if defined(_WIN32)
+#define ADB_WINDOWS 1
+#else
+#define ADB_WINDOWS 0
+#endif
+
+// Not static because it is used in commandline.c.
+int gListenAll = 0;
+
+struct LocalSocketType {
+ int socket_namespace;
+ bool available;
+};
+
+static auto& kLocalSocketTypes = *new std::unordered_map<std::string, LocalSocketType>({
+#if ADB_HOST
+ { "local", { ANDROID_SOCKET_NAMESPACE_FILESYSTEM, !ADB_WINDOWS } },
+#else
+ { "local", { ANDROID_SOCKET_NAMESPACE_RESERVED, !ADB_WINDOWS } },
+#endif
+
+ { "localreserved", { ANDROID_SOCKET_NAMESPACE_RESERVED, !ADB_HOST } },
+ { "localabstract", { ANDROID_SOCKET_NAMESPACE_ABSTRACT, ADB_LINUX } },
+ { "localfilesystem", { ANDROID_SOCKET_NAMESPACE_FILESYSTEM, !ADB_WINDOWS } },
+});
+
+static bool parse_tcp_spec(const std::string& spec, std::string* hostname, int* port,
+ std::string* error) {
+ std::vector<std::string> fragments = android::base::Split(spec, ":");
+ if (fragments.size() == 1 || fragments.size() > 3) {
+ *error = StringPrintf("invalid tcp specification: '%s'", spec.c_str());
+ return false;
+ }
+
+ if (fragments[0] != "tcp") {
+ *error = StringPrintf("specification is not tcp: '%s'", spec.c_str());
+ return false;
+ }
+
+ // strtol accepts leading whitespace.
+ const std::string& port_str = fragments.back();
+ if (port_str.empty() || port_str[0] < '0' || port_str[0] > '9') {
+ *error = StringPrintf("invalid port '%s'", port_str.c_str());
+ return false;
+ }
+
+ char* parsed_end;
+ long parsed_port = strtol(port_str.c_str(), &parsed_end, 10);
+ if (*parsed_end != '\0') {
+ *error = StringPrintf("trailing chars in port: '%s'", port_str.c_str());
+ return false;
+ }
+ if (parsed_port > 65535) {
+ *error = StringPrintf("invalid port %ld", parsed_port);
+ return false;
+ }
+
+ // tcp:123 is valid, tcp::123 isn't.
+ if (fragments.size() == 2) {
+ // Empty hostname.
+ if (hostname) {
+ *hostname = "";
+ }
+ } else {
+ if (fragments[1].empty()) {
+ *error = StringPrintf("empty host in '%s'", spec.c_str());
+ return false;
+ }
+ if (hostname) {
+ *hostname = fragments[1];
+ }
+ }
+
+ if (port) {
+ *port = parsed_port;
+ }
+
+ return true;
+}
+
+static bool tcp_host_is_local(const std::string& hostname) {
+ // FIXME
+ return hostname.empty() || hostname == "localhost";
+}
+
+bool is_socket_spec(const std::string& spec) {
+ for (const auto& it : kLocalSocketTypes) {
+ std::string prefix = it.first + ":";
+ if (StartsWith(spec, prefix.c_str())) {
+ return true;
+ }
+ }
+ return StartsWith(spec, "tcp:");
+}
+
+int socket_spec_connect(const std::string& spec, std::string* error) {
+ if (StartsWith(spec, "tcp:")) {
+ std::string hostname;
+ int port;
+ if (!parse_tcp_spec(spec, &hostname, &port, error)) {
+ return -1;
+ }
+
+ int result;
+ if (tcp_host_is_local(hostname)) {
+ result = network_loopback_client(port, SOCK_STREAM, error);
+ } else {
+#if ADB_HOST
+ result = network_connect(hostname, port, SOCK_STREAM, 0, error);
+#else
+ // Disallow arbitrary connections in adbd.
+ *error = "adbd does not support arbitrary tcp connections";
+ return -1;
+#endif
+ }
+
+ if (result >= 0) {
+ disable_tcp_nagle(result);
+ }
+ return result;
+ }
+
+ for (const auto& it : kLocalSocketTypes) {
+ std::string prefix = it.first + ":";
+ if (StartsWith(spec, prefix.c_str())) {
+ if (!it.second.available) {
+ *error = StringPrintf("socket type %s is unavailable on this platform",
+ it.first.c_str());
+ return -1;
+ }
+
+ return network_local_client(&spec[prefix.length()], it.second.socket_namespace,
+ SOCK_STREAM, error);
+ }
+ }
+
+ *error = StringPrintf("unknown socket specification '%s'", spec.c_str());
+ return -1;
+}
+
+int socket_spec_listen(const std::string& spec, std::string* error, int* resolved_tcp_port) {
+ if (StartsWith(spec, "tcp:")) {
+ std::string hostname;
+ int port;
+ if (!parse_tcp_spec(spec, &hostname, &port, error)) {
+ return -1;
+ }
+
+ int result;
+ if (hostname.empty() && gListenAll) {
+ result = network_inaddr_any_server(port, SOCK_STREAM, error);
+ } else if (tcp_host_is_local(hostname)) {
+ result = network_loopback_server(port, SOCK_STREAM, error);
+ } else {
+ // TODO: Implement me.
+ *error = "listening on specified hostname currently unsupported";
+ return -1;
+ }
+
+ if (result >= 0 && port == 0 && resolved_tcp_port) {
+ *resolved_tcp_port = adb_socket_get_local_port(result);
+ }
+ return result;
+ }
+
+ for (const auto& it : kLocalSocketTypes) {
+ std::string prefix = it.first + ":";
+ if (StartsWith(spec, prefix.c_str())) {
+ if (!it.second.available) {
+ *error = StringPrintf("attempted to listen on unavailable socket type: '%s'",
+ spec.c_str());
+ return -1;
+ }
+
+ return network_local_server(&spec[prefix.length()], it.second.socket_namespace,
+ SOCK_STREAM, error);
+ }
+ }
+
+ *error = StringPrintf("unknown socket specification '%s'", spec.c_str());
+ return -1;
+}
diff --git a/adb/socket_spec.h b/adb/socket_spec.h
new file mode 100644
index 0000000..69efb07
--- /dev/null
+++ b/adb/socket_spec.h
@@ -0,0 +1,26 @@
+/*
+ * 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 <string>
+
+// Returns true if the argument starts with a plausible socket prefix.
+bool is_socket_spec(const std::string& spec);
+
+int socket_spec_connect(const std::string& spec, std::string* error);
+int socket_spec_listen(const std::string& spec, std::string* error,
+ int* resolved_tcp_port = nullptr);
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 322f992..8d99722 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -277,6 +277,15 @@
int network_loopback_client(int port, int type, std::string* error);
int network_loopback_server(int port, int type, std::string* error);
int network_inaddr_any_server(int port, int type, std::string* error);
+
+inline int network_local_client(const char* name, int namespace_id, int type, std::string* error) {
+ abort();
+}
+
+inline int network_local_server(const char* name, int namespace_id, int type, std::string* error) {
+ abort();
+}
+
int network_connect(const std::string& host, int port, int type, int timeout,
std::string* error);
@@ -638,10 +647,12 @@
return _fd_set_error_str(socket_inaddr_any_server(port, type), error);
}
-inline int network_local_server(const char *name, int namespace_id, int type,
- std::string* error) {
- return _fd_set_error_str(socket_local_server(name, namespace_id, type),
- error);
+inline int network_local_client(const char* name, int namespace_id, int type, std::string* error) {
+ return _fd_set_error_str(socket_local_client(name, namespace_id, type), error);
+}
+
+inline int network_local_server(const char* name, int namespace_id, int type, std::string* error) {
+ return _fd_set_error_str(socket_local_server(name, namespace_id, type), error);
}
inline int network_connect(const std::string& host, int port, int type,
diff --git a/base/include/android-base/macros.h b/base/include/android-base/macros.h
index 299ec35..ee16d02 100644
--- a/base/include/android-base/macros.h
+++ b/base/include/android-base/macros.h
@@ -60,9 +60,18 @@
// This should be used in the private: declarations for a class
// that wants to prevent anyone from instantiating it. This is
// especially useful for classes containing only static methods.
+//
+// When building with C++11 toolchains, just use the language support
+// for explicitly deleted methods.
+#if __cplusplus >= 201103L
+#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
+ TypeName() = delete; \
+ DISALLOW_COPY_AND_ASSIGN(TypeName)
+#else
#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
TypeName(); \
DISALLOW_COPY_AND_ASSIGN(TypeName)
+#endif
// The arraysize(arr) macro returns the # of elements in an array arr.
// The expression is a compile-time constant, and therefore can be
diff --git a/bootstat/Android.bp b/bootstat/Android.bp
new file mode 100644
index 0000000..89b4598
--- /dev/null
+++ b/bootstat/Android.bp
@@ -0,0 +1,94 @@
+//
+// 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.
+//
+
+bootstat_lib_src_files = [
+ "boot_event_record_store.cpp",
+ "event_log_list_builder.cpp",
+ "histogram_logger.cpp",
+ "uptime_parser.cpp",
+]
+
+cc_defaults {
+ name: "bootstat_defaults",
+
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+
+ // 524291 corresponds to sysui_histogram, from
+ // frameworks/base/core/java/com/android/internal/logging/EventLogTags.logtags
+ "-DHISTOGRAM_LOG_TAG=524291",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ "liblog",
+ ],
+ whole_static_libs: ["libgtest_prod"],
+ // Clang is required because of C++14
+ clang: true,
+}
+
+// bootstat static library
+// -----------------------------------------------------------------------------
+cc_library_static {
+ name: "libbootstat",
+ defaults: ["bootstat_defaults"],
+ srcs: bootstat_lib_src_files,
+}
+
+// bootstat static library, debug
+// -----------------------------------------------------------------------------
+cc_library_static {
+ name: "libbootstat_debug",
+ defaults: ["bootstat_defaults"],
+ host_supported: true,
+ srcs: bootstat_lib_src_files,
+
+ target: {
+ host: {
+ cflags: ["-UNDEBUG"],
+ },
+ },
+}
+
+// bootstat binary
+// -----------------------------------------------------------------------------
+cc_binary {
+ name: "bootstat",
+ defaults: ["bootstat_defaults"],
+ static_libs: ["libbootstat"],
+ init_rc: ["bootstat.rc"],
+ srcs: ["bootstat.cpp"],
+}
+
+// Native tests
+// -----------------------------------------------------------------------------
+cc_test {
+ name: "bootstat_tests",
+ defaults: ["bootstat_defaults"],
+ host_supported: true,
+ static_libs: [
+ "libbootstat_debug",
+ "libgmock",
+ ],
+ srcs: [
+ "boot_event_record_store_test.cpp",
+ "event_log_list_builder_test.cpp",
+ "testrunner.cpp",
+ ],
+}
diff --git a/bootstat/Android.mk b/bootstat/Android.mk
deleted file mode 100644
index bdd680d..0000000
--- a/bootstat/Android.mk
+++ /dev/null
@@ -1,141 +0,0 @@
-#
-# 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-bootstat_lib_src_files := \
- boot_event_record_store.cpp \
- event_log_list_builder.cpp \
- histogram_logger.cpp \
- uptime_parser.cpp \
-
-bootstat_src_files := \
- bootstat.cpp \
-
-bootstat_test_src_files := \
- boot_event_record_store_test.cpp \
- event_log_list_builder_test.cpp \
- testrunner.cpp \
-
-bootstat_shared_libs := \
- libbase \
- libcutils \
- liblog \
-
-bootstat_cflags := \
- -Wall \
- -Wextra \
- -Werror \
-
-# 524291 corresponds to sysui_histogram, from
-# frameworks/base/core/java/com/android/internal/logging/EventLogTags.logtags
-bootstat_cflags += -DHISTOGRAM_LOG_TAG=524291
-
-bootstat_debug_cflags := \
- $(bootstat_cflags) \
- -UNDEBUG \
-
-# bootstat static library
-# -----------------------------------------------------------------------------
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libbootstat
-LOCAL_CFLAGS := $(bootstat_cflags)
-LOCAL_WHOLE_STATIC_LIBRARIES := libgtest_prod
-LOCAL_SHARED_LIBRARIES := $(bootstat_shared_libs)
-LOCAL_SRC_FILES := $(bootstat_lib_src_files)
-# Clang is required because of C++14
-LOCAL_CLANG := true
-
-include $(BUILD_STATIC_LIBRARY)
-
-# bootstat static library, debug
-# -----------------------------------------------------------------------------
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libbootstat_debug
-LOCAL_CFLAGS := $(bootstat_cflags)
-LOCAL_WHOLE_STATIC_LIBRARIES := libgtest_prod
-LOCAL_SHARED_LIBRARIES := $(bootstat_shared_libs)
-LOCAL_SRC_FILES := $(bootstat_lib_src_files)
-# Clang is required because of C++14
-LOCAL_CLANG := true
-
-include $(BUILD_STATIC_LIBRARY)
-
-# bootstat host static library, debug
-# -----------------------------------------------------------------------------
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libbootstat_host_debug
-LOCAL_CFLAGS := $(bootstat_debug_cflags)
-LOCAL_WHOLE_STATIC_LIBRARIES := libgtest_prod
-LOCAL_SHARED_LIBRARIES := $(bootstat_shared_libs)
-LOCAL_SRC_FILES := $(bootstat_lib_src_files)
-# Clang is required because of C++14
-LOCAL_CLANG := true
-
-include $(BUILD_HOST_STATIC_LIBRARY)
-
-# bootstat binary
-# -----------------------------------------------------------------------------
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := bootstat
-LOCAL_CFLAGS := $(bootstat_cflags)
-LOCAL_WHOLE_STATIC_LIBRARIES := libgtest_prod
-LOCAL_SHARED_LIBRARIES := $(bootstat_shared_libs)
-LOCAL_STATIC_LIBRARIES := libbootstat
-LOCAL_INIT_RC := bootstat.rc
-LOCAL_SRC_FILES := $(bootstat_src_files)
-# Clang is required because of C++14
-LOCAL_CLANG := true
-
-include $(BUILD_EXECUTABLE)
-
-# Native tests
-# -----------------------------------------------------------------------------
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := bootstat_tests
-LOCAL_CFLAGS := $(bootstat_tests_cflags)
-LOCAL_SHARED_LIBRARIES := $(bootstat_shared_libs)
-LOCAL_STATIC_LIBRARIES := libbootstat_debug libgmock
-LOCAL_SRC_FILES := $(bootstat_test_src_files)
-# Clang is required because of C++14
-LOCAL_CLANG := true
-
-include $(BUILD_NATIVE_TEST)
-
-# Host native tests
-# -----------------------------------------------------------------------------
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := bootstat_tests
-LOCAL_CFLAGS := $(bootstat_tests_cflags)
-LOCAL_SHARED_LIBRARIES := $(bootstat_shared_libs)
-LOCAL_STATIC_LIBRARIES := libbootstat_host_debug libgmock_host
-LOCAL_SRC_FILES := $(bootstat_test_src_files)
-# Clang is required because of C++14
-LOCAL_CLANG := true
-
-include $(BUILD_HOST_NATIVE_TEST)
diff --git a/debuggerd/tombstone.cpp b/debuggerd/tombstone.cpp
index 3ddd2b0..8506765 100644
--- a/debuggerd/tombstone.cpp
+++ b/debuggerd/tombstone.cpp
@@ -136,8 +136,13 @@
#if defined(SEGV_BNDERR)
case SEGV_BNDERR: return "SEGV_BNDERR";
#endif
+#if defined(SEGV_PKUERR)
+ case SEGV_PKUERR: return "SEGV_PKUERR";
+#endif
}
-#if defined(SEGV_BNDERR)
+#if defined(SEGV_PKUERR)
+ static_assert(NSIGSEGV == SEGV_PKUERR, "missing SEGV_* si_code");
+#elif defined(SEGV_BNDERR)
static_assert(NSIGSEGV == SEGV_BNDERR, "missing SEGV_* si_code");
#else
static_assert(NSIGSEGV == SEGV_ACCERR, "missing SEGV_* si_code");
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index 1b4ecbe..28e3d4b 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -58,7 +58,7 @@
LOCAL_C_INCLUDES_windows := development/host/windows/usb/api
LOCAL_STATIC_LIBRARIES := \
- libziparchive-host \
+ libziparchive \
libext4_utils_host \
libsparse_host \
libutils \
diff --git a/include/utils/Unicode.h b/include/utils/Unicode.h
index a006082..a13f347 100644
--- a/include/utils/Unicode.h
+++ b/include/utils/Unicode.h
@@ -31,7 +31,7 @@
char16_t *strncpy16(char16_t *, const char16_t *, size_t);
char16_t *strstr16(const char16_t*, const char16_t*);
-// Version of comparison that supports embedded nulls.
+// Version of comparison that supports embedded NULs.
// This is different than strncmp() because we don't stop
// at a nul character and consider the strings to be different
// if the lengths are different (thus we need to supply the
@@ -58,7 +58,7 @@
* large enough to store the string, the part of the "src" string is stored
* into "dst" as much as possible. See the examples for more detail.
* Returns the size actually used for storing the string.
- * dst" is not null-terminated when dst_len is fully used (like strncpy).
+ * dst" is not nul-terminated when dst_len is fully used (like strncpy).
*
* Example 1
* "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84)
@@ -67,7 +67,7 @@
* ->
* Returned value == 6
* "dst" becomes \xE3\x81\x82\xE3\x81\x84\0
- * (note that "dst" is null-terminated)
+ * (note that "dst" is nul-terminated)
*
* Example 2
* "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84)
@@ -76,7 +76,7 @@
* ->
* Returned value == 3
* "dst" becomes \xE3\x81\x82\0
- * (note that "dst" is null-terminated, but \u3044 is not stored in "dst"
+ * (note that "dst" is nul-terminated, but \u3044 is not stored in "dst"
* since "dst" does not have enough size to store the character)
*
* Example 3
@@ -86,9 +86,9 @@
* ->
* Returned value == 6
* "dst" becomes \xE3\x81\x82\xE3\x81\x84
- * (note that "dst" is NOT null-terminated, like strncpy)
+ * (note that "dst" is NOT nul-terminated, like strncpy)
*/
-void utf32_to_utf8(const char32_t* src, size_t src_len, char* dst);
+void utf32_to_utf8(const char32_t* src, size_t src_len, char* dst, size_t dst_len);
/**
* Returns the unicode value at "index".
@@ -108,9 +108,9 @@
/**
* Converts a UTF-16 string to UTF-8. The destination buffer must be large
* enough to fit the UTF-16 as measured by utf16_to_utf8_length with an added
- * NULL terminator.
+ * NUL terminator.
*/
-void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst);
+void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst, size_t dst_len);
/**
* Returns the length of "src" when "src" is valid UTF-8 string.
@@ -118,7 +118,7 @@
* is an invalid string.
*
* This function should be used to determine whether "src" is valid UTF-8
- * characters with valid unicode codepoints. "src" must be null-terminated.
+ * characters with valid unicode codepoints. "src" must be nul-terminated.
*
* If you are going to use other utf8_to_... functions defined in this header
* with string which may not be valid UTF-8 with valid codepoint (form 0 to
@@ -138,35 +138,38 @@
/**
* Stores a UTF-32 string converted from "src" in "dst". "dst" must be large
* enough to store the entire converted string as measured by
- * utf8_to_utf32_length plus space for a NULL terminator.
+ * utf8_to_utf32_length plus space for a NUL terminator.
*/
void utf8_to_utf32(const char* src, size_t src_len, char32_t* dst);
/**
- * Returns the UTF-16 length of UTF-8 string "src".
+ * Returns the UTF-16 length of UTF-8 string "src". Returns -1 in case
+ * it's invalid utf8. No buffer over-read occurs because of bound checks. Using overreadIsFatal you
+ * can ask to log a message and fail in case the invalid utf8 could have caused an override if no
+ * bound checks were used (otherwise -1 is returned).
*/
-ssize_t utf8_to_utf16_length(const uint8_t* src, size_t srcLen);
+ssize_t utf8_to_utf16_length(const uint8_t* src, size_t srcLen, bool overreadIsFatal = false);
/**
* Convert UTF-8 to UTF-16 including surrogate pairs.
- * Returns a pointer to the end of the string (where a null terminator might go
- * if you wanted to add one).
+ * Returns a pointer to the end of the string (where a NUL terminator might go
+ * if you wanted to add one). At most dstLen characters are written; it won't emit half a surrogate
+ * pair. If dstLen == 0 nothing is written and dst is returned. If dstLen > SSIZE_MAX it aborts
+ * (this being probably a negative number returned as an error and casted to unsigned).
*/
-char16_t* utf8_to_utf16_no_null_terminator(const uint8_t* src, size_t srcLen, char16_t* dst);
+char16_t* utf8_to_utf16_no_null_terminator(
+ const uint8_t* src, size_t srcLen, char16_t* dst, size_t dstLen);
/**
- * Convert UTF-8 to UTF-16 including surrogate pairs. The destination buffer
- * must be large enough to hold the result as measured by utf8_to_utf16_length
- * plus an added NULL terminator.
+ * Convert UTF-8 to UTF-16 including surrogate pairs. At most dstLen - 1
+ * characters are written; it won't emit half a surrogate pair; and a NUL terminator is appended
+ * after. dstLen - 1 can be measured beforehand using utf8_to_utf16_length. Aborts if dstLen == 0
+ * (at least one character is needed for the NUL terminator) or dstLen > SSIZE_MAX (the latter
+ * case being likely a negative number returned as an error and casted to unsigned) . Returns a
+ * pointer to the NUL terminator.
*/
-void utf8_to_utf16(const uint8_t* src, size_t srcLen, char16_t* dst);
-
-/**
- * Like utf8_to_utf16_no_null_terminator, but you can supply a maximum length of the
- * decoded string. The decoded string will fill up to that length; if it is longer
- * the returned pointer will be to the character after dstLen.
- */
-char16_t* utf8_to_utf16_n(const uint8_t* src, size_t srcLen, char16_t* dst, size_t dstLen);
+char16_t *utf8_to_utf16(
+ const uint8_t* src, size_t srcLen, char16_t* dst, size_t dstLen);
}
diff --git a/libbacktrace/Android.mk b/libbacktrace/Android.mk
index 9bb113a..56a3970 100644
--- a/libbacktrace/Android.mk
+++ b/libbacktrace/Android.mk
@@ -67,7 +67,7 @@
libbacktrace_offline_static_libraries_host := \
libbacktrace \
libunwind \
- libziparchive-host \
+ libziparchive \
libz \
libbase \
liblog \
@@ -129,7 +129,7 @@
libz \
backtrace_test_static_libraries_host := \
- libziparchive-host \
+ libziparchive \
libz \
libutils \
libLLVMObject \
diff --git a/libcutils/sched_policy.c b/libcutils/sched_policy.c
index 32e6c25..ad5671b 100644
--- a/libcutils/sched_policy.c
+++ b/libcutils/sched_policy.c
@@ -52,6 +52,7 @@
static pthread_once_t the_once = PTHREAD_ONCE_INIT;
static int __sys_supports_schedgroups = -1;
+static int __sys_supports_timerslack = -1;
// File descriptors open to /dev/cpuctl/../tasks, setup by initialize, or -1 on error.
static int bg_cgroup_fd = -1;
@@ -104,7 +105,7 @@
static void __initialize(void) {
char* filename;
- if (!access("/dev/cpuctl/tasks", F_OK)) {
+ if (!access("/dev/cpuctl/tasks", W_OK)) {
__sys_supports_schedgroups = 1;
filename = "/dev/cpuctl/tasks";
@@ -123,7 +124,7 @@
}
#ifdef USE_CPUSETS
- if (!access("/dev/cpuset/tasks", F_OK)) {
+ if (!access("/dev/cpuset/tasks", W_OK)) {
filename = "/dev/cpuset/foreground/tasks";
fg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
@@ -142,6 +143,10 @@
#endif
}
#endif
+
+ char buf[64];
+ snprintf(buf, sizeof(buf), "/proc/%d/timerslack_ns", getpid());
+ __sys_supports_timerslack = !access(buf, W_OK);
}
/*
@@ -417,8 +422,10 @@
¶m);
}
- set_timerslack_ns(tid, policy == SP_BACKGROUND ?
+ if (__sys_supports_timerslack) {
+ set_timerslack_ns(tid, policy == SP_BACKGROUND ?
TIMER_SLACK_BG : TIMER_SLACK_FG);
+ }
return 0;
}
diff --git a/libmemunreachable/Android.bp b/libmemunreachable/Android.bp
new file mode 100644
index 0000000..85bc421
--- /dev/null
+++ b/libmemunreachable/Android.bp
@@ -0,0 +1,80 @@
+cc_defaults {
+ name: "libmemunreachable_defaults",
+
+ cflags: [
+ "-std=c++14",
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ clang: true,
+ shared_libs: [
+ "libbase",
+ "liblog",
+ ],
+}
+
+cc_library_shared {
+ name: "libmemunreachable",
+ defaults: ["libmemunreachable_defaults"],
+ srcs: [
+ "Allocator.cpp",
+ "HeapWalker.cpp",
+ "LeakFolding.cpp",
+ "LeakPipe.cpp",
+ "LineBuffer.cpp",
+ "MemUnreachable.cpp",
+ "ProcessMappings.cpp",
+ "PtracerThread.cpp",
+ "ThreadCapture.cpp",
+ ],
+
+ static_libs: [
+ "libc_malloc_debug_backtrace",
+ "libc_logging",
+ ],
+ // Only need this for arm since libc++ uses its own unwind code that
+ // doesn't mix with the other default unwind code.
+ arch: {
+ arm: {
+ static_libs: ["libunwind_llvm"],
+ },
+ },
+ export_include_dirs: ["include"],
+ local_include_dirs: ["include"],
+}
+
+cc_test {
+ name: "memunreachable_test",
+ defaults: ["libmemunreachable_defaults"],
+ host_supported: true,
+ srcs: [
+ "tests/Allocator_test.cpp",
+ "tests/HeapWalker_test.cpp",
+ "tests/LeakFolding_test.cpp",
+ ],
+
+ target: {
+ android: {
+ srcs: [
+ "tests/DisableMalloc_test.cpp",
+ "tests/MemUnreachable_test.cpp",
+ "tests/ThreadCapture_test.cpp",
+ ],
+ shared_libs: [
+ "libmemunreachable",
+ ],
+ },
+ host: {
+ srcs: [
+ "Allocator.cpp",
+ "HeapWalker.cpp",
+ "LeakFolding.cpp",
+ "tests/HostMallocStub.cpp",
+ ],
+ },
+ darwin: {
+ enabled: false,
+ },
+ },
+}
diff --git a/libmemunreachable/Android.mk b/libmemunreachable/Android.mk
deleted file mode 100644
index 7b66d44..0000000
--- a/libmemunreachable/Android.mk
+++ /dev/null
@@ -1,65 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-memunreachable_srcs := \
- Allocator.cpp \
- HeapWalker.cpp \
- LeakFolding.cpp \
- LeakPipe.cpp \
- LineBuffer.cpp \
- MemUnreachable.cpp \
- ProcessMappings.cpp \
- PtracerThread.cpp \
- ThreadCapture.cpp \
-
-memunreachable_test_srcs := \
- tests/Allocator_test.cpp \
- tests/DisableMalloc_test.cpp \
- tests/HeapWalker_test.cpp \
- tests/LeakFolding_test.cpp \
- tests/MemUnreachable_test.cpp \
- tests/ThreadCapture_test.cpp \
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libmemunreachable
-LOCAL_SRC_FILES := $(memunreachable_srcs)
-LOCAL_CFLAGS := -std=c++14 -Wall -Wextra -Werror
-LOCAL_SHARED_LIBRARIES := libbase liblog
-LOCAL_STATIC_LIBRARIES := libc_malloc_debug_backtrace libc_logging
-# Only need this for arm since libc++ uses its own unwind code that
-# doesn't mix with the other default unwind code.
-LOCAL_STATIC_LIBRARIES_arm := libunwind_llvm
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_CLANG := true
-
-include $(BUILD_SHARED_LIBRARY)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := memunreachable_test
-LOCAL_SRC_FILES := $(memunreachable_test_srcs)
-LOCAL_CFLAGS := -std=c++14 -Wall -Wextra -Werror
-LOCAL_CLANG := true
-LOCAL_SHARED_LIBRARIES := libmemunreachable libbase liblog
-
-include $(BUILD_NATIVE_TEST)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := memunreachable_test
-LOCAL_SRC_FILES := \
- Allocator.cpp \
- HeapWalker.cpp \
- LeakFolding.cpp \
- tests/Allocator_test.cpp \
- tests/HeapWalker_test.cpp \
- tests/HostMallocStub.cpp \
- tests/LeakFolding_test.cpp \
-
-LOCAL_CFLAGS := -std=c++14 -Wall -Wextra -Werror
-LOCAL_CLANG := true
-LOCAL_SHARED_LIBRARIES := libbase liblog
-LOCAL_MODULE_HOST_OS := linux
-
-include $(BUILD_HOST_NATIVE_TEST)
diff --git a/libsync/Android.bp b/libsync/Android.bp
new file mode 100644
index 0000000..4948aa5
--- /dev/null
+++ b/libsync/Android.bp
@@ -0,0 +1,42 @@
+cc_defaults {
+ name: "libsync_defaults",
+ srcs: ["sync.c"],
+ local_include_dirs: ["include"],
+ export_include_dirs: ["include"],
+ cflags: ["-Werror"],
+}
+
+cc_library_shared {
+ name: "libsync",
+ defaults: ["libsync_defaults"],
+}
+
+// libsync_recovery is only intended for the recovery binary.
+// Future versions of the kernel WILL require an updated libsync, and will break
+// anything statically linked against the current libsync.
+cc_library_static {
+ name: "libsync_recovery",
+ defaults: ["libsync_defaults"],
+}
+
+cc_test {
+ name: "sync_test",
+ defaults: ["libsync_defaults"],
+ gtest: false,
+ srcs: ["sync_test.c"],
+}
+
+cc_test {
+ name: "sync-unit-tests",
+ shared_libs: ["libsync"],
+ srcs: ["tests/sync_test.cpp"],
+ cflags: [
+ "-g",
+ "-Wall",
+ "-Werror",
+ "-std=gnu++11",
+ "-Wno-missing-field-initializers",
+ "-Wno-sign-compare",
+ ],
+ clang: true,
+}
diff --git a/libsync/Android.mk b/libsync/Android.mk
deleted file mode 100644
index f407bd1..0000000
--- a/libsync/Android.mk
+++ /dev/null
@@ -1,30 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := sync.c
-LOCAL_MODULE := libsync
-LOCAL_MODULE_TAGS := optional
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Werror
-include $(BUILD_SHARED_LIBRARY)
-
-# libsync_recovery is only intended for the recovery binary.
-# Future versions of the kernel WILL require an updated libsync, and will break
-# anything statically linked against the current libsync.
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := sync.c
-LOCAL_MODULE := libsync_recovery
-LOCAL_MODULE_TAGS := optional
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Werror
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := sync.c sync_test.c
-LOCAL_MODULE := sync_test
-LOCAL_MODULE_TAGS := optional tests
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Werror
-include $(BUILD_EXECUTABLE)
diff --git a/libsync/sync.c b/libsync/sync.c
index d73bb11..169dc36 100644
--- a/libsync/sync.c
+++ b/libsync/sync.c
@@ -21,13 +21,27 @@
#include <stdint.h>
#include <string.h>
-#include <linux/sync.h>
#include <linux/sw_sync.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <sync/sync.h>
+
+// The sync code is undergoing a major change. Add enough in to get
+// everything to compile wih the latest uapi headers.
+struct sync_merge_data {
+ int32_t fd2;
+ char name[32];
+ int32_t fence;
+};
+
+#define SYNC_IOC_MAGIC '>'
+#define SYNC_IOC_WAIT _IOW(SYNC_IOC_MAGIC, 0, __s32)
+#define SYNC_IOC_MERGE _IOWR(SYNC_IOC_MAGIC, 1, struct sync_merge_data)
+#define SYNC_IOC_FENCE_INFO _IOWR(SYNC_IOC_MAGIC, 2, struct sync_fence_info_data)
+
int sync_wait(int fd, int timeout)
{
__s32 to = timeout;
diff --git a/libsync/tests/Android.mk b/libsync/tests/Android.mk
deleted file mode 100644
index 8137c7a..0000000
--- a/libsync/tests/Android.mk
+++ /dev/null
@@ -1,29 +0,0 @@
-#
-# Copyright 2014 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.
-#
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_CLANG := true
-LOCAL_MODULE := sync-unit-tests
-LOCAL_CFLAGS += -g -Wall -Werror -std=gnu++11 -Wno-missing-field-initializers -Wno-sign-compare
-LOCAL_SHARED_LIBRARIES += libsync
-LOCAL_STATIC_LIBRARIES += libgtest_main
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/../include
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/..
-LOCAL_SRC_FILES := \
- sync_test.cpp
-include $(BUILD_NATIVE_TEST)
diff --git a/libutils/String16.cpp b/libutils/String16.cpp
index ac12d8a..9f5cfea 100644
--- a/libutils/String16.cpp
+++ b/libutils/String16.cpp
@@ -71,7 +71,7 @@
u8cur = (const uint8_t*) u8str;
char16_t* u16str = (char16_t*)buf->data();
- utf8_to_utf16(u8cur, u8len, u16str);
+ utf8_to_utf16(u8cur, u8len, u16str, ((size_t) u16len) + 1);
//printf("Created UTF-16 string from UTF-8 \"%s\":", in);
//printHexData(1, str, buf->size(), 16, 1);
diff --git a/libutils/String8.cpp b/libutils/String8.cpp
index ad45282..cacaf91 100644
--- a/libutils/String8.cpp
+++ b/libutils/String8.cpp
@@ -104,20 +104,21 @@
{
if (len == 0) return getEmptyString();
- const ssize_t bytes = utf16_to_utf8_length(in, len);
- if (bytes < 0) {
+ // Allow for closing '\0'
+ const ssize_t resultStrLen = utf16_to_utf8_length(in, len) + 1;
+ if (resultStrLen < 1) {
return getEmptyString();
}
- SharedBuffer* buf = SharedBuffer::alloc(bytes+1);
+ SharedBuffer* buf = SharedBuffer::alloc(resultStrLen);
ALOG_ASSERT(buf, "Unable to allocate shared buffer");
if (!buf) {
return getEmptyString();
}
- char* str = (char*)buf->data();
- utf16_to_utf8(in, len, str);
- return str;
+ char* resultStr = (char*)buf->data();
+ utf16_to_utf8(in, len, resultStr, resultStrLen);
+ return resultStr;
}
static char* allocFromUTF32(const char32_t* in, size_t len)
@@ -126,21 +127,21 @@
return getEmptyString();
}
- const ssize_t bytes = utf32_to_utf8_length(in, len);
- if (bytes < 0) {
+ const ssize_t resultStrLen = utf32_to_utf8_length(in, len) + 1;
+ if (resultStrLen < 1) {
return getEmptyString();
}
- SharedBuffer* buf = SharedBuffer::alloc(bytes+1);
+ SharedBuffer* buf = SharedBuffer::alloc(resultStrLen);
ALOG_ASSERT(buf, "Unable to allocate shared buffer");
if (!buf) {
return getEmptyString();
}
- char* str = (char*) buf->data();
- utf32_to_utf8(in, len, str);
+ char* resultStr = (char*) buf->data();
+ utf32_to_utf8(in, len, resultStr, resultStrLen);
- return str;
+ return resultStr;
}
// ---------------------------------------------------------------------------
diff --git a/libutils/Unicode.cpp b/libutils/Unicode.cpp
index f1f8bc9..5f96efa 100644
--- a/libutils/Unicode.cpp
+++ b/libutils/Unicode.cpp
@@ -14,8 +14,10 @@
* limitations under the License.
*/
+#include <log/log.h>
#include <utils/Unicode.h>
+#include <limits.h>
#include <stddef.h>
#if defined(_WIN32)
@@ -182,7 +184,7 @@
return ret;
}
-void utf32_to_utf8(const char32_t* src, size_t src_len, char* dst)
+void utf32_to_utf8(const char32_t* src, size_t src_len, char* dst, size_t dst_len)
{
if (src == NULL || src_len == 0 || dst == NULL) {
return;
@@ -193,9 +195,12 @@
char *cur = dst;
while (cur_utf32 < end_utf32) {
size_t len = utf32_codepoint_utf8_length(*cur_utf32);
+ LOG_ALWAYS_FATAL_IF(dst_len < len, "%zu < %zu", dst_len, len);
utf32_codepoint_to_utf8((uint8_t *)cur, *cur_utf32++, len);
cur += len;
+ dst_len -= len;
}
+ LOG_ALWAYS_FATAL_IF(dst_len < 1, "dst_len < 1: %zu < 1", dst_len);
*cur = '\0';
}
@@ -348,7 +353,7 @@
: 0);
}
-void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst)
+void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst, size_t dst_len)
{
if (src == NULL || src_len == 0 || dst == NULL) {
return;
@@ -369,9 +374,12 @@
utf32 = (char32_t) *cur_utf16++;
}
const size_t len = utf32_codepoint_utf8_length(utf32);
+ LOG_ALWAYS_FATAL_IF(dst_len < len, "%zu < %zu", dst_len, len);
utf32_codepoint_to_utf8((uint8_t*)cur, utf32, len);
cur += len;
+ dst_len -= len;
}
+ LOG_ALWAYS_FATAL_IF(dst_len < 1, "%zu < 1", dst_len);
*cur = '\0';
}
@@ -432,10 +440,10 @@
const char16_t* const end = src + src_len;
while (src < end) {
if ((*src & 0xFC00) == 0xD800 && (src + 1) < end
- && (*++src & 0xFC00) == 0xDC00) {
+ && (*(src + 1) & 0xFC00) == 0xDC00) {
// surrogate pairs are always 4 bytes.
ret += 4;
- src++;
+ src += 2;
} else {
ret += utf32_codepoint_utf8_length((char32_t) *src++);
}
@@ -535,7 +543,7 @@
//printf("Char at %p: len=%d, utf-16=%p\n", src, length, (void*)result);
}
-ssize_t utf8_to_utf16_length(const uint8_t* u8str, size_t u8len)
+ssize_t utf8_to_utf16_length(const uint8_t* u8str, size_t u8len, bool overreadIsFatal)
{
const uint8_t* const u8end = u8str + u8len;
const uint8_t* u8cur = u8str;
@@ -545,6 +553,20 @@
while (u8cur < u8end) {
u16measuredLen++;
int u8charLen = utf8_codepoint_len(*u8cur);
+ // Malformed utf8, some characters are beyond the end.
+ // Cases:
+ // If u8charLen == 1, this becomes u8cur >= u8end, which cannot happen as u8cur < u8end,
+ // then this condition fail and we continue, as expected.
+ // If u8charLen == 2, this becomes u8cur + 1 >= u8end, which fails only if
+ // u8cur == u8end - 1, that is, there was only one remaining character to read but we need
+ // 2 of them. This condition holds and we return -1, as expected.
+ if (u8cur + u8charLen - 1 >= u8end) {
+ if (overreadIsFatal) {
+ LOG_ALWAYS_FATAL("Attempt to overread computing length of utf8 string");
+ } else {
+ return -1;
+ }
+ }
uint32_t codepoint = utf8_to_utf32_codepoint(u8cur, u8charLen);
if (codepoint > 0xFFFF) u16measuredLen++; // this will be a surrogate pair in utf16
u8cur += u8charLen;
@@ -561,38 +583,21 @@
return u16measuredLen;
}
-char16_t* utf8_to_utf16_no_null_terminator(const uint8_t* u8str, size_t u8len, char16_t* u16str)
-{
- const uint8_t* const u8end = u8str + u8len;
- const uint8_t* u8cur = u8str;
- char16_t* u16cur = u16str;
-
- while (u8cur < u8end) {
- size_t u8len = utf8_codepoint_len(*u8cur);
- uint32_t codepoint = utf8_to_utf32_codepoint(u8cur, u8len);
-
- // Convert the UTF32 codepoint to one or more UTF16 codepoints
- if (codepoint <= 0xFFFF) {
- // Single UTF16 character
- *u16cur++ = (char16_t) codepoint;
- } else {
- // Multiple UTF16 characters with surrogates
- codepoint = codepoint - 0x10000;
- *u16cur++ = (char16_t) ((codepoint >> 10) + 0xD800);
- *u16cur++ = (char16_t) ((codepoint & 0x3FF) + 0xDC00);
- }
-
- u8cur += u8len;
- }
- return u16cur;
-}
-
-void utf8_to_utf16(const uint8_t* u8str, size_t u8len, char16_t* u16str) {
- char16_t* end = utf8_to_utf16_no_null_terminator(u8str, u8len, u16str);
+char16_t* utf8_to_utf16(const uint8_t* u8str, size_t u8len, char16_t* u16str, size_t u16len) {
+ // A value > SSIZE_MAX is probably a negative value returned as an error and casted.
+ LOG_ALWAYS_FATAL_IF(u16len == 0 || u16len > SSIZE_MAX, "u16len is %zu", u16len);
+ char16_t* end = utf8_to_utf16_no_null_terminator(u8str, u8len, u16str, u16len - 1);
*end = 0;
+ return end;
}
-char16_t* utf8_to_utf16_n(const uint8_t* src, size_t srcLen, char16_t* dst, size_t dstLen) {
+char16_t* utf8_to_utf16_no_null_terminator(
+ const uint8_t* src, size_t srcLen, char16_t* dst, size_t dstLen) {
+ if (dstLen == 0) {
+ return dst;
+ }
+ // A value > SSIZE_MAX is probably a negative value returned as an error and casted.
+ LOG_ALWAYS_FATAL_IF(dstLen > SSIZE_MAX, "dstLen is %zu", dstLen);
const uint8_t* const u8end = src + srcLen;
const uint8_t* u8cur = src;
const char16_t* const u16end = dst + dstLen;
diff --git a/libutils/tests/String8_test.cpp b/libutils/tests/String8_test.cpp
index 01e64f6..3947a5f 100644
--- a/libutils/tests/String8_test.cpp
+++ b/libutils/tests/String8_test.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "String8_test"
#include <utils/Log.h>
#include <utils/String8.h>
+#include <utils/String16.h>
#include <gtest/gtest.h>
@@ -77,4 +78,22 @@
EXPECT_EQ(NO_MEMORY, String8("").setTo(in, SIZE_MAX));
}
+// http://b/29250543
+TEST_F(String8Test, CorrectInvalidSurrogate) {
+ // d841d8 is an invalid start for a surrogate pair. Make sure this is handled by ignoring the
+ // first character in the pair and handling the rest correctly.
+ String16 string16(u"\xd841\xd841\xdc41\x0000");
+ String8 string8(string16);
+
+ EXPECT_EQ(4U, string8.length());
+}
+
+TEST_F(String8Test, CheckUtf32Conversion) {
+ // Since bound checks were added, check the conversion can be done without fatal errors.
+ // The utf8 lengths of these are chars are 1 + 2 + 3 + 4 = 10.
+ const char32_t string32[] = U"\x0000007f\x000007ff\x0000911\x0010fffe";
+ String8 string8(string32);
+ EXPECT_EQ(10U, string8.length());
+}
+
}
diff --git a/libutils/tests/Unicode_test.cpp b/libutils/tests/Unicode_test.cpp
index c263f75..d23e43a 100644
--- a/libutils/tests/Unicode_test.cpp
+++ b/libutils/tests/Unicode_test.cpp
@@ -98,7 +98,7 @@
char16_t output[1 + 1 + 1 + 2 + 1]; // Room for NULL
- utf8_to_utf16(str, sizeof(str), output);
+ utf8_to_utf16(str, sizeof(str), output, sizeof(output) / sizeof(output[0]));
EXPECT_EQ(0x0030, output[0])
<< "should be U+0030";
@@ -147,4 +147,15 @@
EXPECT_EQ(nullptr, result);
}
+// http://b/29267949
+// Test that overreading in utf8_to_utf16_length is detected
+TEST_F(UnicodeTest, InvalidUtf8OverreadDetected) {
+ // An utf8 char starting with \xc4 is two bytes long.
+ // Add extra zeros so no extra memory is read in case the code doesn't
+ // work as expected.
+ static char utf8[] = "\xc4\x00\x00\x00";
+ ASSERT_DEATH(utf8_to_utf16_length((uint8_t *) utf8, strlen(utf8),
+ true /* overreadIsFatal */), "" /* regex for ASSERT_DEATH */);
+}
+
}