Merge "Add the setAutoRefresh interface in ANativeWindow" into nyc-dev
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..1d16f69 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,67 @@
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);
+}
+
+constexpr int32_t kUnknownBootReason = 1;
+
+// 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, int32_t> kBootReasonMap = {
+ {"unknown", kUnknownBootReason},
+ {"normal", 2},
+ {"recovery", 3},
+ {"reboot", 4},
+ {"PowerKey", 5},
+ {"hard_reset", 6},
+ {"kernel_panic", 7},
+ {"rpm_err", 8},
+ {"hw_reset", 9},
+ {"tz_err", 10},
+ {"adsp_err", 11},
+ {"modem_err", 12},
+ {"mba_err", 13},
+ {"Watchdog", 14},
+ {"Panic", 15},
+ {"power_key", 16},
+ {"power_on", 17},
+ {"Reboot", 18},
+ {"rtc", 19},
+ {"edl", 20},
+};
+
+// 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) {
+ 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 +163,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 7c7d417..636092e 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
@@ -245,22 +246,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());
}
@@ -337,8 +357,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/socket.cpp b/fastboot/socket.cpp
index d49f47f..14ecd93 100644
--- a/fastboot/socket.cpp
+++ b/fastboot/socket.cpp
@@ -48,18 +48,6 @@
return ret;
}
-bool Socket::SetReceiveTimeout(int timeout_ms) {
- if (timeout_ms != receive_timeout_ms_) {
- if (socket_set_receive_timeout(sock_, timeout_ms) == 0) {
- receive_timeout_ms_ = timeout_ms;
- return true;
- }
- return false;
- }
-
- return true;
-}
-
ssize_t Socket::ReceiveAll(void* data, size_t length, int timeout_ms) {
size_t total = 0;
@@ -82,6 +70,40 @@
return socket_get_local_port(sock_);
}
+// According to Windows setsockopt() documentation, if a Windows socket times out during send() or
+// recv() the state is indeterminate and should not be used. Our UDP protocol relies on being able
+// to re-send after a timeout, so we must use select() rather than SO_RCVTIMEO.
+// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms740476(v=vs.85).aspx.
+bool Socket::WaitForRecv(int timeout_ms) {
+ receive_timed_out_ = false;
+
+ // In our usage |timeout_ms| <= 0 means block forever, so just return true immediately and let
+ // the subsequent recv() do the blocking.
+ if (timeout_ms <= 0) {
+ return true;
+ }
+
+ // select() doesn't always check this case and will block for |timeout_ms| if we let it.
+ if (sock_ == INVALID_SOCKET) {
+ return false;
+ }
+
+ fd_set read_set;
+ FD_ZERO(&read_set);
+ FD_SET(sock_, &read_set);
+
+ timeval timeout;
+ timeout.tv_sec = timeout_ms / 1000;
+ timeout.tv_usec = (timeout_ms % 1000) * 1000;
+
+ int result = TEMP_FAILURE_RETRY(select(sock_ + 1, &read_set, nullptr, nullptr, &timeout));
+
+ if (result == 0) {
+ receive_timed_out_ = true;
+ }
+ return result == 1;
+}
+
// Implements the Socket interface for UDP.
class UdpSocket : public Socket {
public:
@@ -127,7 +149,7 @@
}
ssize_t UdpSocket::Receive(void* data, size_t length, int timeout_ms) {
- if (!SetReceiveTimeout(timeout_ms)) {
+ if (!WaitForRecv(timeout_ms)) {
return -1;
}
@@ -206,7 +228,7 @@
}
ssize_t TcpSocket::Receive(void* data, size_t length, int timeout_ms) {
- if (!SetReceiveTimeout(timeout_ms)) {
+ if (!WaitForRecv(timeout_ms)) {
return -1;
}
diff --git a/fastboot/socket.h b/fastboot/socket.h
index c0bd7c9..de543db 100644
--- a/fastboot/socket.h
+++ b/fastboot/socket.h
@@ -81,13 +81,17 @@
virtual bool Send(std::vector<cutils_socket_buffer_t> buffers) = 0;
// Waits up to |timeout_ms| to receive up to |length| bytes of data. |timout_ms| of 0 will
- // block forever. Returns the number of bytes received or -1 on error/timeout. On timeout
- // errno will be set to EAGAIN or EWOULDBLOCK.
+ // block forever. Returns the number of bytes received or -1 on error/timeout; see
+ // ReceiveTimedOut() to distinguish between the two.
virtual ssize_t Receive(void* data, size_t length, int timeout_ms) = 0;
// Calls Receive() until exactly |length| bytes have been received or an error occurs.
virtual ssize_t ReceiveAll(void* data, size_t length, int timeout_ms);
+ // Returns true if the last Receive() call timed out normally and can be retried; fatal errors
+ // or successful reads will return false.
+ bool ReceiveTimedOut() { return receive_timed_out_; }
+
// Closes the socket. Returns 0 on success, -1 on error.
virtual int Close();
@@ -102,10 +106,13 @@
// Protected constructor to force factory function use.
Socket(cutils_socket_t sock);
- // Update the socket receive timeout if necessary.
- bool SetReceiveTimeout(int timeout_ms);
+ // Blocks up to |timeout_ms| until a read is possible on |sock_|, and sets |receive_timed_out_|
+ // as appropriate to help distinguish between normal timeouts and fatal errors. Returns true if
+ // a subsequent recv() on |sock_| will complete without blocking or if |timeout_ms| <= 0.
+ bool WaitForRecv(int timeout_ms);
cutils_socket_t sock_ = INVALID_SOCKET;
+ bool receive_timed_out_ = false;
// Non-class functions we want to override during tests to verify functionality. Implementation
// should call this rather than using socket_send_buffers() directly.
@@ -113,8 +120,6 @@
socket_send_buffers_function_ = &socket_send_buffers;
private:
- int receive_timeout_ms_ = 0;
-
FRIEND_TEST(SocketTest, TestTcpSendBuffers);
FRIEND_TEST(SocketTest, TestUdpSendBuffers);
diff --git a/fastboot/socket_mock.cpp b/fastboot/socket_mock.cpp
index c962f30..2531b53 100644
--- a/fastboot/socket_mock.cpp
+++ b/fastboot/socket_mock.cpp
@@ -55,7 +55,7 @@
return false;
}
- bool return_value = events_.front().return_value;
+ bool return_value = events_.front().status;
events_.pop();
return return_value;
}
@@ -76,21 +76,28 @@
return -1;
}
- if (events_.front().type != EventType::kReceive) {
+ const Event& event = events_.front();
+ if (event.type != EventType::kReceive) {
ADD_FAILURE() << "Receive() was called out-of-order";
return -1;
}
- if (events_.front().return_value > static_cast<ssize_t>(length)) {
- ADD_FAILURE() << "Receive(): not enough bytes (" << length << ") for "
- << events_.front().message;
+ const std::string& message = event.message;
+ if (message.length() > length) {
+ ADD_FAILURE() << "Receive(): not enough bytes (" << length << ") for " << message;
return -1;
}
- ssize_t return_value = events_.front().return_value;
- if (return_value > 0) {
- memcpy(data, events_.front().message.data(), return_value);
+ receive_timed_out_ = event.status;
+ ssize_t return_value = message.length();
+
+ // Empty message indicates failure.
+ if (message.empty()) {
+ return_value = -1;
+ } else {
+ memcpy(data, message.data(), message.length());
}
+
events_.pop();
return return_value;
}
@@ -124,18 +131,21 @@
}
void SocketMock::AddReceive(std::string message) {
- ssize_t return_value = message.length();
- events_.push(Event(EventType::kReceive, std::move(message), return_value, nullptr));
+ events_.push(Event(EventType::kReceive, std::move(message), false, nullptr));
+}
+
+void SocketMock::AddReceiveTimeout() {
+ events_.push(Event(EventType::kReceive, "", true, nullptr));
}
void SocketMock::AddReceiveFailure() {
- events_.push(Event(EventType::kReceive, "", -1, nullptr));
+ events_.push(Event(EventType::kReceive, "", false, nullptr));
}
void SocketMock::AddAccept(std::unique_ptr<Socket> sock) {
- events_.push(Event(EventType::kAccept, "", 0, std::move(sock)));
+ events_.push(Event(EventType::kAccept, "", false, std::move(sock)));
}
-SocketMock::Event::Event(EventType _type, std::string _message, ssize_t _return_value,
+SocketMock::Event::Event(EventType _type, std::string _message, ssize_t _status,
std::unique_ptr<Socket> _sock)
- : type(_type), message(_message), return_value(_return_value), sock(std::move(_sock)) {}
+ : type(_type), message(_message), status(_status), sock(std::move(_sock)) {}
diff --git a/fastboot/socket_mock.h b/fastboot/socket_mock.h
index 41fe06d..eacd6bb 100644
--- a/fastboot/socket_mock.h
+++ b/fastboot/socket_mock.h
@@ -71,7 +71,10 @@
// Adds data to provide for Receive().
void AddReceive(std::string message);
- // Adds a Receive() failure.
+ // Adds a Receive() timeout after which ReceiveTimedOut() will return true.
+ void AddReceiveTimeout();
+
+ // Adds a Receive() failure after which ReceiveTimedOut() will return false.
void AddReceiveFailure();
// Adds a Socket to return from Accept().
@@ -81,12 +84,12 @@
enum class EventType { kSend, kReceive, kAccept };
struct Event {
- Event(EventType _type, std::string _message, ssize_t _return_value,
+ Event(EventType _type, std::string _message, ssize_t _status,
std::unique_ptr<Socket> _sock);
EventType type;
std::string message;
- ssize_t return_value;
+ bool status; // Return value for Send() or timeout status for Receive().
std::unique_ptr<Socket> sock;
};
diff --git a/fastboot/socket_test.cpp b/fastboot/socket_test.cpp
index cc71075..affbdfd 100644
--- a/fastboot/socket_test.cpp
+++ b/fastboot/socket_test.cpp
@@ -28,7 +28,8 @@
#include <gtest/gtest-spi.h>
#include <gtest/gtest.h>
-enum { kTestTimeoutMs = 3000 };
+static constexpr int kShortTimeoutMs = 10;
+static constexpr int kTestTimeoutMs = 3000;
// Creates connected sockets |server| and |client|. Returns true on success.
bool MakeConnectedSockets(Socket::Protocol protocol, std::unique_ptr<Socket>* server,
@@ -87,6 +88,50 @@
}
}
+TEST(SocketTest, TestReceiveTimeout) {
+ std::unique_ptr<Socket> server, client;
+ char buffer[16];
+
+ for (Socket::Protocol protocol : {Socket::Protocol::kUdp, Socket::Protocol::kTcp}) {
+ ASSERT_TRUE(MakeConnectedSockets(protocol, &server, &client));
+
+ EXPECT_EQ(-1, server->Receive(buffer, sizeof(buffer), kShortTimeoutMs));
+ EXPECT_TRUE(server->ReceiveTimedOut());
+
+ EXPECT_EQ(-1, client->Receive(buffer, sizeof(buffer), kShortTimeoutMs));
+ EXPECT_TRUE(client->ReceiveTimedOut());
+ }
+
+ // UDP will wait for timeout if the other side closes.
+ ASSERT_TRUE(MakeConnectedSockets(Socket::Protocol::kUdp, &server, &client));
+ EXPECT_EQ(0, server->Close());
+ EXPECT_EQ(-1, client->Receive(buffer, sizeof(buffer), kShortTimeoutMs));
+ EXPECT_TRUE(client->ReceiveTimedOut());
+}
+
+TEST(SocketTest, TestReceiveFailure) {
+ std::unique_ptr<Socket> server, client;
+ char buffer[16];
+
+ for (Socket::Protocol protocol : {Socket::Protocol::kUdp, Socket::Protocol::kTcp}) {
+ ASSERT_TRUE(MakeConnectedSockets(protocol, &server, &client));
+
+ EXPECT_EQ(0, server->Close());
+ EXPECT_EQ(-1, server->Receive(buffer, sizeof(buffer), kTestTimeoutMs));
+ EXPECT_FALSE(server->ReceiveTimedOut());
+
+ EXPECT_EQ(0, client->Close());
+ EXPECT_EQ(-1, client->Receive(buffer, sizeof(buffer), kTestTimeoutMs));
+ EXPECT_FALSE(client->ReceiveTimedOut());
+ }
+
+ // TCP knows right away when the other side closes and returns 0 to indicate EOF.
+ ASSERT_TRUE(MakeConnectedSockets(Socket::Protocol::kTcp, &server, &client));
+ EXPECT_EQ(0, server->Close());
+ EXPECT_EQ(0, client->Receive(buffer, sizeof(buffer), kTestTimeoutMs));
+ EXPECT_FALSE(client->ReceiveTimedOut());
+}
+
// Tests sending and receiving large packets.
TEST(SocketTest, TestLargePackets) {
std::string message(1024, '\0');
@@ -290,6 +335,11 @@
mock->AddReceiveFailure();
EXPECT_FALSE(ReceiveString(mock, "foo"));
+ EXPECT_FALSE(mock->ReceiveTimedOut());
+
+ mock->AddReceiveTimeout();
+ EXPECT_FALSE(ReceiveString(mock, "foo"));
+ EXPECT_TRUE(mock->ReceiveTimedOut());
mock->AddReceive("foo");
mock->AddReceiveFailure();
diff --git a/fastboot/tcp.cpp b/fastboot/tcp.cpp
index da2880a..e42c4e1 100644
--- a/fastboot/tcp.cpp
+++ b/fastboot/tcp.cpp
@@ -28,6 +28,7 @@
#include "tcp.h"
+#include <android-base/parseint.h>
#include <android-base/stringprintf.h>
namespace tcp {
@@ -98,7 +99,8 @@
return false;
}
- char buffer[kHandshakeLength];
+ char buffer[kHandshakeLength + 1];
+ buffer[kHandshakeLength] = '\0';
if (socket_->ReceiveAll(buffer, kHandshakeLength, kHandshakeTimeoutMs) != kHandshakeLength) {
*error = android::base::StringPrintf(
"No initialization message received (%s). Target may not support TCP fastboot",
@@ -111,9 +113,10 @@
return false;
}
- if (memcmp(buffer + 2, "01", 2) != 0) {
+ int version = 0;
+ if (!android::base::ParseInt(buffer + 2, &version) || version < kProtocolVersion) {
*error = android::base::StringPrintf("Unknown TCP protocol version %s (host version %02d)",
- std::string(buffer + 2, 2).c_str(), kProtocolVersion);
+ buffer + 2, kProtocolVersion);
return false;
}
diff --git a/fastboot/tcp_test.cpp b/fastboot/tcp_test.cpp
index 7d80d76..6e867ae 100644
--- a/fastboot/tcp_test.cpp
+++ b/fastboot/tcp_test.cpp
@@ -42,6 +42,16 @@
EXPECT_EQ("", error);
}
+TEST(TcpConnectTest, TestNewerVersionSuccess) {
+ std::unique_ptr<SocketMock> mock(new SocketMock);
+ mock->ExpectSend("FB01");
+ mock->AddReceive("FB99");
+
+ std::string error;
+ EXPECT_NE(nullptr, tcp::internal::Connect(std::move(mock), &error));
+ EXPECT_EQ("", error);
+}
+
TEST(TcpConnectTest, TestSendFailure) {
std::unique_ptr<SocketMock> mock(new SocketMock);
mock->ExpectSendFailure("FB01");
@@ -74,11 +84,11 @@
TEST(TcpConnectTest, TestUnknownVersionFailure) {
std::unique_ptr<SocketMock> mock(new SocketMock);
mock->ExpectSend("FB01");
- mock->AddReceive("FB02");
+ mock->AddReceive("FB00");
std::string error;
EXPECT_EQ(nullptr, tcp::internal::Connect(std::move(mock), &error));
- EXPECT_EQ("Unknown TCP protocol version 02 (host version 01)", error);
+ EXPECT_EQ("Unknown TCP protocol version 00 (host version 01)", error);
}
// Fixture to configure a SocketMock for a successful TCP connection.
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/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c
index bd30e94..78d3450 100644
--- a/fs_mgr/fs_mgr.c
+++ b/fs_mgr/fs_mgr.c
@@ -440,26 +440,32 @@
return ret;
}
+static bool needs_block_encryption(const struct fstab_rec* rec)
+{
+ if (device_is_force_encrypted() && fs_mgr_is_encryptable(rec)) return true;
+ if (rec->fs_mgr_flags & MF_FORCECRYPT) return true;
+ if (rec->fs_mgr_flags & MF_CRYPT) {
+ /* Check for existence of convert_fde breadcrumb file */
+ char convert_fde_name[PATH_MAX];
+ snprintf(convert_fde_name, sizeof(convert_fde_name),
+ "%s/misc/vold/convert_fde", rec->mount_point);
+ if (access(convert_fde_name, F_OK) == 0) return true;
+ }
+ if (rec->fs_mgr_flags & MF_FORCEFDEORFBE) {
+ /* Check for absence of convert_fbe breadcrumb file */
+ char convert_fbe_name[PATH_MAX];
+ snprintf(convert_fbe_name, sizeof(convert_fbe_name),
+ "%s/convert_fbe", rec->mount_point);
+ if (access(convert_fbe_name, F_OK) != 0) return true;
+ }
+ return false;
+}
+
// Check to see if a mountable volume has encryption requirements
static int handle_encryptable(const struct fstab_rec* rec)
{
- /* Check for existence of convert_fbe breadcrumb file */
- char convert_fbe_name[PATH_MAX];
- snprintf(convert_fbe_name, sizeof(convert_fbe_name),
- "%s/convert_fbe", rec->mount_point);
- bool convert_fbe = (access(convert_fbe_name, F_OK) == 0);
-
- /* Check for existence of convert_fbe breadcrumb file */
- char convert_fde_name[PATH_MAX];
- snprintf(convert_fde_name, sizeof(convert_fbe_name),
- "%s/misc/vold/convert_fde", rec->mount_point);
- bool convert_fde = (access(convert_fde_name, F_OK) == 0);
-
/* If this is block encryptable, need to trigger encryption */
- if ( (rec->fs_mgr_flags & MF_FORCECRYPT)
- || ((rec->fs_mgr_flags & MF_CRYPT) && convert_fde)
- || ((rec->fs_mgr_flags & MF_FORCEFDEORFBE) && !convert_fbe)
- || (device_is_force_encrypted() && fs_mgr_is_encryptable(rec))) {
+ if (needs_block_encryption(rec)) {
if (umount(rec->mount_point) == 0) {
return FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION;
} else {
@@ -467,16 +473,13 @@
rec->mount_point, strerror(errno));
return FS_MGR_MNTALL_DEV_NOT_ENCRYPTED;
}
- }
-
+ } else if (rec->fs_mgr_flags & (MF_FILEENCRYPTION | MF_FORCEFDEORFBE)) {
// Deal with file level encryption
- if ( (rec->fs_mgr_flags & MF_FILEENCRYPTION)
- || ((rec->fs_mgr_flags & MF_FORCEFDEORFBE) && convert_fbe)) {
INFO("%s is file encrypted\n", rec->mount_point);
return FS_MGR_MNTALL_DEV_FILE_ENCRYPTED;
+ } else {
+ return FS_MGR_MNTALL_DEV_NOT_ENCRYPTED;
}
-
- return FS_MGR_MNTALL_DEV_NOT_ENCRYPTED;
}
/* When multiple fstab records share the same mount_point, it will
diff --git a/include/system/graphics.h b/include/system/graphics.h
index ac5ae96..e255614 100644
--- a/include/system/graphics.h
+++ b/include/system/graphics.h
@@ -553,6 +553,37 @@
* which describes both gamma curve and numeric range (within the bit depth).
*
* Other dataspaces include depth measurement data from a depth camera.
+ *
+ * A dataspace is comprised of a number of fields.
+ *
+ * Version
+ * --------
+ * The top 2 bits represent the revision of the field specification. This is
+ * currently always 0.
+ *
+ *
+ * bits 31-30 29 - 0
+ * +-----+----------------------------------------------------+
+ * fields | Rev | Revision specific fields |
+ * +-----+----------------------------------------------------+
+ *
+ * Field layout for version = 0:
+ * ----------------------------
+ *
+ * A dataspace is comprised of the following fields:
+ * Standard
+ * Transfer function
+ * Range
+ *
+ * bits 31-30 29-27 26 - 22 21 - 16 15 - 0
+ * +-----+-----+--------+--------+----------------------------+
+ * fields | 0 |Range|Transfer|Standard| Legacy and custom |
+ * +-----+-----+--------+--------+----------------------------+
+ * VV RRR TTTTT SSSSSS LLLLLLLL LLLLLLLL
+ *
+ * If range, transfer and standard fields are all 0 (e.g. top 16 bits are
+ * all zeroes), the bottom 16 bits contain either a legacy dataspace value,
+ * or a custom value.
*/
typedef enum android_dataspace {
@@ -581,14 +612,309 @@
HAL_DATASPACE_ARBITRARY = 0x1,
/*
- * RGB Colorspaces
- * -----------------
+ * Color-description aspects
*
- * Primaries are given using (x,y) coordinates in the CIE 1931 definition
- * of x and y specified by ISO 11664-1.
+ * The following aspects define various characteristics of the color
+ * specification. These represent bitfields, so that a data space value
+ * can specify each of them independently.
+ */
+
+ HAL_DATASPACE_STANDARD_SHIFT = 16,
+
+ /*
+ * Standard aspect
+ *
+ * Defines the chromaticity coordinates of the source primaries in terms of
+ * the CIE 1931 definition of x and y specified in ISO 11664-1.
+ */
+ HAL_DATASPACE_STANDARD_MASK = 63 << HAL_DATASPACE_STANDARD_SHIFT, // 0x3F
+
+ /*
+ * Chromacity coordinates are unknown or are determined by the application.
+ * Implementations shall use the following suggested standards:
+ *
+ * All YCbCr formats: BT709 if size is 720p or larger (since most video
+ * content is letterboxed this corresponds to width is
+ * 1280 or greater, or height is 720 or greater).
+ * BT601_625 if size is smaller than 720p or is JPEG.
+ * All RGB formats: BT709.
+ *
+ * For all other formats standard is undefined, and implementations should use
+ * an appropriate standard for the data represented.
+ */
+ HAL_DATASPACE_STANDARD_UNSPECIFIED = 0 << HAL_DATASPACE_STANDARD_SHIFT,
+
+ /*
+ * Primaries: x y
+ * green 0.300 0.600
+ * blue 0.150 0.060
+ * red 0.640 0.330
+ * white (D65) 0.3127 0.3290
+ *
+ * Use the unadjusted KR = 0.2126, KB = 0.0722 luminance interpretation
+ * for RGB conversion.
+ */
+ HAL_DATASPACE_STANDARD_BT709 = 1 << HAL_DATASPACE_STANDARD_SHIFT,
+
+ /*
+ * Primaries: x y
+ * green 0.290 0.600
+ * blue 0.150 0.060
+ * red 0.640 0.330
+ * white (D65) 0.3127 0.3290
+ *
+ * KR = 0.299, KB = 0.114. This adjusts the luminance interpretation
+ * for RGB conversion from the one purely determined by the primaries
+ * to minimize the color shift into RGB space that uses BT.709
+ * primaries.
+ */
+ HAL_DATASPACE_STANDARD_BT601_625 = 2 << HAL_DATASPACE_STANDARD_SHIFT,
+
+ /*
+ * Primaries: x y
+ * green 0.290 0.600
+ * blue 0.150 0.060
+ * red 0.640 0.330
+ * white (D65) 0.3127 0.3290
+ *
+ * Use the unadjusted KR = 0.222, KB = 0.071 luminance interpretation
+ * for RGB conversion.
+ */
+ HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED = 3 << HAL_DATASPACE_STANDARD_SHIFT,
+
+ /*
+ * Primaries: x y
+ * green 0.310 0.595
+ * blue 0.155 0.070
+ * red 0.630 0.340
+ * white (D65) 0.3127 0.3290
+ *
+ * KR = 0.299, KB = 0.114. This adjusts the luminance interpretation
+ * for RGB conversion from the one purely determined by the primaries
+ * to minimize the color shift into RGB space that uses BT.709
+ * primaries.
+ */
+ HAL_DATASPACE_STANDARD_BT601_525 = 4 << HAL_DATASPACE_STANDARD_SHIFT,
+
+ /*
+ * Primaries: x y
+ * green 0.310 0.595
+ * blue 0.155 0.070
+ * red 0.630 0.340
+ * white (D65) 0.3127 0.3290
+ *
+ * Use the unadjusted KR = 0.212, KB = 0.087 luminance interpretation
+ * for RGB conversion (as in SMPTE 240M).
+ */
+ HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED = 5 << HAL_DATASPACE_STANDARD_SHIFT,
+
+ /*
+ * Primaries: x y
+ * green 0.170 0.797
+ * blue 0.131 0.046
+ * red 0.708 0.292
+ * white (D65) 0.3127 0.3290
+ *
+ * Use the unadjusted KR = 0.2627, KB = 0.0593 luminance interpretation
+ * for RGB conversion.
+ */
+ HAL_DATASPACE_STANDARD_BT2020 = 6 << HAL_DATASPACE_STANDARD_SHIFT,
+
+ /*
+ * Primaries: x y
+ * green 0.170 0.797
+ * blue 0.131 0.046
+ * red 0.708 0.292
+ * white (D65) 0.3127 0.3290
+ *
+ * Use the unadjusted KR = 0.2627, KB = 0.0593 luminance interpretation
+ * for RGB conversion using the linear domain.
+ */
+ HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE = 7 << HAL_DATASPACE_STANDARD_SHIFT,
+
+ /*
+ * Primaries: x y
+ * green 0.21 0.71
+ * blue 0.14 0.08
+ * red 0.67 0.33
+ * white (C) 0.310 0.316
+ *
+ * Use the unadjusted KR = 0.30, KB = 0.11 luminance interpretation
+ * for RGB conversion.
+ */
+ HAL_DATASPACE_STANDARD_BT470M = 8 << HAL_DATASPACE_STANDARD_SHIFT,
+
+ /*
+ * Primaries: x y
+ * green 0.243 0.692
+ * blue 0.145 0.049
+ * red 0.681 0.319
+ * white (C) 0.310 0.316
+ *
+ * Use the unadjusted KR = 0.254, KB = 0.068 luminance interpretation
+ * for RGB conversion.
+ */
+ HAL_DATASPACE_STANDARD_FILM = 9 << HAL_DATASPACE_STANDARD_SHIFT,
+
+ HAL_DATASPACE_TRANSFER_SHIFT = 22,
+
+ /*
+ * Transfer aspect
*
* Transfer characteristics are the opto-electronic transfer characteristic
* at the source as a function of linear optical intensity (luminance).
+ *
+ * For digital signals, E corresponds to the recorded value. Normally, the
+ * transfer function is applied in RGB space to each of the R, G and B
+ * components independently. This may result in color shift that can be
+ * minized by applying the transfer function in Lab space only for the L
+ * component. Implementation may apply the transfer function in RGB space
+ * for all pixel formats if desired.
+ */
+
+ HAL_DATASPACE_TRANSFER_MASK = 31 << HAL_DATASPACE_TRANSFER_SHIFT, // 0x1F
+
+ /*
+ * Transfer characteristics are unknown or are determined by the
+ * application.
+ *
+ * Implementations should use the following transfer functions:
+ *
+ * For YCbCr formats: use HAL_DATASPACE_TRANSFER_SMPTE_170M
+ * For RGB formats: use HAL_DATASPACE_TRANSFER_SRGB
+ *
+ * For all other formats transfer function is undefined, and implementations
+ * should use an appropriate standard for the data represented.
+ */
+ HAL_DATASPACE_TRANSFER_UNSPECIFIED = 0 << HAL_DATASPACE_TRANSFER_SHIFT,
+
+ /*
+ * Transfer characteristic curve:
+ * E = L
+ * L - luminance of image 0 <= L <= 1 for conventional colorimetry
+ * E - corresponding electrical signal
+ */
+ HAL_DATASPACE_TRANSFER_LINEAR = 1 << HAL_DATASPACE_TRANSFER_SHIFT,
+
+ /*
+ * Transfer characteristic curve:
+ *
+ * E = 1.055 * L^(1/2.4) - 0.055 for 0.0031308 <= L <= 1
+ * = 12.92 * L for 0 <= L < 0.0031308
+ * L - luminance of image 0 <= L <= 1 for conventional colorimetry
+ * E - corresponding electrical signal
+ */
+ HAL_DATASPACE_TRANSFER_SRGB = 2 << HAL_DATASPACE_TRANSFER_SHIFT,
+
+ /*
+ * BT.601 525, BT.601 625, BT.709, BT.2020
+ *
+ * Transfer characteristic curve:
+ * E = 1.099 * L ^ 0.45 - 0.099 for 0.018 <= L <= 1
+ * = 4.500 * L for 0 <= L < 0.018
+ * L - luminance of image 0 <= L <= 1 for conventional colorimetry
+ * E - corresponding electrical signal
+ */
+ HAL_DATASPACE_TRANSFER_SMPTE_170M = 3 << HAL_DATASPACE_TRANSFER_SHIFT,
+
+ /*
+ * Assumed display gamma 2.2.
+ *
+ * Transfer characteristic curve:
+ * E = L ^ (1/2.2)
+ * L - luminance of image 0 <= L <= 1 for conventional colorimetry
+ * E - corresponding electrical signal
+ */
+ HAL_DATASPACE_TRANSFER_GAMMA2_2 = 4 << HAL_DATASPACE_TRANSFER_SHIFT,
+
+ /*
+ * display gamma 2.8.
+ *
+ * Transfer characteristic curve:
+ * E = L ^ (1/2.8)
+ * L - luminance of image 0 <= L <= 1 for conventional colorimetry
+ * E - corresponding electrical signal
+ */
+ HAL_DATASPACE_TRANSFER_GAMMA2_8 = 5 << HAL_DATASPACE_TRANSFER_SHIFT,
+
+ /*
+ * SMPTE ST 2084
+ *
+ * Transfer characteristic curve:
+ * E = ((c1 + c2 * L^n) / (1 + c3 * L^n)) ^ m
+ * c1 = c3 - c2 + 1 = 3424 / 4096 = 0.8359375
+ * c2 = 32 * 2413 / 4096 = 18.8515625
+ * c3 = 32 * 2392 / 4096 = 18.6875
+ * m = 128 * 2523 / 4096 = 78.84375
+ * n = 0.25 * 2610 / 4096 = 0.1593017578125
+ * L - luminance of image 0 <= L <= 1 for HDR colorimetry.
+ * L = 1 corresponds to 10000 cd/m2
+ * E - corresponding electrical signal
+ */
+ HAL_DATASPACE_TRANSFER_ST2084 = 6 << HAL_DATASPACE_TRANSFER_SHIFT,
+
+ /*
+ * ARIB STD-B67 Hybrid Log Gamma
+ *
+ * Transfer characteristic curve:
+ * E = r * L^0.5 for 0 <= L <= 1
+ * = a * ln(L - b) + c for 1 < L
+ * a = 0.17883277
+ * b = 0.28466892
+ * c = 0.55991073
+ * r = 0.5
+ * L - luminance of image 0 <= L for HDR colorimetry. L = 1 corresponds
+ * to reference white level of 100 cd/m2
+ * E - corresponding electrical signal
+ */
+ HAL_DATASPACE_TRANSFER_HLG = 7 << HAL_DATASPACE_TRANSFER_SHIFT,
+
+ HAL_DATASPACE_RANGE_SHIFT = 27,
+
+ /*
+ * Range aspect
+ *
+ * Defines the range of values corresponding to the unit range of 0-1.
+ * This is defined for YCbCr only, but can be expanded to RGB space.
+ */
+ HAL_DATASPACE_RANGE_MASK = 7 << HAL_DATASPACE_RANGE_SHIFT, // 0x7
+
+ /*
+ * Range is unknown or are determined by the application. Implementations
+ * shall use the following suggested ranges:
+ *
+ * All YCbCr formats: limited range.
+ * All RGB or RGBA formats (including RAW and Bayer): full range.
+ * All Y formats: full range
+ *
+ * For all other formats range is undefined, and implementations should use
+ * an appropriate range for the data represented.
+ */
+ HAL_DATASPACE_RANGE_UNSPECIFIED = 0 << HAL_DATASPACE_RANGE_SHIFT,
+
+ /*
+ * Full range uses all values for Y, Cb and Cr from
+ * 0 to 2^b-1, where b is the bit depth of the color format.
+ */
+ HAL_DATASPACE_RANGE_FULL = 1 << HAL_DATASPACE_RANGE_SHIFT,
+
+ /*
+ * Limited range uses values 16/256*2^b to 235/256*2^b for Y, and
+ * 1/16*2^b to 15/16*2^b for Cb, Cr, R, G and B, where b is the bit depth of
+ * the color format.
+ *
+ * E.g. For 8-bit-depth formats:
+ * Luma (Y) samples should range from 16 to 235, inclusive
+ * Chroma (Cb, Cr) samples should range from 16 to 240, inclusive
+ *
+ * For 10-bit-depth formats:
+ * Luma (Y) samples should range from 64 to 940, inclusive
+ * Chroma (Cb, Cr) samples should range from 64 to 960, inclusive
+ */
+ HAL_DATASPACE_RANGE_LIMITED = 2 << HAL_DATASPACE_RANGE_SHIFT,
+
+ /*
+ * Legacy dataspaces
*/
/*
@@ -601,34 +927,30 @@
* The values are encoded using the full range ([0,255] for 8-bit) for all
* components.
*/
- HAL_DATASPACE_SRGB_LINEAR = 0x200,
+ HAL_DATASPACE_SRGB_LINEAR_LEGACY = 0x200,
+
+ HAL_DATASPACE_SRGB_LINEAR = HAL_DATASPACE_STANDARD_BT709 |
+ HAL_DATASPACE_TRANSFER_LINEAR | HAL_DATASPACE_RANGE_FULL,
+
/*
* sRGB gamma encoding:
*
* The red, green and blue components are stored in sRGB space, and
- * converted to linear space when read, using the standard sRGB to linear
- * equation:
- *
- * Clinear = Csrgb / 12.92 for Csrgb <= 0.04045
- * = (Csrgb + 0.055 / 1.055)^2.4 for Csrgb > 0.04045
- *
- * When written the inverse transformation is performed:
- *
- * Csrgb = 12.92 * Clinear for Clinear <= 0.0031308
- * = 1.055 * Clinear^(1/2.4) - 0.055 for Clinear > 0.0031308
- *
+ * converted to linear space when read, using the SRGB transfer function
+ * for each of the R, G and B components. When written, the inverse
+ * transformation is performed.
*
* The alpha component, if present, is always stored in linear space and
* is left unmodified when read or written.
*
- * The RGB primaries and the white point are the same as BT.709.
- *
- * The values are encoded using the full range ([0,255] for 8-bit) for all
- * components.
- *
+ * Use full range and BT.709 standard.
*/
- HAL_DATASPACE_SRGB = 0x201,
+ HAL_DATASPACE_SRGB_LEGACY = 0x201,
+
+ HAL_DATASPACE_SRGB = HAL_DATASPACE_STANDARD_BT709 |
+ HAL_DATASPACE_TRANSFER_SRGB | HAL_DATASPACE_RANGE_FULL,
+
/*
* YCbCr Colorspaces
@@ -646,94 +968,53 @@
*
* Same model as BT.601-625, but all values (Y, Cb, Cr) range from 0 to 255
*
- * Transfer characteristic curve:
- * E = 1.099 * L ^ 0.45 - 0.099, 1.00 >= L >= 0.018
- * E = 4.500 L, 0.018 > L >= 0
- * L - luminance of image 0 <= L <= 1 for conventional colorimetry
- * E - corresponding electrical signal
- *
- * Primaries: x y
- * green 0.290 0.600
- * blue 0.150 0.060
- * red 0.640 0.330
- * white (D65) 0.3127 0.3290
+ * Use full range, BT.601 transfer and BT.601_625 standard.
*/
- HAL_DATASPACE_JFIF = 0x101,
+ HAL_DATASPACE_JFIF_LEGACY = 0x101,
+
+ HAL_DATASPACE_JFIF = HAL_DATASPACE_STANDARD_BT601_625 |
+ HAL_DATASPACE_TRANSFER_SMPTE_170M | HAL_DATASPACE_RANGE_FULL,
/*
* ITU-R Recommendation 601 (BT.601) - 625-line
*
* Standard-definition television, 625 Lines (PAL)
*
- * For 8-bit-depth formats:
- * Luma (Y) samples should range from 16 to 235, inclusive
- * Chroma (Cb, Cr) samples should range from 16 to 240, inclusive
- *
- * For 10-bit-depth formats:
- * Luma (Y) samples should range from 64 to 940, inclusive
- * Chroma (Cb, Cr) samples should range from 64 to 960, inclusive
- *
- * Transfer characteristic curve:
- * E = 1.099 * L ^ 0.45 - 0.099, 1.00 >= L >= 0.018
- * E = 4.500 L, 0.018 > L >= 0
- * L - luminance of image 0 <= L <= 1 for conventional colorimetry
- * E - corresponding electrical signal
- *
- * Primaries: x y
- * green 0.290 0.600
- * blue 0.150 0.060
- * red 0.640 0.330
- * white (D65) 0.3127 0.3290
+ * Use limited range, BT.601 transfer and BT.601_625 standard.
*/
- HAL_DATASPACE_BT601_625 = 0x102,
+ HAL_DATASPACE_BT601_625_LEGACY = 0x102,
+
+ HAL_DATASPACE_BT601_625 = HAL_DATASPACE_STANDARD_BT601_625 |
+ HAL_DATASPACE_TRANSFER_SMPTE_170M | HAL_DATASPACE_RANGE_LIMITED,
+
/*
* ITU-R Recommendation 601 (BT.601) - 525-line
*
* Standard-definition television, 525 Lines (NTSC)
*
- * For 8-bit-depth formats:
- * Luma (Y) samples should range from 16 to 235, inclusive
- * Chroma (Cb, Cr) samples should range from 16 to 240, inclusive
- *
- * For 10-bit-depth formats:
- * Luma (Y) samples should range from 64 to 940, inclusive
- * Chroma (Cb, Cr) samples should range from 64 to 960, inclusive
- *
- * Transfer characteristic curve:
- * E = 1.099 * L ^ 0.45 - 0.099, 1.00 >= L >= 0.018
- * E = 4.500 L, 0.018 > L >= 0
- * L - luminance of image 0 <= L <= 1 for conventional colorimetry
- * E - corresponding electrical signal
- *
- * Primaries: x y
- * green 0.310 0.595
- * blue 0.155 0.070
- * red 0.630 0.340
- * white (D65) 0.3127 0.3290
+ * Use limited range, BT.601 transfer and BT.601_525 standard.
*/
- HAL_DATASPACE_BT601_525 = 0x103,
+ HAL_DATASPACE_BT601_525_LEGACY = 0x103,
+
+ HAL_DATASPACE_BT601_525 = HAL_DATASPACE_STANDARD_BT601_525 |
+ HAL_DATASPACE_TRANSFER_SMPTE_170M | HAL_DATASPACE_RANGE_LIMITED,
/*
* ITU-R Recommendation 709 (BT.709)
*
* High-definition television
*
- * For 8-bit-depth formats:
- * Luma (Y) samples should range from 16 to 235, inclusive
- * Chroma (Cb, Cr) samples should range from 16 to 240, inclusive
- *
- * For 10-bit-depth formats:
- * Luma (Y) samples should range from 64 to 940, inclusive
- * Chroma (Cb, Cr) samples should range from 64 to 960, inclusive
- *
- * Primaries: x y
- * green 0.300 0.600
- * blue 0.150 0.060
- * red 0.640 0.330
- * white (D65) 0.3127 0.3290
+ * Use limited range, BT.709 transfer and BT.709 standard.
*/
- HAL_DATASPACE_BT709 = 0x104,
+ HAL_DATASPACE_BT709_LEGACY = 0x104,
+
+ HAL_DATASPACE_BT709 = HAL_DATASPACE_STANDARD_BT709 |
+ HAL_DATASPACE_TRANSFER_SMPTE_170M | HAL_DATASPACE_RANGE_LIMITED,
+
+ /*
+ * Data spaces for non-color formats
+ */
/*
* The buffer contains depth ranging measurements from a depth camera.
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 75d2a2d..a2d79e1 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -36,10 +36,6 @@
#ifdef __ANDROID__
// TODO(dimitry): move this to system properties.
static const char* kPublicNativeLibraries = "libandroid.so:"
- // TODO (dimitry): This is a workaround for http://b/26436837
- // will be removed before the release.
- "libart.so:"
- // END OF WORKAROUND
"libc.so:"
"libcamera2ndk.so:"
"libdl.so:"
@@ -62,12 +58,15 @@
class LibraryNamespaces {
public:
- LibraryNamespaces() : initialized_(false) { }
+ LibraryNamespaces() : initialized_(false) {
+ PreloadPublicLibraries();
+ }
android_namespace_t* GetOrCreate(JNIEnv* env, jobject class_loader,
bool is_shared,
jstring java_library_path,
- jstring java_permitted_path) {
+ jstring java_permitted_path,
+ int32_t target_sdk_version) {
ScopedUtfChars library_path(env, java_library_path);
std::string permitted_path;
@@ -76,7 +75,7 @@
permitted_path = path.c_str();
}
- if (!initialized_ && !InitPublicNamespace(library_path.c_str())) {
+ if (!initialized_ && !InitPublicNamespace(library_path.c_str(), target_sdk_version)) {
return nullptr;
}
@@ -108,18 +107,28 @@
}
private:
- bool InitPublicNamespace(const char* library_path) {
- // Make sure all the public libraries are loaded
+ void PreloadPublicLibraries() {
+ // android_init_namespaces() expects all the public libraries
+ // to be loaded so that they can be found by soname alone.
std::vector<std::string> sonames = android::base::Split(kPublicNativeLibraries, ":");
for (const auto& soname : sonames) {
- if (dlopen(soname.c_str(), RTLD_NOW | RTLD_NODELETE) == nullptr) {
- return false;
- }
+ dlopen(soname.c_str(), RTLD_NOW | RTLD_NODELETE);
}
+ }
+ bool InitPublicNamespace(const char* library_path, int32_t target_sdk_version) {
// Some apps call dlopen from generated code unknown to linker in which
// case linker uses anonymous namespace. See b/25844435 for details.
- initialized_ = android_init_namespaces(kPublicNativeLibraries, library_path);
+ std::string publicNativeLibraries = kPublicNativeLibraries;
+
+ // TODO (dimitry): This is a workaround for http://b/26436837
+ // will be removed before the release.
+ if (target_sdk_version <= 23) {
+ publicNativeLibraries += ":libart.so";
+ }
+ // END OF WORKAROUND
+
+ initialized_ = android_init_namespaces(publicNativeLibraries.c_str(), library_path);
return initialized_;
}
@@ -153,7 +162,7 @@
android_namespace_t* ns =
g_namespaces->GetOrCreate(env, class_loader, is_shared,
- java_library_path, java_permitted_path);
+ java_library_path, java_permitted_path, target_sdk_version);
if (ns == nullptr) {
return nullptr;
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index f160ac1..5ab957d 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -38,6 +38,7 @@
#include <processgroup/processgroup.h>
#define MEM_CGROUP_PATH "/dev/memcg/apps"
+#define MEM_CGROUP_TASKS "/dev/memcg/apps/tasks"
#define ACCT_CGROUP_PATH "/acct"
#define PROCESSGROUP_UID_PREFIX "uid_"
@@ -68,7 +69,10 @@
static const char* getCgroupRootPath() {
static const char* cgroup_root_path = NULL;
std::call_once(init_path_flag, [&]() {
- cgroup_root_path = access(MEM_CGROUP_PATH, W_OK) ? ACCT_CGROUP_PATH : MEM_CGROUP_PATH;
+ // Check if mem cgroup is mounted, only then check for write-access to avoid
+ // SELinux denials
+ cgroup_root_path = access(MEM_CGROUP_TASKS, F_OK) || access(MEM_CGROUP_PATH, W_OK) ?
+ ACCT_CGROUP_PATH : MEM_CGROUP_PATH;
});
return cgroup_root_path;
}
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp
index 23dcd62..739fad7 100644
--- a/libsysutils/src/NetlinkEvent.cpp
+++ b/libsysutils/src/NetlinkEvent.cpp
@@ -159,7 +159,7 @@
struct ifaddrmsg *ifaddr = (struct ifaddrmsg *) NLMSG_DATA(nh);
struct ifa_cacheinfo *cacheinfo = NULL;
char addrstr[INET6_ADDRSTRLEN] = "";
- char ifname[IFNAMSIZ];
+ char ifname[IFNAMSIZ] = "";
if (!checkRtNetlinkLength(nh, sizeof(*ifaddr)))
return false;
@@ -207,8 +207,7 @@
// Find the interface name.
if (!if_indextoname(ifaddr->ifa_index, ifname)) {
- SLOGE("Unknown ifindex %d in %s", ifaddr->ifa_index, msgtype);
- return false;
+ SLOGD("Unknown ifindex %d in %s", ifaddr->ifa_index, msgtype);
}
} else if (rta->rta_type == IFA_CACHEINFO) {
@@ -235,8 +234,7 @@
mAction = (type == RTM_NEWADDR) ? Action::kAddressUpdated :
Action::kAddressRemoved;
mSubsystem = strdup("net");
- asprintf(&mParams[0], "ADDRESS=%s/%d", addrstr,
- ifaddr->ifa_prefixlen);
+ asprintf(&mParams[0], "ADDRESS=%s/%d", addrstr, ifaddr->ifa_prefixlen);
asprintf(&mParams[1], "INTERFACE=%s", ifname);
asprintf(&mParams[2], "FLAGS=%u", ifaddr->ifa_flags);
asprintf(&mParams[3], "SCOPE=%u", ifaddr->ifa_scope);
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 511bf68..afc81ed 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -39,7 +39,7 @@
#endif
#define MEMCG_SYSFS_PATH "/dev/memcg/"
-#define MEMPRESSURE_WATCH_LEVEL "medium"
+#define MEMPRESSURE_WATCH_LEVEL "low"
#define ZONEINFO_PATH "/proc/zoneinfo"
#define LINE_MAX 128
diff --git a/rootdir/init.rc b/rootdir/init.rc
index e2ffe5d..0673255 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -85,7 +85,7 @@
# root memory control cgroup, used by lmkd
mkdir /dev/memcg 0700 root system
mount cgroup none /dev/memcg memory
- # app mem cgroups, used by activity manager and lmkd
+ # app mem cgroups, used by activity manager, lmkd and zygote
mkdir /dev/memcg/apps/ 0755 system system
write /proc/sys/kernel/panic_on_oops 1
@@ -400,6 +400,7 @@
mkdir /data/anr 0775 system system
# symlink to bugreport storage location
+ rm /data/bugreports
symlink /data/user_de/0/com.android.shell/files/bugreports /data/bugreports
# Separate location for storing security policy files on data
diff --git a/toolbox/Android.mk b/toolbox/Android.mk
index 52716e9..9ade759 100644
--- a/toolbox/Android.mk
+++ b/toolbox/Android.mk
@@ -80,10 +80,16 @@
$(LOCAL_PATH)/getevent.c: $(intermediates)/input.h-labels.h
+UAPI_INPUT_EVENT_CODES_H := bionic/libc/kernel/uapi/linux/input-event-codes.h
INPUT_H_LABELS_H := $(intermediates)/input.h-labels.h
$(INPUT_H_LABELS_H): PRIVATE_LOCAL_PATH := $(LOCAL_PATH)
-$(INPUT_H_LABELS_H): PRIVATE_CUSTOM_TOOL = $(PRIVATE_LOCAL_PATH)/generate-input.h-labels.py > $@
-$(INPUT_H_LABELS_H): $(LOCAL_PATH)/Android.mk $(LOCAL_PATH)/generate-input.h-labels.py
+# The PRIVATE_CUSTOM_TOOL line uses = to evaluate the output path late.
+# We copy the input path so it can't be accidentally modified later.
+$(INPUT_H_LABELS_H): PRIVATE_UAPI_INPUT_EVENT_CODES_H := $(UAPI_INPUT_EVENT_CODES_H)
+$(INPUT_H_LABELS_H): PRIVATE_CUSTOM_TOOL = $(PRIVATE_LOCAL_PATH)/generate-input.h-labels.py $(PRIVATE_UAPI_INPUT_EVENT_CODES_H) > $@
+# The dependency line though gets evaluated now, so the PRIVATE_ copy doesn't exist yet,
+# and the original can't yet have been modified, so this is both sufficient and necessary.
+$(INPUT_H_LABELS_H): $(LOCAL_PATH)/Android.mk $(LOCAL_PATH)/generate-input.h-labels.py $(UAPI_INPUT_EVENT_CODES_H)
$(INPUT_H_LABELS_H):
$(transform-generated-source)
diff --git a/toolbox/generate-input.h-labels.py b/toolbox/generate-input.h-labels.py
index ebb9588..30485a0 100755
--- a/toolbox/generate-input.h-labels.py
+++ b/toolbox/generate-input.h-labels.py
@@ -18,6 +18,7 @@
import os
import re
+import sys
input_prop_list = []
ev_list = []
@@ -36,7 +37,7 @@
r = re.compile(r'#define\s+(\S+)\s+((?:0x)?\d+)')
-with open('bionic/libc/kernel/uapi/linux/input.h', 'r') as f:
+with open(sys.argv[1], 'r') as f:
for line in f:
m = r.match(line)
if m: