Merge "adb: sysdeps: add support for joining threads."
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 58ccd0a..cb54d04 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -883,8 +883,6 @@
fprintf(stderr, "ADB server didn't ACK\n" );
return -1;
}
-
- setsid();
}
#endif /* !defined(_WIN32) */
return 0;
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index 6397c52..27b7109 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -127,10 +127,12 @@
setup_daemon_logging();
#if !defined(_WIN32)
- // Set the process group so that ctrl-c in the spawning process doesn't kill us.
- // Do this here instead of after the fork so that a ctrl-c between the "starting server" and
- // "done starting server" messages gets a chance to terminate the server.
- setpgrp();
+ // Start a new session for the daemon. Do this here instead of after the fork so
+ // that a ctrl-c between the "starting server" and "done starting server" messages
+ // gets a chance to terminate the server.
+ if (setsid() == -1) {
+ fatal("setsid() failed: %s", strerror(errno));
+ }
#endif
// Any error output written to stderr now goes to adb.log. We could
diff --git a/adb/sysdeps_win32_test.cpp b/adb/sysdeps_win32_test.cpp
index 8f610cf..c3a3fd7 100755
--- a/adb/sysdeps_win32_test.cpp
+++ b/adb/sysdeps_win32_test.cpp
@@ -87,8 +87,12 @@
TestAdbStrError(-1, "Unknown error");
// Test very big, positive unknown error.
TestAdbStrError(1000000, "Unknown error");
+
// Test success case.
- TestAdbStrError(0, "No error");
+ // Wine returns "Success" for strerror(0), Windows returns "No error", so accept both.
+ std::string success = adb_strerror(0);
+ EXPECT_TRUE(success == "Success" || success == "No error") << "strerror(0) = " << success;
+
// Test error that regular strerror() should have a string for.
TestAdbStrError(EPERM, "Operation not permitted");
// Test error that regular strerror() doesn't have a string for, but that
diff --git a/bootstat/Android.mk b/bootstat/Android.mk
index bbb903d..c6349c1 100644
--- a/bootstat/Android.mk
+++ b/bootstat/Android.mk
@@ -32,6 +32,7 @@
bootstat_shared_libs := \
libbase \
+ libcutils \
liblog
bootstat_cflags := \
diff --git a/bootstat/README.md b/bootstat/README.md
index 1b4bf7f..b3964ce 100644
--- a/bootstat/README.md
+++ b/bootstat/README.md
@@ -7,10 +7,11 @@
Usage: bootstat [options]
options include:
- -d Dump the boot event records to the console.
- -h Show this help.
- -l Log all metrics to logstorage.
- -r Record the relative time of a named boot event.
+ -h, --help Show this help
+ -l, --log Log all metrics to logstorage
+ -p, --print Dump the boot event records to the console
+ -r, --record Record the timestamp of a named boot event
+ --record_boot_reason Record the reason why the device booted
## Relative time ##
diff --git a/bootstat/boot_event_record_store.cpp b/bootstat/boot_event_record_store.cpp
index 1bdcf2b..22a67bb 100644
--- a/bootstat/boot_event_record_store.cpp
+++ b/bootstat/boot_event_record_store.cpp
@@ -56,19 +56,31 @@
LOG(ERROR) << "Failed to read /proc/uptime";
}
+ // Cast intentionally rounds down.
+ int32_t uptime = static_cast<int32_t>(strtod(uptime_str.c_str(), NULL));
+ AddBootEventWithValue(name, uptime);
+}
+
+// The implementation of AddBootEventValue makes use of the mtime file
+// attribute to store the value associated with a boot event in order to
+// optimize on-disk size requirements and small-file thrashing.
+void BootEventRecordStore::AddBootEventWithValue(
+ const std::string& name, int32_t value) {
std::string record_path = GetBootEventPath(name);
if (creat(record_path.c_str(), S_IRUSR | S_IWUSR) == -1) {
PLOG(ERROR) << "Failed to create " << record_path;
}
+ // Fill out the stat structure for |record_path| in order to get the atime to
+ // set in the utime() call.
struct stat file_stat;
if (stat(record_path.c_str(), &file_stat) == -1) {
PLOG(ERROR) << "Failed to read " << record_path;
}
- // Cast intentionally rounds down.
- time_t uptime = static_cast<time_t>(strtod(uptime_str.c_str(), NULL));
- struct utimbuf times = {file_stat.st_atime, uptime};
+ // Set the |modtime| of the file to store the value of the boot event while
+ // preserving the |actime| (as read by stat).
+ struct utimbuf times = {/* actime */ file_stat.st_atime, /* modtime */ value};
if (utime(record_path.c_str(), ×) == -1) {
PLOG(ERROR) << "Failed to set mtime for " << record_path;
}
diff --git a/bootstat/boot_event_record_store.h b/bootstat/boot_event_record_store.h
index 77978ef..d1b7835 100644
--- a/bootstat/boot_event_record_store.h
+++ b/bootstat/boot_event_record_store.h
@@ -37,6 +37,10 @@
// Persists the boot event named |name| in the record store.
void AddBootEvent(const std::string& name);
+ // Persists the boot event named |name| with the associated |value| in the
+ // record store.
+ void AddBootEventWithValue(const std::string& name, int32_t value);
+
// Returns a list of all of the boot events persisted in the record store.
std::vector<BootEventRecord> GetAllBootEvents() const;
@@ -45,6 +49,7 @@
// more test-friendly path.
FRIEND_TEST(BootEventRecordStoreTest, AddSingleBootEvent);
FRIEND_TEST(BootEventRecordStoreTest, AddMultipleBootEvents);
+ FRIEND_TEST(BootEventRecordStoreTest, AddBootEventWithValue);
// Sets the filesystem path of the record store.
void SetStorePath(const std::string& path);
diff --git a/bootstat/boot_event_record_store_test.cpp b/bootstat/boot_event_record_store_test.cpp
index 384f84d..3e6d706 100644
--- a/bootstat/boot_event_record_store_test.cpp
+++ b/bootstat/boot_event_record_store_test.cpp
@@ -154,3 +154,15 @@
EXPECT_TRUE(FuzzUptimeEquals(uptime, *i));
}
}
+
+TEST_F(BootEventRecordStoreTest, AddBootEventWithValue) {
+ BootEventRecordStore store;
+ store.SetStorePath(GetStorePathForTesting());
+
+ store.AddBootEventWithValue("permian", 42);
+
+ auto events = store.GetAllBootEvents();
+ ASSERT_EQ(1U, events.size());
+ EXPECT_EQ("permian", events[0].first);
+ EXPECT_EQ(42, events[0].second);
+}
\ No newline at end of file
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index a83f8ad..31c84c7 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -18,14 +18,15 @@
// timestamp, dump the persisted events, and log all events to EventLog to be
// uploaded to Android log storage via Tron.
-//#define LOG_TAG "bootstat"
-
+#include <getopt.h>
#include <unistd.h>
#include <cstddef>
#include <cstdio>
+#include <map>
#include <memory>
#include <string>
#include <android-base/logging.h>
+#include <cutils/properties.h>
#include <log/log.h>
#include "boot_event_record_store.h"
#include "event_log_list_builder.h"
@@ -35,7 +36,7 @@
// Builds an EventLog buffer named |event| containing |data| and writes
// the log into the Tron histogram logs.
void LogBootEvent(const std::string& event, int32_t data) {
- LOG(INFO) << "Logging boot time: " << event << " " << data;
+ LOG(INFO) << "Logging boot metric: " << event << " " << data;
EventLogListBuilder log_builder;
log_builder.Append(event);
@@ -74,10 +75,11 @@
fprintf(stderr, "Usage: %s [options]\n", cmd);
fprintf(stderr,
"options include:\n"
- " -d Dump the boot event records to the console.\n"
- " -h Show this help.\n"
- " -l Log all metrics to logstorage.\n"
- " -r Record the timestamp of a named boot event.\n");
+ " -h, --help Show this help\n"
+ " -l, --log Log all metrics to logstorage\n"
+ " -p, --print Dump the boot event records to the console\n"
+ " -r, --record Record the timestamp of a named boot event\n"
+ " --record_boot_reason Record the reason why the device booted\n");
}
// Constructs a readable, printable string from the givencommand line
@@ -92,6 +94,61 @@
return cmd;
}
+// Convenience wrapper over the property API that returns an
+// std::string.
+std::string GetProperty(const char* key) {
+ std::vector<char> temp(PROPERTY_VALUE_MAX);
+ const int len = property_get(key, &temp[0], nullptr);
+ if (len < 0) {
+ return "";
+ }
+ return std::string(&temp[0], len);
+}
+
+// A mapping from boot reason string, as read from the ro.boot.bootreason
+// system property, to a unique integer ID. Viewers of log data dashboards for
+// the boot_reason metric may refer to this mapping to discern the histogram
+// values.
+const std::map<std::string, int> kBootReasonMap = {
+ {"normal", 0},
+ {"recovery", 1},
+ {"reboot", 2},
+ {"PowerKey", 3},
+ {"hard_reset", 4},
+ {"kernel_panic", 5},
+ {"rpm_err", 6},
+ {"hw_reset", 7},
+ {"tz_err", 8},
+ {"adsp_err", 9},
+ {"modem_err", 10},
+ {"mba_err", 11},
+ {"Watchdog", 12},
+ {"Panic", 13},
+};
+
+// Converts a string value representing the reason the system booted to an
+// integer representation. This is necessary for logging the boot_reason metric
+// via Tron, which does not accept non-integer buckets in histograms.
+int32_t BootReasonStrToEnum(const std::string& boot_reason) {
+ static const int32_t kUnknownBootReason = -1;
+
+ auto mapping = kBootReasonMap.find(boot_reason);
+ if (mapping != kBootReasonMap.end()) {
+ return mapping->second;
+ }
+
+ LOG(INFO) << "Unknown boot reason: " << boot_reason;
+ return kUnknownBootReason;
+}
+
+// Records the boot_reason metric by querying the ro.boot.bootreason system
+// property.
+void RecordBootReason() {
+ int32_t boot_reason = BootReasonStrToEnum(GetProperty("ro.boot.bootreason"));
+ BootEventRecordStore boot_event_store;
+ boot_event_store.AddBootEventWithValue("boot_reason", boot_reason);
+}
+
} // namespace
int main(int argc, char **argv) {
@@ -100,9 +157,31 @@
const std::string cmd_line = GetCommandLine(argc, argv);
LOG(INFO) << "Service started: " << cmd_line;
+ int option_index = 0;
+ static const char boot_reason_str[] = "record_boot_reason";
+ static const struct option long_options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "log", no_argument, NULL, 'l' },
+ { "print", no_argument, NULL, 'p' },
+ { "record", required_argument, NULL, 'r' },
+ { boot_reason_str, no_argument, NULL, 0 },
+ { NULL, 0, NULL, 0 }
+ };
+
int opt = 0;
- while ((opt = getopt(argc, argv, "hlpr:")) != -1) {
+ while ((opt = getopt_long(argc, argv, "hlpr:", long_options, &option_index)) != -1) {
switch (opt) {
+ // This case handles long options which have no single-character mapping.
+ case 0: {
+ const std::string option_name = long_options[option_index].name;
+ if (option_name == boot_reason_str) {
+ RecordBootReason();
+ } else {
+ LOG(ERROR) << "Invalid option: " << option_name;
+ }
+ break;
+ }
+
case 'h': {
ShowHelp(argv[0]);
break;
diff --git a/bootstat/bootstat.rc b/bootstat/bootstat.rc
index 2c37dd2..218b9f8 100644
--- a/bootstat/bootstat.rc
+++ b/bootstat/bootstat.rc
@@ -10,5 +10,8 @@
# Record boot_complete timing event.
exec - root root -- /system/bin/bootstat -r boot_complete
+ # Record the boot reason.
+ exec - root root -- /system/bin/bootstat --record_boot_reason
+
# Log all boot events.
exec - root root -- /system/bin/bootstat -l
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index e0f7c73..a326f55 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -32,6 +32,7 @@
protocol.cpp \
socket.cpp \
tcp.cpp \
+ udp.cpp \
util.cpp \
LOCAL_MODULE := fastboot
@@ -114,6 +115,8 @@
socket_test.cpp \
tcp.cpp \
tcp_test.cpp \
+ udp.cpp \
+ udp_test.cpp \
LOCAL_STATIC_LIBRARIES := libbase libcutils
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index b219564..7e60a72 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -59,6 +59,7 @@
#include "fs.h"
#include "tcp.h"
#include "transport.h"
+#include "udp.h"
#include "usb.h"
#ifndef O_BINARY
@@ -243,22 +244,41 @@
return transport;
}
+ Socket::Protocol protocol = Socket::Protocol::kTcp;
std::string host;
- int port = tcp::kDefaultPort;
- if (serial != nullptr && android::base::StartsWith(serial, "tcp:")) {
- std::string error;
- const char* address = serial + strlen("tcp:");
+ int port = 0;
+ if (serial != nullptr) {
+ const char* net_address = nullptr;
- if (!android::base::ParseNetAddress(address, &host, &port, nullptr, &error)) {
- fprintf(stderr, "error: Invalid network address '%s': %s\n", address, error.c_str());
- return nullptr;
+ if (android::base::StartsWith(serial, "tcp:")) {
+ protocol = Socket::Protocol::kTcp;
+ port = tcp::kDefaultPort;
+ net_address = serial + strlen("tcp:");
+ } else if (android::base::StartsWith(serial, "udp:")) {
+ protocol = Socket::Protocol::kUdp;
+ port = udp::kDefaultPort;
+ net_address = serial + strlen("udp:");
+ }
+
+ if (net_address != nullptr) {
+ std::string error;
+ if (!android::base::ParseNetAddress(net_address, &host, &port, nullptr, &error)) {
+ fprintf(stderr, "error: Invalid network address '%s': %s\n", net_address,
+ error.c_str());
+ return nullptr;
+ }
}
}
while (true) {
if (!host.empty()) {
std::string error;
- transport = tcp::Connect(host, port, &error).release();
+ if (protocol == Socket::Protocol::kTcp) {
+ transport = tcp::Connect(host, port, &error).release();
+ } else if (protocol == Socket::Protocol::kUdp) {
+ transport = udp::Connect(host, port, &error).release();
+ }
+
if (transport == nullptr && announce) {
fprintf(stderr, "error: %s\n", error.c_str());
}
@@ -335,8 +355,9 @@
" formatting.\n"
" -s <specific device> Specify a device. For USB, provide either\n"
" a serial number or path to device port.\n"
- " For TCP, provide an address in the form\n"
- " tcp:<hostname>[:port].\n"
+ " For ethernet, provide an address in the"
+ " form <protocol>:<hostname>[:port] where"
+ " <protocol> is either tcp or udp.\n"
" -p <product> Specify product name.\n"
" -c <cmdline> Override kernel commandline.\n"
" -i <vendor id> Specify a custom USB vendor id.\n"
diff --git a/fastboot/fastboot_protocol.txt b/fastboot/fastboot_protocol.txt
index 4aa48b1..2801703 100644
--- a/fastboot/fastboot_protocol.txt
+++ b/fastboot/fastboot_protocol.txt
@@ -17,9 +17,9 @@
* The protocol is entirely host-driven and synchronous (unlike the
multi-channel, bi-directional, asynchronous ADB protocol)
-* TCP
+* TCP or UDP
* Device must be reachable via IP.
- * Device will act as the TCP server, fastboot will be the client.
+ * Device will act as the server, fastboot will be the client.
* Fastboot data is wrapped in a simple protocol; see below for details.
@@ -217,3 +217,226 @@
Host [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x0B]getvar:none
Device [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x04]OKAY
Host <disconnect>
+
+
+UDP Protocol v1
+---------------
+
+The UDP protocol is more complex than TCP since we must implement reliability
+to ensure no packets are lost, but the general concept of wrapping the fastboot
+protocol is the same.
+
+Overview:
+ 1. As with TCP, the device will listen on UDP port 5554.
+ 2. Maximum UDP packet size is negotiated during initialization.
+ 3. The host drives all communication; the device may only send a packet as a
+ response to a host packet.
+ 4. If the host does not receive a response in 500ms it will re-transmit.
+
+-- UDP Packet format --
+ +----------+----+-------+-------+--------------------+
+ | Byte # | 0 | 1 | 2 - 3 | 4+ |
+ +----------+----+-------+-------+--------------------+
+ | Contents | ID | Flags | Seq # | Data |
+ +----------+----+-------+-------+--------------------+
+
+ ID Packet ID:
+ 0x00: Error.
+ 0x01: Query.
+ 0x02: Initialization.
+ 0x03: Fastboot.
+
+ Packet types are described in more detail below.
+
+ Flags Packet flags: 0 0 0 0 0 0 0 C
+ C=1 indicates a continuation packet; the data is too large and will
+ continue in the next packet.
+
+ Remaining bits are reserved for future use and must be set to 0.
+
+ Seq # 2-byte packet sequence number (big-endian). The host will increment
+ this by 1 with each new packet, and the device must provide the
+ corresponding sequence number in the response packets.
+
+ Data Packet data, not present in all packets.
+
+-- Packet Types --
+Query The host sends a query packet once on startup to sync with the device.
+ The host will not know the current sequence number, so the device must
+ respond to all query packets regardless of sequence number.
+
+ The response data field should contain a 2-byte big-endian value
+ giving the next expected sequence number.
+
+Init The host sends an init packet once the query response is returned. The
+ device must abort any in-progress operation and prepare for a new
+ fastboot session. This message is meant to allow recovery if a
+ previous session failed, e.g. due to network error or user Ctrl+C.
+
+ The data field contains two big-endian 2-byte values, a protocol
+ version and the max UDP packet size (including the 4-byte header).
+ Both the host and device will send these values, and in each case
+ the minimum of the sent values must be used.
+
+Fastboot These packets wrap the fastboot protocol. To write, the host will
+ send a packet with fastboot data, and the device will reply with an
+ empty packet as an ACK. To read, the host will send an empty packet,
+ and the device will reply with fastboot data. The device may not give
+ any data in the ACK packet.
+
+Error The device may respond to any packet with an error packet to indicate
+ a UDP protocol error. The data field should contain an ASCII string
+ describing the error. This is the only case where a device is allowed
+ to return a packet ID other than the one sent by the host.
+
+-- Packet Size --
+The maximum packet size is negotiated by the host and device in the Init packet.
+Devices must support at least 512-byte packets, but packet size has a direct
+correlation with download speed, so devices are strongly suggested to support at
+least 1024-byte packets. On a local network with 0.5ms round-trip time this will
+provide transfer rates of ~2MB/s. Over WiFi it will likely be significantly
+less.
+
+Query and Initialization packets, which are sent before size negotiation is
+complete, must always be 512 bytes or less.
+
+-- Packet Re-Transmission --
+The host will re-transmit any packet that does not receive a response. The
+requirement of exactly one device response packet per host packet is how we
+achieve reliability and in-order delivery of packets.
+
+For simplicity of implementation, there is no windowing of multiple
+unacknowledged packets in this version of the protocol. The host will continue
+to send the same packet until a response is received. Windowing functionality
+may be implemented in future versions if necessary to increase performance.
+
+The first Query packet will only be attempted a small number of times, but
+subsequent packets will attempt to retransmit for at least 1 minute before
+giving up. This means a device may safely ignore host UDP packets for up to 1
+minute during long operations, e.g. writing to flash.
+
+-- Continuation Packets --
+Any packet may set the continuation flag to indicate that the data is
+incomplete. Large data such as downloading an image may require many
+continuation packets. The receiver should respond to a continuation packet with
+an empty packet to acknowledge receipt. See examples below.
+
+-- Summary --
+The host starts with a Query packet, then an Initialization packet, after
+which only Fastboot packets are sent. Fastboot packets may contain data from
+the host for writes, or from the device for reads, but not both.
+
+Given a next expected sequence number S and a received packet P, the device
+behavior should be:
+ if P is a Query packet:
+ * respond with a Query packet with S in the data field
+ else if P has sequence == S:
+ * process P and take any required action
+ * create a response packet R with the same ID and sequence as P, containing
+ any response data required.
+ * transmit R and save it in case of re-transmission
+ * increment S
+ else if P has sequence == S - 1:
+ * re-transmit the saved response packet R from above
+ else:
+ * ignore the packet
+
+-- Examples --
+In the examples below, S indicates the starting client sequence number.
+
+Host Client
+======================================================================
+[Initialization, S = 0x55AA]
+[Host: version 1, 2048-byte packets. Client: version 2, 1024-byte packets.]
+[Resulting values to use: version = 1, max packet size = 1024]
+ID Flag SeqH SeqL Data ID Flag SeqH SeqL Data
+----------------------------------------------------------------------
+0x01 0x00 0x00 0x00
+ 0x01 0x00 0x00 0x00 0x55 0xAA
+0x02 0x00 0x55 0xAA 0x00 0x01 0x08 0x00
+ 0x02 0x00 0x55 0xAA 0x00 0x02 0x04 0x00
+
+----------------------------------------------------------------------
+[fastboot "getvar" commands, S = 0x0001]
+ID Flags SeqH SeqL Data ID Flags SeqH SeqL Data
+----------------------------------------------------------------------
+0x03 0x00 0x00 0x01 getvar:version
+ 0x03 0x00 0x00 0x01
+0x03 0x00 0x00 0x02
+ 0x03 0x00 0x00 0x02 OKAY0.4
+0x03 0x00 0x00 0x03 getvar:foo
+ 0x03 0x00 0x00 0x03
+0x03 0x00 0x00 0x04
+ 0x03 0x00 0x00 0x04 OKAY
+
+----------------------------------------------------------------------
+[fastboot "INFO" responses, S = 0x0000]
+ID Flags SeqH SeqL Data ID Flags SeqH SeqL Data
+----------------------------------------------------------------------
+0x03 0x00 0x00 0x00 <command>
+ 0x03 0x00 0x00 0x00
+0x03 0x00 0x00 0x01
+ 0x03 0x00 0x00 0x01 INFOWait1
+0x03 0x00 0x00 0x02
+ 0x03 0x00 0x00 0x02 INFOWait2
+0x03 0x00 0x00 0x03
+ 0x03 0x00 0x00 0x03 OKAY
+
+----------------------------------------------------------------------
+[Chunking 2100 bytes of data, max packet size = 1024, S = 0xFFFF]
+ID Flag SeqH SeqL Data ID Flag SeqH SeqL Data
+----------------------------------------------------------------------
+0x03 0x00 0xFF 0xFF download:0000834
+ 0x03 0x00 0xFF 0xFF
+0x03 0x00 0x00 0x00
+ 0x03 0x00 0x00 0x00 DATA0000834
+0x03 0x01 0x00 0x01 <1020 bytes>
+ 0x03 0x00 0x00 0x01
+0x03 0x01 0x00 0x02 <1020 bytes>
+ 0x03 0x00 0x00 0x02
+0x03 0x00 0x00 0x03 <60 bytes>
+ 0x03 0x00 0x00 0x03
+0x03 0x00 0x00 0x04
+ 0x03 0x00 0x00 0x04 OKAY
+
+----------------------------------------------------------------------
+[Unknown ID error, S = 0x0000]
+ID Flags SeqH SeqL Data ID Flags SeqH SeqL Data
+----------------------------------------------------------------------
+0x10 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 <error message>
+
+----------------------------------------------------------------------
+[Host packet loss and retransmission, S = 0x0000]
+ID Flags SeqH SeqL Data ID Flags SeqH SeqL Data
+----------------------------------------------------------------------
+0x03 0x00 0x00 0x00 getvar:version [lost]
+0x03 0x00 0x00 0x00 getvar:version [lost]
+0x03 0x00 0x00 0x00 getvar:version
+ 0x03 0x00 0x00 0x00
+0x03 0x00 0x00 0x01
+ 0x03 0x00 0x00 0x01 OKAY0.4
+
+----------------------------------------------------------------------
+[Client packet loss and retransmission, S = 0x0000]
+ID Flags SeqH SeqL Data ID Flags SeqH SeqL Data
+----------------------------------------------------------------------
+0x03 0x00 0x00 0x00 getvar:version
+ 0x03 0x00 0x00 0x00 [lost]
+0x03 0x00 0x00 0x00 getvar:version
+ 0x03 0x00 0x00 0x00 [lost]
+0x03 0x00 0x00 0x00 getvar:version
+ 0x03 0x00 0x00 0x00
+0x03 0x00 0x00 0x01
+ 0x03 0x00 0x00 0x01 OKAY0.4
+
+----------------------------------------------------------------------
+[Host packet delayed, S = 0x0000]
+ID Flags SeqH SeqL Data ID Flags SeqH SeqL Data
+----------------------------------------------------------------------
+0x03 0x00 0x00 0x00 getvar:version [delayed]
+0x03 0x00 0x00 0x00 getvar:version
+ 0x03 0x00 0x00 0x00
+0x03 0x00 0x00 0x01
+ 0x03 0x00 0x00 0x01 OKAY0.4
+0x03 0x00 0x00 0x00 getvar:version [arrives late with old seq#, is ignored]
diff --git a/fastboot/udp.cpp b/fastboot/udp.cpp
new file mode 100644
index 0000000..b36bd60
--- /dev/null
+++ b/fastboot/udp.cpp
@@ -0,0 +1,391 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+// This file implements the fastboot UDP protocol; see fastboot_protocol.txt for documentation.
+
+#include "udp.h"
+
+#include <errno.h>
+#include <stdio.h>
+
+#include <list>
+#include <memory>
+#include <vector>
+
+#include <android-base/macros.h>
+#include <android-base/stringprintf.h>
+
+#include "socket.h"
+
+namespace udp {
+
+using namespace internal;
+
+constexpr size_t kMinPacketSize = 512;
+constexpr size_t kHeaderSize = 4;
+
+enum Index {
+ kIndexId = 0,
+ kIndexFlags = 1,
+ kIndexSeqH = 2,
+ kIndexSeqL = 3,
+};
+
+// Extracts a big-endian uint16_t from a byte array.
+static uint16_t ExtractUint16(const uint8_t* bytes) {
+ return (static_cast<uint16_t>(bytes[0]) << 8) | bytes[1];
+}
+
+// Packet header handling.
+class Header {
+ public:
+ Header();
+ ~Header() = default;
+
+ uint8_t id() const { return bytes_[kIndexId]; }
+ const uint8_t* bytes() const { return bytes_; }
+
+ void Set(uint8_t id, uint16_t sequence, Flag flag);
+
+ // Checks whether |response| is a match for this header.
+ bool Matches(const uint8_t* response);
+
+ private:
+ uint8_t bytes_[kHeaderSize];
+};
+
+Header::Header() {
+ Set(kIdError, 0, kFlagNone);
+}
+
+void Header::Set(uint8_t id, uint16_t sequence, Flag flag) {
+ bytes_[kIndexId] = id;
+ bytes_[kIndexFlags] = flag;
+ bytes_[kIndexSeqH] = sequence >> 8;
+ bytes_[kIndexSeqL] = sequence;
+}
+
+bool Header::Matches(const uint8_t* response) {
+ // Sequence numbers must be the same to match, but the response ID can either be the same
+ // or an error response which is always accepted.
+ return bytes_[kIndexSeqH] == response[kIndexSeqH] &&
+ bytes_[kIndexSeqL] == response[kIndexSeqL] &&
+ (bytes_[kIndexId] == response[kIndexId] || response[kIndexId] == kIdError);
+}
+
+// Implements the Transport interface to work with the fastboot engine.
+class UdpTransport : public Transport {
+ public:
+ // Factory function so we can return nullptr if initialization fails.
+ static std::unique_ptr<UdpTransport> NewTransport(std::unique_ptr<Socket> socket,
+ std::string* error);
+ ~UdpTransport() override = default;
+
+ ssize_t Read(void* data, size_t length) override;
+ ssize_t Write(const void* data, size_t length) override;
+ int Close() override;
+
+ private:
+ UdpTransport(std::unique_ptr<Socket> socket) : socket_(std::move(socket)) {}
+
+ // Performs the UDP initialization procedure. Returns true on success.
+ bool InitializeProtocol(std::string* error);
+
+ // Sends |length| bytes from |data| and waits for the response packet up to |attempts| times.
+ // Continuation packets are handled automatically and any return data is written to |rx_data|.
+ // Excess bytes that cannot fit in |rx_data| are dropped.
+ // On success, returns the number of response data bytes received, which may be greater than
+ // |rx_length|. On failure, returns -1 and fills |error| on failure.
+ ssize_t SendData(Id id, const uint8_t* tx_data, size_t tx_length, uint8_t* rx_data,
+ size_t rx_length, int attempts, std::string* error);
+
+ // Helper for SendData(); sends a single packet and handles the response. |header| specifies
+ // the initial outgoing packet information but may be modified by this function.
+ ssize_t SendSinglePacketHelper(Header* header, const uint8_t* tx_data, size_t tx_length,
+ uint8_t* rx_data, size_t rx_length, int attempts,
+ std::string* error);
+
+ std::unique_ptr<Socket> socket_;
+ int sequence_ = -1;
+ size_t max_data_length_ = kMinPacketSize - kHeaderSize;
+ std::vector<uint8_t> rx_packet_;
+
+ DISALLOW_COPY_AND_ASSIGN(UdpTransport);
+};
+
+std::unique_ptr<UdpTransport> UdpTransport::NewTransport(std::unique_ptr<Socket> socket,
+ std::string* error) {
+ std::unique_ptr<UdpTransport> transport(new UdpTransport(std::move(socket)));
+
+ if (!transport->InitializeProtocol(error)) {
+ return nullptr;
+ }
+
+ return transport;
+}
+
+bool UdpTransport::InitializeProtocol(std::string* error) {
+ uint8_t rx_data[4];
+
+ sequence_ = 0;
+ rx_packet_.resize(kMinPacketSize);
+
+ // First send the query packet to sync with the target. Only attempt this a small number of
+ // times so we can fail out quickly if the target isn't available.
+ ssize_t rx_bytes = SendData(kIdDeviceQuery, nullptr, 0, rx_data, sizeof(rx_data),
+ kMaxConnectAttempts, error);
+ if (rx_bytes == -1) {
+ return false;
+ } else if (rx_bytes < 2) {
+ *error = "invalid query response from target";
+ return false;
+ }
+ // The first two bytes contain the next expected sequence number.
+ sequence_ = ExtractUint16(rx_data);
+
+ // Now send the initialization packet with our version and maximum packet size.
+ uint8_t init_data[] = {kProtocolVersion >> 8, kProtocolVersion & 0xFF,
+ kHostMaxPacketSize >> 8, kHostMaxPacketSize & 0xFF};
+ rx_bytes = SendData(kIdInitialization, init_data, sizeof(init_data), rx_data, sizeof(rx_data),
+ kMaxTransmissionAttempts, error);
+ if (rx_bytes == -1) {
+ return false;
+ } else if (rx_bytes < 4) {
+ *error = "invalid initialization response from target";
+ return false;
+ }
+
+ // The first two data bytes contain the version, the second two bytes contain the target max
+ // supported packet size, which must be at least 512 bytes.
+ uint16_t version = ExtractUint16(rx_data);
+ if (version < kProtocolVersion) {
+ *error = android::base::StringPrintf("target reported invalid protocol version %d",
+ version);
+ return false;
+ }
+ uint16_t packet_size = ExtractUint16(rx_data + 2);
+ if (packet_size < kMinPacketSize) {
+ *error = android::base::StringPrintf("target reported invalid packet size %d", packet_size);
+ return false;
+ }
+
+ packet_size = std::min(kHostMaxPacketSize, packet_size);
+ max_data_length_ = packet_size - kHeaderSize;
+ rx_packet_.resize(packet_size);
+
+ return true;
+}
+
+// SendData() is just responsible for chunking |data| into packets until it's all been sent.
+// Per-packet timeout/retransmission logic is done in SendSinglePacketHelper().
+ssize_t UdpTransport::SendData(Id id, const uint8_t* tx_data, size_t tx_length, uint8_t* rx_data,
+ size_t rx_length, int attempts, std::string* error) {
+ if (socket_ == nullptr) {
+ *error = "socket is closed";
+ return -1;
+ }
+
+ Header header;
+ size_t packet_data_length;
+ ssize_t ret = 0;
+ // We often send header-only packets with no data as part of the protocol, so always send at
+ // least once even if |length| == 0, then repeat until we've sent all of |data|.
+ do {
+ // Set the continuation flag and truncate packet data if needed.
+ if (tx_length > max_data_length_) {
+ packet_data_length = max_data_length_;
+ header.Set(id, sequence_, kFlagContinuation);
+ } else {
+ packet_data_length = tx_length;
+ header.Set(id, sequence_, kFlagNone);
+ }
+
+ ssize_t bytes = SendSinglePacketHelper(&header, tx_data, packet_data_length, rx_data,
+ rx_length, attempts, error);
+
+ // Advance our read and write buffers for the next packet. Keep going even if we run out
+ // of receive buffer space so we can detect overflows.
+ if (bytes == -1) {
+ return -1;
+ } else if (static_cast<size_t>(bytes) < rx_length) {
+ rx_data += bytes;
+ rx_length -= bytes;
+ } else {
+ rx_data = nullptr;
+ rx_length = 0;
+ }
+
+ tx_length -= packet_data_length;
+ tx_data += packet_data_length;
+
+ ret += bytes;
+ } while (tx_length > 0);
+
+ return ret;
+}
+
+ssize_t UdpTransport::SendSinglePacketHelper(
+ Header* header, const uint8_t* tx_data, size_t tx_length, uint8_t* rx_data,
+ size_t rx_length, const int attempts, std::string* error) {
+ ssize_t total_data_bytes = 0;
+ error->clear();
+
+ int attempts_left = attempts;
+ while (attempts_left > 0) {
+ if (!socket_->Send({{header->bytes(), kHeaderSize}, {tx_data, tx_length}})) {
+ *error = Socket::GetErrorMessage();
+ return -1;
+ }
+
+ // Keep receiving until we get a matching response or we timeout.
+ ssize_t bytes = 0;
+ do {
+ bytes = socket_->Receive(rx_packet_.data(), rx_packet_.size(), kResponseTimeoutMs);
+ if (bytes == -1) {
+ if (socket_->ReceiveTimedOut()) {
+ break;
+ }
+ *error = Socket::GetErrorMessage();
+ return -1;
+ } else if (bytes < static_cast<ssize_t>(kHeaderSize)) {
+ *error = "protocol error: incomplete header";
+ return -1;
+ }
+ } while (!header->Matches(rx_packet_.data()));
+
+ if (socket_->ReceiveTimedOut()) {
+ --attempts_left;
+ continue;
+ }
+ ++sequence_;
+
+ // Save to |error| or |rx_data| as appropriate.
+ if (rx_packet_[kIndexId] == kIdError) {
+ error->append(rx_packet_.data() + kHeaderSize, rx_packet_.data() + bytes);
+ } else {
+ total_data_bytes += bytes - kHeaderSize;
+ size_t rx_data_bytes = std::min<size_t>(bytes - kHeaderSize, rx_length);
+ if (rx_data_bytes > 0) {
+ memcpy(rx_data, rx_packet_.data() + kHeaderSize, rx_data_bytes);
+ rx_data += rx_data_bytes;
+ rx_length -= rx_data_bytes;
+ }
+ }
+
+ // If the response has a continuation flag we need to prompt for more data by sending
+ // an empty packet.
+ if (rx_packet_[kIndexFlags] & kFlagContinuation) {
+ // We got a valid response so reset our attempt counter.
+ attempts_left = attempts;
+ header->Set(rx_packet_[kIndexId], sequence_, kFlagNone);
+ tx_data = nullptr;
+ tx_length = 0;
+ continue;
+ }
+
+ break;
+ }
+
+ if (attempts_left <= 0) {
+ *error = "no response from target";
+ return -1;
+ }
+
+ if (rx_packet_[kIndexId] == kIdError) {
+ *error = "target reported error: " + *error;
+ return -1;
+ }
+
+ return total_data_bytes;
+}
+
+ssize_t UdpTransport::Read(void* data, size_t length) {
+ // Read from the target by sending an empty packet.
+ std::string error;
+ ssize_t bytes = SendData(kIdFastboot, nullptr, 0, reinterpret_cast<uint8_t*>(data), length,
+ kMaxTransmissionAttempts, &error);
+
+ if (bytes == -1) {
+ fprintf(stderr, "UDP error: %s\n", error.c_str());
+ return -1;
+ } else if (static_cast<size_t>(bytes) > length) {
+ // Fastboot protocol error: the target sent more data than our fastboot engine was prepared
+ // to receive.
+ fprintf(stderr, "UDP error: receive overflow, target sent too much fastboot data\n");
+ return -1;
+ }
+
+ return bytes;
+}
+
+ssize_t UdpTransport::Write(const void* data, size_t length) {
+ std::string error;
+ ssize_t bytes = SendData(kIdFastboot, reinterpret_cast<const uint8_t*>(data), length, nullptr,
+ 0, kMaxTransmissionAttempts, &error);
+
+ if (bytes == -1) {
+ fprintf(stderr, "UDP error: %s\n", error.c_str());
+ return -1;
+ } else if (bytes > 0) {
+ // UDP protocol error: only empty ACK packets are allowed when writing to a device.
+ fprintf(stderr, "UDP error: target sent fastboot data out-of-turn\n");
+ return -1;
+ }
+
+ return length;
+}
+
+int UdpTransport::Close() {
+ if (socket_ == nullptr) {
+ return 0;
+ }
+
+ int result = socket_->Close();
+ socket_.reset();
+ return result;
+}
+
+std::unique_ptr<Transport> Connect(const std::string& hostname, int port, std::string* error) {
+ return internal::Connect(Socket::NewClient(Socket::Protocol::kUdp, hostname, port, error),
+ error);
+}
+
+namespace internal {
+
+std::unique_ptr<Transport> Connect(std::unique_ptr<Socket> sock, std::string* error) {
+ if (sock == nullptr) {
+ // If Socket creation failed |error| is already set.
+ return nullptr;
+ }
+
+ return UdpTransport::NewTransport(std::move(sock), error);
+}
+
+} // namespace internal
+
+} // namespace udp
diff --git a/fastboot/udp.h b/fastboot/udp.h
new file mode 100644
index 0000000..14f5b35
--- /dev/null
+++ b/fastboot/udp.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef UDP_H_
+#define UDP_H_
+
+#include <memory>
+#include <string>
+
+#include "socket.h"
+#include "transport.h"
+
+namespace udp {
+
+constexpr int kDefaultPort = 5554;
+
+// Returns a newly allocated Transport object connected to |hostname|:|port|. On failure, |error| is
+// filled and nullptr is returned.
+std::unique_ptr<Transport> Connect(const std::string& hostname, int port, std::string* error);
+
+// Internal namespace for test use only.
+namespace internal {
+
+constexpr uint16_t kProtocolVersion = 1;
+
+// This will be negotiated with the device so may end up being smaller.
+constexpr uint16_t kHostMaxPacketSize = 8192;
+
+// Retransmission constants. Retransmission timeout must be at least 500ms, and the host must
+// attempt to send packets for at least 1 minute once the device has connected. See
+// fastboot_protocol.txt for more information.
+constexpr int kResponseTimeoutMs = 500;
+constexpr int kMaxConnectAttempts = 4;
+constexpr int kMaxTransmissionAttempts = 60 * 1000 / kResponseTimeoutMs;
+
+enum Id : uint8_t {
+ kIdError = 0x00,
+ kIdDeviceQuery = 0x01,
+ kIdInitialization = 0x02,
+ kIdFastboot = 0x03
+};
+
+enum Flag : uint8_t {
+ kFlagNone = 0x00,
+ kFlagContinuation = 0x01
+};
+
+// Creates a UDP Transport object using a given Socket. Used for unit tests to create a Transport
+// object that uses a SocketMock.
+std::unique_ptr<Transport> Connect(std::unique_ptr<Socket> sock, std::string* error);
+
+} // namespace internal
+
+} // namespace udp
+
+#endif // UDP_H_
diff --git a/fastboot/udp_test.cpp b/fastboot/udp_test.cpp
new file mode 100644
index 0000000..ff8cf0f
--- /dev/null
+++ b/fastboot/udp_test.cpp
@@ -0,0 +1,531 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "udp.h"
+
+#include <gtest/gtest.h>
+
+#include "socket.h"
+#include "socket_mock.h"
+
+using namespace udp;
+using namespace udp::internal;
+
+// Some possible corner case sequence numbers we want to check.
+static const uint16_t kTestSequenceNumbers[] = {0x0000, 0x0001, 0x00FF, 0x0100,
+ 0x7FFF, 0x8000, 0xFFFF};
+
+// Converts |value| to a binary big-endian string.
+static std::string PacketValue(uint16_t value) {
+ return std::string{static_cast<char>(value >> 8), static_cast<char>(value)};
+}
+
+// Returns an Error packet.
+static std::string ErrorPacket(uint16_t sequence, const std::string& message = "",
+ char flags = kFlagNone) {
+ return std::string{kIdError, flags} + PacketValue(sequence) + message;
+}
+
+// Returns a Query packet with no data.
+static std::string QueryPacket(uint16_t sequence) {
+ return std::string{kIdDeviceQuery, kFlagNone} + PacketValue(sequence);
+}
+
+// Returns a Query packet with a 2-byte |new_sequence|.
+static std::string QueryPacket(uint16_t sequence, uint16_t new_sequence) {
+ return std::string{kIdDeviceQuery, kFlagNone} + PacketValue(sequence) +
+ PacketValue(new_sequence);
+}
+
+// Returns an Init packet with a 2-byte |version| and |max_packet_size|.
+static std::string InitPacket(uint16_t sequence, uint16_t version, uint16_t max_packet_size) {
+ return std::string{kIdInitialization, kFlagNone} + PacketValue(sequence) +
+ PacketValue(version) + PacketValue(max_packet_size);
+}
+
+// Returns a Fastboot packet with |data|.
+static std::string FastbootPacket(uint16_t sequence, const std::string& data = "",
+ char flags = kFlagNone) {
+ return std::string{kIdFastboot, flags} + PacketValue(sequence) + data;
+}
+
+// Fixture class to test protocol initialization. Usage is to set up the expected calls to the
+// SocketMock object then call UdpConnect() and check the result.
+class UdpConnectTest : public ::testing::Test {
+ public:
+ UdpConnectTest() : mock_socket_(new SocketMock) {}
+
+ // Run the initialization, return whether it was successful or not. This passes ownership of
+ // the current |mock_socket_| but allocates a new one for re-use.
+ bool UdpConnect(std::string* error = nullptr) {
+ std::string local_error;
+ if (error == nullptr) {
+ error = &local_error;
+ }
+ std::unique_ptr<Transport> transport(Connect(std::move(mock_socket_), error));
+ mock_socket_.reset(new SocketMock);
+ return transport != nullptr && error->empty();
+ }
+
+ protected:
+ std::unique_ptr<SocketMock> mock_socket_;
+};
+
+// Tests a successful protocol initialization with various starting sequence numbers.
+TEST_F(UdpConnectTest, InitializationSuccess) {
+ for (uint16_t seq : kTestSequenceNumbers) {
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(QueryPacket(0, seq));
+ mock_socket_->ExpectSend(InitPacket(seq, kProtocolVersion, kHostMaxPacketSize));
+ mock_socket_->AddReceive(InitPacket(seq, kProtocolVersion, 1024));
+
+ EXPECT_TRUE(UdpConnect());
+ }
+}
+
+// Tests continuation packets during initialization.
+TEST_F(UdpConnectTest, InitializationContinuationSuccess) {
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(std::string{kIdDeviceQuery, kFlagContinuation, 0, 0, 0x44});
+ mock_socket_->ExpectSend(std::string{kIdDeviceQuery, kFlagNone, 0, 1});
+ mock_socket_->AddReceive(std::string{kIdDeviceQuery, kFlagNone, 0, 1, 0x55});
+
+ mock_socket_->ExpectSend(InitPacket(0x4455, kProtocolVersion, kHostMaxPacketSize));
+ mock_socket_->AddReceive(std::string{kIdInitialization, kFlagContinuation, 0x44, 0x55, 0});
+ mock_socket_->ExpectSend(std::string{kIdInitialization, kFlagNone, 0x44, 0x56});
+ mock_socket_->AddReceive(std::string{kIdInitialization, kFlagContinuation, 0x44, 0x56, 1});
+ mock_socket_->ExpectSend(std::string{kIdInitialization, kFlagNone, 0x44, 0x57});
+ mock_socket_->AddReceive(std::string{kIdInitialization, kFlagContinuation, 0x44, 0x57, 2});
+ mock_socket_->ExpectSend(std::string{kIdInitialization, kFlagNone, 0x44, 0x58});
+ mock_socket_->AddReceive(std::string{kIdInitialization, kFlagNone, 0x44, 0x58, 0});
+
+ EXPECT_TRUE(UdpConnect());
+}
+
+
+// Tests a mismatched version number; as long as the minimum of the two versions is supported
+// we should allow the connection.
+TEST_F(UdpConnectTest, InitializationVersionMismatch) {
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(QueryPacket(0, 0));
+ mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+ mock_socket_->AddReceive(InitPacket(0, 2, 1024));
+
+ EXPECT_TRUE(UdpConnect());
+
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(QueryPacket(0, 0));
+ mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+ mock_socket_->AddReceive(InitPacket(0, 0, 1024));
+
+ EXPECT_FALSE(UdpConnect());
+}
+
+TEST_F(UdpConnectTest, QueryResponseTimeoutFailure) {
+ for (int i = 0; i < kMaxConnectAttempts; ++i) {
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceiveTimeout();
+ }
+
+ EXPECT_FALSE(UdpConnect());
+}
+
+TEST_F(UdpConnectTest, QueryResponseReceiveFailure) {
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceiveFailure();
+
+ EXPECT_FALSE(UdpConnect());
+}
+
+TEST_F(UdpConnectTest, InitResponseTimeoutFailure) {
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(QueryPacket(0, 0));
+ for (int i = 0; i < kMaxTransmissionAttempts; ++i) {
+ mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+ mock_socket_->AddReceiveTimeout();
+ }
+
+ EXPECT_FALSE(UdpConnect());
+}
+
+TEST_F(UdpConnectTest, InitResponseReceiveFailure) {
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(QueryPacket(0, 0));
+ mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+ mock_socket_->AddReceiveFailure();
+
+ EXPECT_FALSE(UdpConnect());
+}
+
+// Tests that we can recover up to the maximum number of allowed retries.
+TEST_F(UdpConnectTest, ResponseRecovery) {
+ // The device query packet can recover from up to (kMaxConnectAttempts - 1) timeouts.
+ for (int i = 0; i < kMaxConnectAttempts - 1; ++i) {
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceiveTimeout();
+ }
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(QueryPacket(0, 0));
+
+ // Subsequent packets try up to (kMaxTransmissionAttempts - 1) times.
+ for (int i = 0; i < kMaxTransmissionAttempts - 1; ++i) {
+ mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+ mock_socket_->AddReceiveTimeout();
+ }
+ mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+ mock_socket_->AddReceive(InitPacket(0, kProtocolVersion, 1024));
+
+ EXPECT_TRUE(UdpConnect());
+}
+
+// Tests that the host can handle receiving additional bytes for forward compatibility.
+TEST_F(UdpConnectTest, ExtraResponseDataSuccess) {
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(QueryPacket(0, 0) + "foo");
+ mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+ mock_socket_->AddReceive(InitPacket(0, kProtocolVersion, 1024) + "bar");
+
+ EXPECT_TRUE(UdpConnect());
+}
+
+// Tests mismatched response sequence numbers. A wrong sequence number is interpreted as a previous
+// retransmission and just ignored so we should be able to recover.
+TEST_F(UdpConnectTest, WrongSequenceRecovery) {
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(QueryPacket(1, 0));
+ mock_socket_->AddReceive(QueryPacket(0, 0));
+
+ mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+ mock_socket_->AddReceive(InitPacket(1, kProtocolVersion, 1024));
+ mock_socket_->AddReceive(InitPacket(0, kProtocolVersion, 1024));
+
+ EXPECT_TRUE(UdpConnect());
+}
+
+// Tests mismatched response IDs. This should also be interpreted as a retransmission and ignored.
+TEST_F(UdpConnectTest, WrongIdRecovery) {
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(FastbootPacket(0));
+ mock_socket_->AddReceive(QueryPacket(0, 0));
+
+ mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+ mock_socket_->AddReceive(FastbootPacket(0));
+ mock_socket_->AddReceive(InitPacket(0, kProtocolVersion, 1024));
+
+ EXPECT_TRUE(UdpConnect());
+}
+
+// Tests an invalid query response. Query responses must have at least 2 bytes of data.
+TEST_F(UdpConnectTest, InvalidQueryResponseFailure) {
+ std::string error;
+
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(QueryPacket(0));
+
+ EXPECT_FALSE(UdpConnect(&error));
+ EXPECT_EQ("invalid query response from target", error);
+
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(QueryPacket(0) + std::string{0x00});
+
+ EXPECT_FALSE(UdpConnect(&error));
+ EXPECT_EQ("invalid query response from target", error);
+}
+
+// Tests an invalid initialization response. Max packet size must be at least 512 bytes.
+TEST_F(UdpConnectTest, InvalidInitResponseFailure) {
+ std::string error;
+
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(QueryPacket(0, 0));
+ mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+ mock_socket_->AddReceive(InitPacket(0, kProtocolVersion, 511));
+
+ EXPECT_FALSE(UdpConnect(&error));
+ EXPECT_EQ("target reported invalid packet size 511", error);
+
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(QueryPacket(0, 0));
+ mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+ mock_socket_->AddReceive(InitPacket(0, 0, 1024));
+
+ EXPECT_FALSE(UdpConnect(&error));
+ EXPECT_EQ("target reported invalid protocol version 0", error);
+}
+
+TEST_F(UdpConnectTest, ErrorResponseFailure) {
+ std::string error;
+
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(ErrorPacket(0, "error1"));
+
+ EXPECT_FALSE(UdpConnect(&error));
+ EXPECT_NE(std::string::npos, error.find("error1"));
+
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(QueryPacket(0, 0));
+ mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+ mock_socket_->AddReceive(ErrorPacket(0, "error2"));
+
+ EXPECT_FALSE(UdpConnect(&error));
+ EXPECT_NE(std::string::npos, error.find("error2"));
+}
+
+// Tests an error response with continuation flag.
+TEST_F(UdpConnectTest, ErrorContinuationFailure) {
+ std::string error;
+
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(ErrorPacket(0, "error1", kFlagContinuation));
+ mock_socket_->ExpectSend(ErrorPacket(1));
+ mock_socket_->AddReceive(ErrorPacket(1, " ", kFlagContinuation));
+ mock_socket_->ExpectSend(ErrorPacket(2));
+ mock_socket_->AddReceive(ErrorPacket(2, "error2"));
+
+ EXPECT_FALSE(UdpConnect(&error));
+ EXPECT_NE(std::string::npos, error.find("error1 error2"));
+}
+
+// Fixture class to test UDP Transport read/write functionality.
+class UdpTest : public ::testing::Test {
+ public:
+ void SetUp() override {
+ // Create |transport_| starting at sequence 0 with 512 byte max packet size. Tests can call
+ // InitializeTransport() again to change settings.
+ ASSERT_TRUE(InitializeTransport(0, 512));
+ }
+
+ // Sets up |mock_socket_| to correctly initialize the protocol and creates |transport_|. This
+ // can be called multiple times in a test if needed.
+ bool InitializeTransport(uint16_t starting_sequence, int device_max_packet_size = 512) {
+ mock_socket_ = new SocketMock;
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(QueryPacket(0, starting_sequence));
+ mock_socket_->ExpectSend(
+ InitPacket(starting_sequence, kProtocolVersion, kHostMaxPacketSize));
+ mock_socket_->AddReceive(
+ InitPacket(starting_sequence, kProtocolVersion, device_max_packet_size));
+
+ std::string error;
+ transport_ = Connect(std::unique_ptr<Socket>(mock_socket_), &error);
+ return transport_ != nullptr && error.empty();
+ }
+
+ // Writes |message| to |transport_|, returns true on success.
+ bool Write(const std::string& message) {
+ return transport_->Write(message.data(), message.length()) ==
+ static_cast<ssize_t>(message.length());
+ }
+
+ // Reads from |transport_|, returns true if it matches |message|.
+ bool Read(const std::string& message) {
+ std::string buffer(message.length(), '\0');
+ return transport_->Read(&buffer[0], buffer.length()) ==
+ static_cast<ssize_t>(message.length()) && buffer == message;
+ }
+
+ protected:
+ // |mock_socket_| is a raw pointer here because we transfer ownership to |transport_| but we
+ // need to retain a pointer to set send and receive expectations.
+ SocketMock* mock_socket_ = nullptr;
+ std::unique_ptr<Transport> transport_;
+};
+
+// Tests sequence behavior with various starting sequence numbers.
+TEST_F(UdpTest, SequenceIncrementCheck) {
+ for (uint16_t seq : kTestSequenceNumbers) {
+ ASSERT_TRUE(InitializeTransport(seq));
+
+ for (int i = 0; i < 10; ++i) {
+ mock_socket_->ExpectSend(FastbootPacket(++seq, "foo"));
+ mock_socket_->AddReceive(FastbootPacket(seq, ""));
+ mock_socket_->ExpectSend(FastbootPacket(++seq, ""));
+ mock_socket_->AddReceive(FastbootPacket(seq, "bar"));
+
+ EXPECT_TRUE(Write("foo"));
+ EXPECT_TRUE(Read("bar"));
+ }
+ }
+}
+
+// Tests sending and receiving a few small packets.
+TEST_F(UdpTest, ReadAndWriteSmallPackets) {
+ mock_socket_->ExpectSend(FastbootPacket(1, "foo"));
+ mock_socket_->AddReceive(FastbootPacket(1, ""));
+ mock_socket_->ExpectSend(FastbootPacket(2, ""));
+ mock_socket_->AddReceive(FastbootPacket(2, "bar"));
+
+ EXPECT_TRUE(Write("foo"));
+ EXPECT_TRUE(Read("bar"));
+
+ mock_socket_->ExpectSend(FastbootPacket(3, "12345 67890"));
+ mock_socket_->AddReceive(FastbootPacket(3));
+ mock_socket_->ExpectSend(FastbootPacket(4, "\x01\x02\x03\x04\x05"));
+ mock_socket_->AddReceive(FastbootPacket(4));
+
+ EXPECT_TRUE(Write("12345 67890"));
+ EXPECT_TRUE(Write("\x01\x02\x03\x04\x05"));
+
+ // Reads are done by sending empty packets.
+ mock_socket_->ExpectSend(FastbootPacket(5));
+ mock_socket_->AddReceive(FastbootPacket(5, "foo bar baz"));
+ mock_socket_->ExpectSend(FastbootPacket(6));
+ mock_socket_->AddReceive(FastbootPacket(6, "\x01\x02\x03\x04\x05"));
+
+ EXPECT_TRUE(Read("foo bar baz"));
+ EXPECT_TRUE(Read("\x01\x02\x03\x04\x05"));
+}
+
+TEST_F(UdpTest, ResponseTimeoutFailure) {
+ for (int i = 0; i < kMaxTransmissionAttempts; ++i) {
+ mock_socket_->ExpectSend(FastbootPacket(1, "foo"));
+ mock_socket_->AddReceiveTimeout();
+ }
+
+ EXPECT_FALSE(Write("foo"));
+}
+
+TEST_F(UdpTest, ResponseReceiveFailure) {
+ mock_socket_->ExpectSend(FastbootPacket(1, "foo"));
+ mock_socket_->AddReceiveFailure();
+
+ EXPECT_FALSE(Write("foo"));
+}
+
+TEST_F(UdpTest, ResponseTimeoutRecovery) {
+ for (int i = 0; i < kMaxTransmissionAttempts - 1; ++i) {
+ mock_socket_->ExpectSend(FastbootPacket(1, "foo"));
+ mock_socket_->AddReceiveTimeout();
+ }
+ mock_socket_->ExpectSend(FastbootPacket(1, "foo"));
+ mock_socket_->AddReceive(FastbootPacket(1, ""));
+
+ EXPECT_TRUE(Write("foo"));
+}
+
+// Tests continuation packets for various max packet sizes.
+// The important part of this test is that regardless of what kind of packet fragmentation happens
+// at the socket layer, a single call to Transport::Read() and Transport::Write() is all the
+// fastboot code needs to do.
+TEST_F(UdpTest, ContinuationPackets) {
+ for (uint16_t max_packet_size : {512, 1024, 1200}) {
+ ASSERT_TRUE(InitializeTransport(0, max_packet_size));
+
+ // Initialize the data we want to send. Use (size - 4) to leave room for the header.
+ size_t max_data_size = max_packet_size - 4;
+ std::string data(max_data_size * 3, '\0');
+ for (size_t i = 0; i < data.length(); ++i) {
+ data[i] = i;
+ }
+ std::string chunks[] = {data.substr(0, max_data_size),
+ data.substr(max_data_size, max_data_size),
+ data.substr(max_data_size * 2, max_data_size)};
+
+ // Write data: split into 3 UDP packets, each of which will be ACKed.
+ mock_socket_->ExpectSend(FastbootPacket(1, chunks[0], kFlagContinuation));
+ mock_socket_->AddReceive(FastbootPacket(1));
+ mock_socket_->ExpectSend(FastbootPacket(2, chunks[1], kFlagContinuation));
+ mock_socket_->AddReceive(FastbootPacket(2));
+ mock_socket_->ExpectSend(FastbootPacket(3, chunks[2]));
+ mock_socket_->AddReceive(FastbootPacket(3));
+ EXPECT_TRUE(Write(data));
+
+ // Same thing for reading the data.
+ mock_socket_->ExpectSend(FastbootPacket(4));
+ mock_socket_->AddReceive(FastbootPacket(4, chunks[0], kFlagContinuation));
+ mock_socket_->ExpectSend(FastbootPacket(5));
+ mock_socket_->AddReceive(FastbootPacket(5, chunks[1], kFlagContinuation));
+ mock_socket_->ExpectSend(FastbootPacket(6));
+ mock_socket_->AddReceive(FastbootPacket(6, chunks[2]));
+ EXPECT_TRUE(Read(data));
+ }
+}
+
+// Tests that the continuation bit is respected even if the packet isn't max size.
+TEST_F(UdpTest, SmallContinuationPackets) {
+ mock_socket_->ExpectSend(FastbootPacket(1));
+ mock_socket_->AddReceive(FastbootPacket(1, "foo", kFlagContinuation));
+ mock_socket_->ExpectSend(FastbootPacket(2));
+ mock_socket_->AddReceive(FastbootPacket(2, "bar"));
+
+ EXPECT_TRUE(Read("foobar"));
+}
+
+// Tests receiving an error packet mid-continuation.
+TEST_F(UdpTest, ContinuationPacketError) {
+ mock_socket_->ExpectSend(FastbootPacket(1));
+ mock_socket_->AddReceive(FastbootPacket(1, "foo", kFlagContinuation));
+ mock_socket_->ExpectSend(FastbootPacket(2));
+ mock_socket_->AddReceive(ErrorPacket(2, "test error"));
+
+ EXPECT_FALSE(Read("foo"));
+}
+
+// Tests timeout during a continuation sequence.
+TEST_F(UdpTest, ContinuationTimeoutRecovery) {
+ mock_socket_->ExpectSend(FastbootPacket(1));
+ mock_socket_->AddReceive(FastbootPacket(1, "foo", kFlagContinuation));
+ mock_socket_->ExpectSend(FastbootPacket(2));
+ mock_socket_->AddReceiveTimeout();
+ mock_socket_->ExpectSend(FastbootPacket(2));
+ mock_socket_->AddReceive(FastbootPacket(2, "bar"));
+
+ EXPECT_TRUE(Read("foobar"));
+}
+
+// Tests read overflow returns -1 to indicate the failure.
+TEST_F(UdpTest, MultipleReadPacket) {
+ mock_socket_->ExpectSend(FastbootPacket(1));
+ mock_socket_->AddReceive(FastbootPacket(1, "foobarbaz"));
+
+ char buffer[3];
+ EXPECT_EQ(-1, transport_->Read(buffer, 3));
+}
+
+// Tests that packets arriving out-of-order are ignored.
+TEST_F(UdpTest, IgnoreOutOfOrderPackets) {
+ mock_socket_->ExpectSend(FastbootPacket(1));
+ mock_socket_->AddReceive(FastbootPacket(0, "sequence too low"));
+ mock_socket_->AddReceive(FastbootPacket(2, "sequence too high"));
+ mock_socket_->AddReceive(QueryPacket(1));
+ mock_socket_->AddReceive(FastbootPacket(1, "correct"));
+
+ EXPECT_TRUE(Read("correct"));
+}
+
+// Tests that an error response with the correct sequence number causes immediate failure.
+TEST_F(UdpTest, ErrorResponse) {
+ // Error packets with the wrong sequence number should be ignored like any other packet.
+ mock_socket_->ExpectSend(FastbootPacket(1, "foo"));
+ mock_socket_->AddReceive(ErrorPacket(0, "ignored error"));
+ mock_socket_->AddReceive(FastbootPacket(1));
+
+ EXPECT_TRUE(Write("foo"));
+
+ // Error packets with the correct sequence should abort immediately without retransmission.
+ mock_socket_->ExpectSend(FastbootPacket(2, "foo"));
+ mock_socket_->AddReceive(ErrorPacket(2, "test error"));
+
+ EXPECT_FALSE(Write("foo"));
+}
+
+// Tests that attempting to use a closed transport returns -1 without making any socket calls.
+TEST_F(UdpTest, CloseTransport) {
+ char buffer[32];
+ EXPECT_EQ(0, transport_->Close());
+ EXPECT_EQ(-1, transport_->Write("foo", 3));
+ EXPECT_EQ(-1, transport_->Read(buffer, sizeof(buffer)));
+}
diff --git a/libbacktrace/Android.mk b/libbacktrace/Android.mk
index ee56a5e..397dfda 100644
--- a/libbacktrace/Android.mk
+++ b/libbacktrace/Android.mk
@@ -71,14 +71,22 @@
build_type := host
libbacktrace_multilib := both
include $(LOCAL_PATH)/Android.build.mk
+
+libbacktrace_shared_libraries :=
+
libbacktrace_static_libraries := \
libbase \
liblog \
libunwind \
+ liblzma \
+module := libbacktrace
+build_type := target
build_target := STATIC_LIBRARY
include $(LOCAL_PATH)/Android.build.mk
-libbacktrace_static_libraries :=
+build_type := host
+libbacktrace_multilib := both
+include $(LOCAL_PATH)/Android.build.mk
#-------------------------------------------------------------------------
# The libbacktrace_offline shared library.
@@ -106,7 +114,6 @@
libLLVMSupport \
module := libbacktrace_offline
-module_tag := optional
build_type := target
build_target := SHARED_LIBRARY
include $(LOCAL_PATH)/Android.build.mk
@@ -114,6 +121,26 @@
libbacktrace_multilib := both
include $(LOCAL_PATH)/Android.build.mk
+libbacktrace_offline_shared_libraries :=
+libbacktrace_offline_static_libraries := \
+ libbacktrace \
+ libbase \
+ libcutils \
+ liblog \
+ libunwind \
+ liblzma \
+ libLLVMObject \
+ libLLVMBitReader \
+ libLLVMMC \
+ libLLVMMCParser \
+ libLLVMCore \
+ libLLVMSupport \
+
+module := libbacktrace_offline
+build_type := target
+build_target := STATIC_LIBRARY
+include $(LOCAL_PATH)/Android.build.mk
+
#-------------------------------------------------------------------------
# The libbacktrace_test library needed by backtrace_test.
#-------------------------------------------------------------------------
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 8517c9f..8d9c5a3 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -224,7 +224,8 @@
msgBuf,
sizeof(msgBuf)));
fflush(stderr);
- EXPECT_EQ(31, android_log_printLogLine(logformat, fileno(stderr), &entry));
+ EXPECT_EQ((int)(19 + sizeof(buffer)),
+ android_log_printLogLine(logformat, fileno(stderr), &entry));
android_log_format_free(logformat);
}
}
@@ -272,7 +273,7 @@
}
int len = get4LE(eventData + 4 + 1);
- if (len == 0) {
+ if (len == (sizeof(buffer) - 1)) {
++count;
AndroidLogFormat *logformat = android_log_format_new();
@@ -285,7 +286,8 @@
msgBuf,
sizeof(msgBuf)));
fflush(stderr);
- EXPECT_EQ(20, android_log_printLogLine(logformat, fileno(stderr), &entry));
+ EXPECT_EQ((int)(19 + sizeof(buffer)),
+ android_log_printLogLine(logformat, fileno(stderr), &entry));
android_log_format_free(logformat);
}
}
@@ -295,6 +297,119 @@
android_logger_list_close(logger_list);
}
+TEST(liblog, __android_log_bswrite__newline_prefix) {
+ struct logger_list *logger_list;
+
+ pid_t pid = getpid();
+
+ ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+ LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+
+ static const char buffer[] = "\nHello World\n";
+ log_time ts(android_log_clockid());
+
+ ASSERT_LT(0, __android_log_bswrite(0, buffer));
+ usleep(1000000);
+
+ int count = 0;
+
+ for (;;) {
+ log_msg log_msg;
+ if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+ break;
+ }
+
+ ASSERT_EQ(log_msg.entry.pid, pid);
+
+ if ((log_msg.entry.sec < (ts.tv_sec - 1))
+ || ((ts.tv_sec + 1) < log_msg.entry.sec)
+ || (log_msg.entry.len != (4 + 1 + 4 + sizeof(buffer) - 1))
+ || (log_msg.id() != LOG_ID_EVENTS)) {
+ continue;
+ }
+
+ char *eventData = log_msg.msg();
+
+ if (eventData[4] != EVENT_TYPE_STRING) {
+ continue;
+ }
+
+ int len = get4LE(eventData + 4 + 1);
+ if (len == (sizeof(buffer) - 1)) {
+ ++count;
+
+ AndroidLogFormat *logformat = android_log_format_new();
+ EXPECT_TRUE(NULL != logformat);
+ AndroidLogEntry entry;
+ char msgBuf[1024];
+ EXPECT_EQ(0, android_log_processBinaryLogBuffer(&log_msg.entry_v1,
+ &entry,
+ NULL,
+ msgBuf,
+ sizeof(msgBuf)));
+ fflush(stderr);
+ EXPECT_EQ((int)(19 + 19 - 1 + sizeof(buffer)),
+ android_log_printLogLine(logformat, fileno(stderr), &entry));
+ android_log_format_free(logformat);
+ }
+ }
+
+ EXPECT_EQ(1, count);
+
+ android_logger_list_close(logger_list);
+}
+
+TEST(liblog, __android_log_buf_write__newline_prefix) {
+ struct logger_list *logger_list;
+
+ pid_t pid = getpid();
+
+ ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+ LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+
+ static const char tag[] = "TEST__android_log_buf_write";
+ static const char buffer[] = "\nHello World\n";
+ log_time ts(android_log_clockid());
+
+ EXPECT_LT(0, __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_INFO,
+ tag, buffer));
+ usleep(1000000);
+
+ int count = 0;
+
+ for (;;) {
+ log_msg log_msg;
+ if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+ break;
+ }
+
+ ASSERT_EQ(log_msg.entry.pid, pid);
+
+ if ((log_msg.entry.sec < (ts.tv_sec - 1))
+ || ((ts.tv_sec + 1) < log_msg.entry.sec)
+ || (log_msg.entry.len != (sizeof(tag) + sizeof(buffer) + 1))
+ || (log_msg.id() != LOG_ID_MAIN)) {
+ continue;
+ }
+
+ ++count;
+
+ AndroidLogFormat *logformat = android_log_format_new();
+ EXPECT_TRUE(NULL != logformat);
+ AndroidLogEntry entry;
+ EXPECT_EQ(0, android_log_processLogBuffer(&log_msg.entry_v1,
+ &entry));
+ fflush(stderr);
+ EXPECT_EQ((int)(11 + 11 + sizeof(tag) + sizeof(tag) + sizeof(buffer) - 3),
+ android_log_printLogLine(logformat, fileno(stderr), &entry));
+ android_log_format_free(logformat);
+ }
+
+ EXPECT_EQ(1, count);
+
+ android_logger_list_close(logger_list);
+}
+
TEST(liblog, __security) {
static const char persist_key[] = "persist.logd.security";
static const char readonly_key[] = "ro.device_owner";
@@ -609,7 +724,7 @@
EXPECT_GT(one_percent_ticks, user_ticks + system_ticks);
}
-static const char max_payload_tag[] = "TEST_max_payload_XXXX";
+static const char max_payload_tag[] = "TEST_max_payload_and_longish_tag_XXXX";
#define SIZEOF_MAX_PAYLOAD_BUF (LOGGER_ENTRY_MAX_PAYLOAD - \
sizeof(max_payload_tag) - 1)
static const char max_payload_buf[] = "LEONATO\n\
diff --git a/logcat/logpersist b/logcat/logpersist
index dab466d..8762ff1 100755
--- a/logcat/logpersist
+++ b/logcat/logpersist
@@ -1,8 +1,8 @@
#! /system/bin/sh
# logpersist cat start and stop handlers
progname="${0##*/}"
-case `getprop ro.build.type` in
-userdebug|eng) ;;
+case `getprop ro.debuggable` in
+1) ;;
*) echo "${progname} - Permission denied"
exit 1
;;
diff --git a/logd/README.property b/logd/README.property
index 22f86b9..6200d3e 100644
--- a/logd/README.property
+++ b/logd/README.property
@@ -8,7 +8,7 @@
ro.device_owner bool false Override persist.logd.security to false
ro.logd.kernel bool+ svelte+ Enable klogd daemon
ro.logd.statistics bool+ svelte+ Enable logcat -S statistics.
-ro.build.type string if user, logd.statistics &
+ro.debuggable number if not "1", logd.statistics &
ro.logd.kernel default false.
persist.logd.logpersistd string Enable logpersist daemon, "logcatd"
turns on logcat -f in logd context
@@ -46,10 +46,10 @@
NB:
- bool+ - "true", "false" and comma separated list of "eng" (forced false if
- ro.build.type is "user") or "svelte" (forced false if ro.config.low_ram is
+ ro.debuggable is not "1") or "svelte" (forced false if ro.config.low_ram is
true).
- svelte - see ro.config.low_ram for details.
-- svelte+ - see ro.config.low_ram and ro.build.type for details.
+- svelte+ - see ro.config.low_ram and ro.debuggable for details.
- ro - <base property> temporary override, ro.<base property> platform default.
- persist - <base property> override, persist.<base property> platform default.
- build - VERBOSE for native, DEBUG for jvm isLoggable, or developer option.
diff --git a/logd/main.cpp b/logd/main.cpp
index 9cec313..bef40c7 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -202,8 +202,8 @@
return false;
}
if (flag & BOOL_DEFAULT_FLAG_ENG) {
- property_get("ro.build.type", property, "");
- if (!strcmp(property, "user")) {
+ property_get("ro.debuggable", property, "");
+ if (strcmp(property, "1")) {
return false;
}
}
diff --git a/metricsd/uploader/upload_service_test.cc b/metricsd/uploader/upload_service_test.cc
index 70112f4..0f77fe4 100644
--- a/metricsd/uploader/upload_service_test.cc
+++ b/metricsd/uploader/upload_service_test.cc
@@ -304,6 +304,8 @@
upload_service_->PersistToDisk();
EXPECT_EQ(
1, upload_service_->current_log_->uma_proto()->histogram_event().size());
+ // Destroy the old service before creating a new one.
+ upload_service_.reset();
upload_service_.reset(new UploadService(
"", base::TimeDelta(), base::TimeDelta(), private_dir_, shared_dir_));
upload_service_->InitForTest(nullptr);
@@ -325,6 +327,8 @@
// Write a bogus saved log.
EXPECT_EQ(5, base::WriteFile(upload_service_->saved_log_path_, "hello", 5));
+ // Destroy the old service before creating a new one.
+ upload_service_.reset();
upload_service_.reset(new UploadService(
"", base::TimeDelta(), base::TimeDelta(), private_dir_, shared_dir_));