Merge "crash_reporter: Fix paths for kernel panic collection"
diff --git a/debuggerd/debuggerd.rc b/debuggerd/debuggerd.rc
index e43fe96..1c6b9ff 100644
--- a/debuggerd/debuggerd.rc
+++ b/debuggerd/debuggerd.rc
@@ -1,4 +1,3 @@
service debuggerd /system/bin/debuggerd
- class main
group root readproc
writepid /dev/cpuset/system-background/tasks
diff --git a/debuggerd/debuggerd64.rc b/debuggerd/debuggerd64.rc
index 35b5af3..3e8847a 100644
--- a/debuggerd/debuggerd64.rc
+++ b/debuggerd/debuggerd64.rc
@@ -1,4 +1,3 @@
service debuggerd64 /system/bin/debuggerd64
- class main
group root readproc
writepid /dev/cpuset/system-background/tasks
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index fcec5b1..11d769b 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -65,6 +65,7 @@
libdiagnose_usb \
libbase \
libcutils \
+ libgtest_host \
# libf2fs_dlutils_host will dlopen("libf2fs_fmt_host_dyn")
LOCAL_CFLAGS_linux := -DUSE_F2FS
@@ -106,7 +107,11 @@
LOCAL_MODULE := fastboot_test
LOCAL_MODULE_HOST_OS := darwin linux windows
-LOCAL_SRC_FILES := socket.cpp socket_test.cpp
+LOCAL_SRC_FILES := \
+ socket.cpp \
+ socket_mock.cpp \
+ socket_test.cpp \
+
LOCAL_STATIC_LIBRARIES := libbase libcutils
LOCAL_CFLAGS += -Wall -Wextra -Werror -Wunreachable-code
diff --git a/fastboot/socket.cpp b/fastboot/socket.cpp
index d41f1fe..d49f47f 100644
--- a/fastboot/socket.cpp
+++ b/fastboot/socket.cpp
@@ -28,6 +28,7 @@
#include "socket.h"
+#include <android-base/errors.h>
#include <android-base/stringprintf.h>
Socket::Socket(cutils_socket_t sock) : sock_(sock) {}
@@ -77,6 +78,10 @@
return total;
}
+int Socket::GetLocalPort() {
+ return socket_get_local_port(sock_);
+}
+
// Implements the Socket interface for UDP.
class UdpSocket : public Socket {
public:
@@ -84,7 +89,8 @@
UdpSocket(Type type, cutils_socket_t sock);
- ssize_t Send(const void* data, size_t length) override;
+ bool Send(const void* data, size_t length) override;
+ bool Send(std::vector<cutils_socket_buffer_t> buffers) override;
ssize_t Receive(void* data, size_t length, int timeout_ms) override;
private:
@@ -104,9 +110,20 @@
}
}
-ssize_t UdpSocket::Send(const void* data, size_t length) {
+bool UdpSocket::Send(const void* data, size_t length) {
return TEMP_FAILURE_RETRY(sendto(sock_, reinterpret_cast<const char*>(data), length, 0,
- reinterpret_cast<sockaddr*>(addr_.get()), addr_size_));
+ reinterpret_cast<sockaddr*>(addr_.get()), addr_size_)) ==
+ static_cast<ssize_t>(length);
+}
+
+bool UdpSocket::Send(std::vector<cutils_socket_buffer_t> buffers) {
+ size_t total_length = 0;
+ for (const auto& buffer : buffers) {
+ total_length += buffer.length;
+ }
+
+ return TEMP_FAILURE_RETRY(socket_send_buffers_function_(
+ sock_, buffers.data(), buffers.size())) == static_cast<ssize_t>(total_length);
}
ssize_t UdpSocket::Receive(void* data, size_t length, int timeout_ms) {
@@ -130,7 +147,8 @@
public:
TcpSocket(cutils_socket_t sock) : Socket(sock) {}
- ssize_t Send(const void* data, size_t length) override;
+ bool Send(const void* data, size_t length) override;
+ bool Send(std::vector<cutils_socket_buffer_t> buffers) override;
ssize_t Receive(void* data, size_t length, int timeout_ms) override;
std::unique_ptr<Socket> Accept() override;
@@ -139,23 +157,52 @@
DISALLOW_COPY_AND_ASSIGN(TcpSocket);
};
-ssize_t TcpSocket::Send(const void* data, size_t length) {
- size_t total = 0;
+bool TcpSocket::Send(const void* data, size_t length) {
+ while (length > 0) {
+ ssize_t sent =
+ TEMP_FAILURE_RETRY(send(sock_, reinterpret_cast<const char*>(data), length, 0));
- while (total < length) {
- ssize_t bytes = TEMP_FAILURE_RETRY(
- send(sock_, reinterpret_cast<const char*>(data) + total, length - total, 0));
-
- if (bytes == -1) {
- if (total == 0) {
- return -1;
- }
- break;
+ if (sent == -1) {
+ return false;
}
- total += bytes;
+ length -= sent;
}
- return total;
+ return true;
+}
+
+bool TcpSocket::Send(std::vector<cutils_socket_buffer_t> buffers) {
+ while (!buffers.empty()) {
+ ssize_t sent = TEMP_FAILURE_RETRY(
+ socket_send_buffers_function_(sock_, buffers.data(), buffers.size()));
+
+ if (sent == -1) {
+ return false;
+ }
+
+ // Adjust the buffers to skip past the bytes we've just sent.
+ auto iter = buffers.begin();
+ while (sent > 0) {
+ if (iter->length > static_cast<size_t>(sent)) {
+ // Incomplete buffer write; adjust the buffer to point to the next byte to send.
+ iter->length -= sent;
+ iter->data = reinterpret_cast<const char*>(iter->data) + sent;
+ break;
+ }
+
+ // Complete buffer write; move on to the next buffer.
+ sent -= iter->length;
+ ++iter;
+ }
+
+ // Shortcut the common case: we've written everything remaining.
+ if (iter == buffers.end()) {
+ break;
+ }
+ buffers.erase(buffers.begin(), iter);
+ }
+
+ return true;
}
ssize_t TcpSocket::Receive(void* data, size_t length, int timeout_ms) {
@@ -210,3 +257,12 @@
return nullptr;
}
+
+std::string Socket::GetErrorMessage() {
+#if defined(_WIN32)
+ DWORD error_code = WSAGetLastError();
+#else
+ int error_code = errno;
+#endif
+ return android::base::SystemErrorCodeToString(error_code);
+}
diff --git a/fastboot/socket.h b/fastboot/socket.h
index 3e66c27..c0bd7c9 100644
--- a/fastboot/socket.h
+++ b/fastboot/socket.h
@@ -33,17 +33,25 @@
#ifndef SOCKET_H_
#define SOCKET_H_
+#include <functional>
#include <memory>
#include <string>
+#include <utility>
+#include <vector>
#include <android-base/macros.h>
#include <cutils/sockets.h>
+#include <gtest/gtest_prod.h>
// Socket interface to be implemented for each platform.
class Socket {
public:
enum class Protocol { kTcp, kUdp };
+ // Returns the socket error message. This must be called immediately after a socket failure
+ // before any other system calls are made.
+ static std::string GetErrorMessage();
+
// Creates a new client connection. Clients are connected to a specific hostname/port and can
// only send to that destination.
// On failure, |error| is filled (if non-null) and nullptr is returned.
@@ -60,8 +68,17 @@
virtual ~Socket();
// Sends |length| bytes of |data|. For TCP sockets this will continue trying to send until all
- // bytes are transmitted. Returns the number of bytes actually sent or -1 on error.
- virtual ssize_t Send(const void* data, size_t length) = 0;
+ // bytes are transmitted. Returns true on success.
+ virtual bool Send(const void* data, size_t length) = 0;
+
+ // Sends |buffers| using multi-buffer write, which can be significantly faster than making
+ // multiple calls. For UDP sockets |buffers| are all combined into a single datagram; for
+ // TCP sockets this will continue sending until all buffers are fully transmitted. Returns true
+ // on success.
+ //
+ // Note: This is non-functional for UDP server Sockets because it's not currently needed and
+ // would require an additional sendto() variation of multi-buffer write.
+ 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
@@ -78,6 +95,9 @@
// connected to the client on success, nullptr on failure.
virtual std::unique_ptr<Socket> Accept() { return nullptr; }
+ // Returns the local port the Socket is bound to or -1 on error.
+ int GetLocalPort();
+
protected:
// Protected constructor to force factory function use.
Socket(cutils_socket_t sock);
@@ -87,9 +107,17 @@
cutils_socket_t sock_ = INVALID_SOCKET;
+ // Non-class functions we want to override during tests to verify functionality. Implementation
+ // should call this rather than using socket_send_buffers() directly.
+ std::function<ssize_t(cutils_socket_t, cutils_socket_buffer_t*, size_t)>
+ socket_send_buffers_function_ = &socket_send_buffers;
+
private:
int receive_timeout_ms_ = 0;
+ FRIEND_TEST(SocketTest, TestTcpSendBuffers);
+ FRIEND_TEST(SocketTest, TestUdpSendBuffers);
+
DISALLOW_COPY_AND_ASSIGN(Socket);
};
diff --git a/fastboot/socket_mock.cpp b/fastboot/socket_mock.cpp
new file mode 100644
index 0000000..c962f30
--- /dev/null
+++ b/fastboot/socket_mock.cpp
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include "socket_mock.h"
+
+#include <gtest/gtest.h>
+
+SocketMock::SocketMock() : Socket(INVALID_SOCKET) {}
+
+SocketMock::~SocketMock() {
+ if (!events_.empty()) {
+ ADD_FAILURE() << events_.size() << " event(s) were not handled";
+ }
+}
+
+bool SocketMock::Send(const void* data, size_t length) {
+ if (events_.empty()) {
+ ADD_FAILURE() << "Send() was called when no message was expected";
+ return false;
+ }
+
+ if (events_.front().type != EventType::kSend) {
+ ADD_FAILURE() << "Send() was called out-of-order";
+ return false;
+ }
+
+ std::string message(reinterpret_cast<const char*>(data), length);
+ if (events_.front().message != message) {
+ ADD_FAILURE() << "Send() expected " << events_.front().message << ", but got " << message;
+ return false;
+ }
+
+ bool return_value = events_.front().return_value;
+ events_.pop();
+ return return_value;
+}
+
+// Mock out multi-buffer send to be one large send, since that's what it should looks like from
+// the user's perspective.
+bool SocketMock::Send(std::vector<cutils_socket_buffer_t> buffers) {
+ std::string data;
+ for (const auto& buffer : buffers) {
+ data.append(reinterpret_cast<const char*>(buffer.data), buffer.length);
+ }
+ return Send(data.data(), data.size());
+}
+
+ssize_t SocketMock::Receive(void* data, size_t length, int /*timeout_ms*/) {
+ if (events_.empty()) {
+ ADD_FAILURE() << "Receive() was called when no message was ready";
+ return -1;
+ }
+
+ if (events_.front().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;
+ return -1;
+ }
+
+ ssize_t return_value = events_.front().return_value;
+ if (return_value > 0) {
+ memcpy(data, events_.front().message.data(), return_value);
+ }
+ events_.pop();
+ return return_value;
+}
+
+int SocketMock::Close() {
+ return 0;
+}
+
+std::unique_ptr<Socket> SocketMock::Accept() {
+ if (events_.empty()) {
+ ADD_FAILURE() << "Accept() was called when no socket was ready";
+ return nullptr;
+ }
+
+ if (events_.front().type != EventType::kAccept) {
+ ADD_FAILURE() << "Accept() was called out-of-order";
+ return nullptr;
+ }
+
+ std::unique_ptr<Socket> sock = std::move(events_.front().sock);
+ events_.pop();
+ return sock;
+}
+
+void SocketMock::ExpectSend(std::string message) {
+ events_.push(Event(EventType::kSend, std::move(message), true, nullptr));
+}
+
+void SocketMock::ExpectSendFailure(std::string message) {
+ events_.push(Event(EventType::kSend, std::move(message), false, nullptr));
+}
+
+void SocketMock::AddReceive(std::string message) {
+ ssize_t return_value = message.length();
+ events_.push(Event(EventType::kReceive, std::move(message), return_value, nullptr));
+}
+
+void SocketMock::AddReceiveFailure() {
+ events_.push(Event(EventType::kReceive, "", -1, nullptr));
+}
+
+void SocketMock::AddAccept(std::unique_ptr<Socket> sock) {
+ events_.push(Event(EventType::kAccept, "", 0, std::move(sock)));
+}
+
+SocketMock::Event::Event(EventType _type, std::string _message, ssize_t _return_value,
+ std::unique_ptr<Socket> _sock)
+ : type(_type), message(_message), return_value(_return_value), sock(std::move(_sock)) {}
diff --git a/fastboot/socket_mock.h b/fastboot/socket_mock.h
new file mode 100644
index 0000000..41fe06d
--- /dev/null
+++ b/fastboot/socket_mock.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2016 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 SOCKET_MOCK_H_
+#define SOCKET_MOCK_H_
+
+#include <memory>
+#include <queue>
+#include <string>
+
+#include <android-base/macros.h>
+
+#include "socket.h"
+
+// A mock Socket implementation to be used for testing. Tests can set expectations for messages
+// to be sent and provide messages to be received in order to verify protocol behavior.
+//
+// Example: testing sending "foo" and receiving "bar".
+// SocketMock mock;
+// mock.ExpectSend("foo");
+// mock.AddReceive("bar");
+// EXPECT_TRUE(DoFooBar(&mock));
+//
+// Example: testing sending "foo" and expecting "bar", but receiving "baz" instead.
+// SocketMock mock;
+// mock.ExpectSend("foo");
+// mock.AddReceive("baz");
+// EXPECT_FALSE(DoFooBar(&mock));
+class SocketMock : public Socket {
+ public:
+ SocketMock();
+ ~SocketMock() override;
+
+ bool Send(const void* data, size_t length) override;
+ bool Send(std::vector<cutils_socket_buffer_t> buffers) override;
+ ssize_t Receive(void* data, size_t length, int timeout_ms) override;
+ int Close() override;
+ virtual std::unique_ptr<Socket> Accept();
+
+ // Adds an expectation for Send().
+ void ExpectSend(std::string message);
+
+ // Adds an expectation for Send() that returns false.
+ void ExpectSendFailure(std::string message);
+
+ // Adds data to provide for Receive().
+ void AddReceive(std::string message);
+
+ // Adds a Receive() failure.
+ void AddReceiveFailure();
+
+ // Adds a Socket to return from Accept().
+ void AddAccept(std::unique_ptr<Socket> sock);
+
+ private:
+ enum class EventType { kSend, kReceive, kAccept };
+
+ struct Event {
+ Event(EventType _type, std::string _message, ssize_t _return_value,
+ std::unique_ptr<Socket> _sock);
+
+ EventType type;
+ std::string message;
+ ssize_t return_value;
+ std::unique_ptr<Socket> sock;
+ };
+
+ std::queue<Event> events_;
+
+ DISALLOW_COPY_AND_ASSIGN(SocketMock);
+};
+
+#endif // SOCKET_MOCK_H_
diff --git a/fastboot/socket_test.cpp b/fastboot/socket_test.cpp
index 1fd9d7c..cc71075 100644
--- a/fastboot/socket_test.cpp
+++ b/fastboot/socket_test.cpp
@@ -14,33 +14,33 @@
* limitations under the License.
*/
-// Tests UDP functionality using loopback connections. Requires that kTestPort is available
-// for loopback communication on the host. These tests also assume that no UDP packets are lost,
-// which should be the case for loopback communication, but is not guaranteed.
+// Tests socket functionality using loopback connections. The UDP tests assume that no packets are
+// lost, which should be the case for loopback communication, but is not guaranteed.
+//
+// Also tests our SocketMock class to make sure it works as expected and reports errors properly
+// if the mock expectations aren't met during a test.
#include "socket.h"
+#include "socket_mock.h"
+#include <list>
+
+#include <gtest/gtest-spi.h>
#include <gtest/gtest.h>
-enum {
- // This port must be available for loopback communication.
- kTestPort = 54321,
-
- // Don't wait forever in a unit test.
- kTestTimeoutMs = 3000,
-};
+enum { kTestTimeoutMs = 3000 };
// Creates connected sockets |server| and |client|. Returns true on success.
bool MakeConnectedSockets(Socket::Protocol protocol, std::unique_ptr<Socket>* server,
- std::unique_ptr<Socket>* client, const std::string hostname = "localhost",
- int port = kTestPort) {
- *server = Socket::NewServer(protocol, port);
+ std::unique_ptr<Socket>* client,
+ const std::string hostname = "localhost") {
+ *server = Socket::NewServer(protocol, 0);
if (*server == nullptr) {
ADD_FAILURE() << "Failed to create server.";
return false;
}
- *client = Socket::NewClient(protocol, hostname, port, nullptr);
+ *client = Socket::NewClient(protocol, hostname, (*server)->GetLocalPort(), nullptr);
if (*client == nullptr) {
ADD_FAILURE() << "Failed to create client.";
return false;
@@ -61,7 +61,7 @@
// Sends a string over a Socket. Returns true if the full string (without terminating char)
// was sent.
static bool SendString(Socket* sock, const std::string& message) {
- return sock->Send(message.c_str(), message.length()) == static_cast<ssize_t>(message.length());
+ return sock->Send(message.c_str(), message.length());
}
// Receives a string from a Socket. Returns true if the full string (without terminating char)
@@ -124,3 +124,212 @@
EXPECT_EQ(-1, bytes);
}
}
+
+// Tests UDP multi-buffer send.
+TEST(SocketTest, TestUdpSendBuffers) {
+ std::unique_ptr<Socket> sock = Socket::NewServer(Socket::Protocol::kUdp, 0);
+ std::vector<std::string> data{"foo", "bar", "12345"};
+ std::vector<cutils_socket_buffer_t> buffers{{data[0].data(), data[0].length()},
+ {data[1].data(), data[1].length()},
+ {data[2].data(), data[2].length()}};
+ ssize_t mock_return_value = 0;
+
+ // Mock out socket_send_buffers() to verify we're sending in the correct buffers and
+ // return |mock_return_value|.
+ sock->socket_send_buffers_function_ = [&buffers, &mock_return_value](
+ cutils_socket_t /*cutils_sock*/, cutils_socket_buffer_t* sent_buffers,
+ size_t num_sent_buffers) -> ssize_t {
+ EXPECT_EQ(buffers.size(), num_sent_buffers);
+ for (size_t i = 0; i < num_sent_buffers; ++i) {
+ EXPECT_EQ(buffers[i].data, sent_buffers[i].data);
+ EXPECT_EQ(buffers[i].length, sent_buffers[i].length);
+ }
+ return mock_return_value;
+ };
+
+ mock_return_value = strlen("foobar12345");
+ EXPECT_TRUE(sock->Send(buffers));
+
+ mock_return_value -= 1;
+ EXPECT_FALSE(sock->Send(buffers));
+
+ mock_return_value = 0;
+ EXPECT_FALSE(sock->Send(buffers));
+
+ mock_return_value = -1;
+ EXPECT_FALSE(sock->Send(buffers));
+}
+
+// Tests TCP re-sending until socket_send_buffers() sends all data. This is a little complicated,
+// but the general idea is that we intercept calls to socket_send_buffers() using a lambda mock
+// function that simulates partial writes.
+TEST(SocketTest, TestTcpSendBuffers) {
+ std::unique_ptr<Socket> sock = Socket::NewServer(Socket::Protocol::kTcp, 0);
+ std::vector<std::string> data{"foo", "bar", "12345"};
+ std::vector<cutils_socket_buffer_t> buffers{{data[0].data(), data[0].length()},
+ {data[1].data(), data[1].length()},
+ {data[2].data(), data[2].length()}};
+
+ // Test breaking up the buffered send at various points.
+ std::list<std::string> test_sends[] = {
+ // Successes.
+ {"foobar12345"},
+ {"f", "oob", "ar12345"},
+ {"fo", "obar12", "345"},
+ {"foo", "bar12345"},
+ {"foob", "ar123", "45"},
+ {"f", "o", "o", "b", "a", "r", "1", "2", "3", "4", "5"},
+
+ // Failures.
+ {},
+ {"f"},
+ {"foo", "bar"},
+ {"fo", "obar12"},
+ {"foobar1234"}
+ };
+
+ for (auto& test : test_sends) {
+ ssize_t bytes_sent = 0;
+ bool expect_success = true;
+
+ // Create a mock function for custom socket_send_buffers() behavior. This function will
+ // check to make sure the input buffers start at the next unsent byte, then return the
+ // number of bytes indicated by the next entry in |test|.
+ sock->socket_send_buffers_function_ = [&bytes_sent, &data, &expect_success, &test](
+ cutils_socket_t /*cutils_sock*/, cutils_socket_buffer_t* buffers,
+ size_t num_buffers) -> ssize_t {
+ EXPECT_TRUE(num_buffers > 0);
+
+ // Failure case - pretend we errored out before sending all the buffers.
+ if (test.empty()) {
+ expect_success = false;
+ return -1;
+ }
+
+ // Count the bytes we've sent to find where the next buffer should start and how many
+ // bytes should be left in it.
+ size_t byte_count = bytes_sent, data_index = 0;
+ while (data_index < data.size()) {
+ if (byte_count >= data[data_index].length()) {
+ byte_count -= data[data_index].length();
+ ++data_index;
+ } else {
+ break;
+ }
+ }
+ void* expected_next_byte = &data[data_index][byte_count];
+ size_t expected_next_size = data[data_index].length() - byte_count;
+
+ EXPECT_EQ(data.size() - data_index, num_buffers);
+ EXPECT_EQ(expected_next_byte, buffers[0].data);
+ EXPECT_EQ(expected_next_size, buffers[0].length);
+
+ std::string to_send = std::move(test.front());
+ test.pop_front();
+ bytes_sent += to_send.length();
+ return to_send.length();
+ };
+
+ EXPECT_EQ(expect_success, sock->Send(buffers));
+ EXPECT_TRUE(test.empty());
+ }
+}
+
+TEST(SocketMockTest, TestSendSuccess) {
+ SocketMock mock;
+
+ mock.ExpectSend("foo");
+ EXPECT_TRUE(SendString(&mock, "foo"));
+
+ mock.ExpectSend("abc");
+ mock.ExpectSend("123");
+ EXPECT_TRUE(SendString(&mock, "abc"));
+ EXPECT_TRUE(SendString(&mock, "123"));
+}
+
+TEST(SocketMockTest, TestSendFailure) {
+ SocketMock* mock = new SocketMock;
+
+ mock->ExpectSendFailure("foo");
+ EXPECT_FALSE(SendString(mock, "foo"));
+
+ EXPECT_NONFATAL_FAILURE(SendString(mock, "foo"), "no message was expected");
+
+ mock->ExpectSend("foo");
+ EXPECT_NONFATAL_FAILURE(SendString(mock, "bar"), "expected foo, but got bar");
+ EXPECT_TRUE(SendString(mock, "foo"));
+
+ mock->AddReceive("foo");
+ EXPECT_NONFATAL_FAILURE(SendString(mock, "foo"), "called out-of-order");
+ EXPECT_TRUE(ReceiveString(mock, "foo"));
+
+ mock->ExpectSend("foo");
+ EXPECT_NONFATAL_FAILURE(delete mock, "1 event(s) were not handled");
+}
+
+TEST(SocketMockTest, TestReceiveSuccess) {
+ SocketMock mock;
+
+ mock.AddReceive("foo");
+ EXPECT_TRUE(ReceiveString(&mock, "foo"));
+
+ mock.AddReceive("abc");
+ mock.AddReceive("123");
+ EXPECT_TRUE(ReceiveString(&mock, "abc"));
+ EXPECT_TRUE(ReceiveString(&mock, "123"));
+
+ // Make sure ReceiveAll() can piece together multiple receives.
+ mock.AddReceive("foo");
+ mock.AddReceive("bar");
+ mock.AddReceive("123");
+ EXPECT_TRUE(ReceiveString(&mock, "foobar123"));
+}
+
+TEST(SocketMockTest, TestReceiveFailure) {
+ SocketMock* mock = new SocketMock;
+
+ mock->AddReceiveFailure();
+ EXPECT_FALSE(ReceiveString(mock, "foo"));
+
+ mock->AddReceive("foo");
+ mock->AddReceiveFailure();
+ EXPECT_FALSE(ReceiveString(mock, "foobar"));
+
+ EXPECT_NONFATAL_FAILURE(ReceiveString(mock, "foo"), "no message was ready");
+
+ mock->ExpectSend("foo");
+ EXPECT_NONFATAL_FAILURE(ReceiveString(mock, "foo"), "called out-of-order");
+ EXPECT_TRUE(SendString(mock, "foo"));
+
+ char c;
+ mock->AddReceive("foo");
+ EXPECT_NONFATAL_FAILURE(mock->Receive(&c, 1, 0), "not enough bytes (1) for foo");
+ EXPECT_TRUE(ReceiveString(mock, "foo"));
+
+ mock->AddReceive("foo");
+ EXPECT_NONFATAL_FAILURE(delete mock, "1 event(s) were not handled");
+}
+
+TEST(SocketMockTest, TestAcceptSuccess) {
+ SocketMock mock;
+
+ SocketMock* mock_handler = new SocketMock;
+ mock.AddAccept(std::unique_ptr<SocketMock>(mock_handler));
+ EXPECT_EQ(mock_handler, mock.Accept().get());
+
+ mock.AddAccept(nullptr);
+ EXPECT_EQ(nullptr, mock.Accept().get());
+}
+
+TEST(SocketMockTest, TestAcceptFailure) {
+ SocketMock* mock = new SocketMock;
+
+ EXPECT_NONFATAL_FAILURE(mock->Accept(), "no socket was ready");
+
+ mock->ExpectSend("foo");
+ EXPECT_NONFATAL_FAILURE(mock->Accept(), "called out-of-order");
+ EXPECT_TRUE(SendString(mock, "foo"));
+
+ mock->AddAccept(nullptr);
+ EXPECT_NONFATAL_FAILURE(delete mock, "1 event(s) were not handled");
+}
diff --git a/include/cutils/sockets.h b/include/cutils/sockets.h
index e25c555..783bd0b 100644
--- a/include/cutils/sockets.h
+++ b/include/cutils/sockets.h
@@ -132,6 +132,36 @@
int socket_set_receive_timeout(cutils_socket_t sock, int timeout_ms);
/*
+ * Returns the local port the socket is bound to or -1 on error.
+ */
+int socket_get_local_port(cutils_socket_t sock);
+
+/*
+ * Sends to a socket from multiple buffers; wraps writev() on Unix or WSASend()
+ * on Windows. This can give significant speedup compared to calling send()
+ * multiple times.
+ *
+ * Example usage:
+ * cutils_socket_buffer_t buffers[2] = { {data0, len0}, {data1, len1} };
+ * socket_send_buffers(sock, buffers, 2);
+ *
+ * If you try to pass more than SOCKET_SEND_BUFFERS_MAX_BUFFERS buffers into
+ * this function it will return -1 without sending anything.
+ *
+ * Returns the number of bytes written or -1 on error.
+ */
+typedef struct {
+ const void* data;
+ size_t length;
+} cutils_socket_buffer_t;
+
+#define SOCKET_SEND_BUFFERS_MAX_BUFFERS 16
+
+ssize_t socket_send_buffers(cutils_socket_t sock,
+ const cutils_socket_buffer_t* buffers,
+ size_t num_buffers);
+
+/*
* socket_peer_is_trusted - Takes a socket which is presumed to be a
* connected local socket (e.g. AF_LOCAL) and returns whether the peer
* (the userid that owns the process on the other end of that socket)
diff --git a/init/devices.cpp b/init/devices.cpp
index d556e30..39cd706 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -40,6 +40,7 @@
#include <sys/time.h>
#include <sys/wait.h>
+#include <android-base/file.h>
#include <cutils/list.h>
#include <cutils/uevent.h>
@@ -769,21 +770,13 @@
ret = -1;
break;
}
-
- len_to_copy -= nr;
- while (nr > 0) {
- ssize_t nw = 0;
-
- nw = write(data_fd, buf + nw, nr);
- if(nw <= 0) {
- ret = -1;
- goto out;
- }
- nr -= nw;
+ if (!android::base::WriteFully(data_fd, buf, nr)) {
+ ret = -1;
+ break;
}
+ len_to_copy -= nr;
}
-out:
if(!ret)
write(loading_fd, "0", 1); /* successful end of transfer */
else
diff --git a/libcutils/Android.mk b/libcutils/Android.mk
index 25b056b..51c6d9d 100644
--- a/libcutils/Android.mk
+++ b/libcutils/Android.mk
@@ -17,21 +17,22 @@
include $(CLEAR_VARS)
libcutils_common_sources := \
- hashmap.c \
atomic.c.arm \
- native_handle.c \
config_utils.c \
+ fs_config.c \
+ hashmap.c \
+ iosched_policy.c \
load_file.c \
- strlcpy.c \
+ native_handle.c \
open_memstream.c \
+ process_name.c \
+ record_stream.c \
+ sched_policy.c \
+ sockets.cpp \
strdup16to8.c \
strdup8to16.c \
- record_stream.c \
- process_name.c \
+ strlcpy.c \
threads.c \
- sched_policy.c \
- iosched_policy.c \
- fs_config.c
# some files must not be compiled when building against Mingw
# they correspond to features not used by our host development tools
@@ -45,7 +46,7 @@
socket_loopback_client_unix.c \
socket_loopback_server_unix.c \
socket_network_client_unix.c \
- sockets_unix.c \
+ sockets_unix.cpp \
str_parms.c \
libcutils_nonwindows_host_sources := \
@@ -55,7 +56,7 @@
libcutils_windows_host_sources := \
socket_inaddr_any_server_windows.c \
socket_network_client_windows.c \
- sockets_windows.c \
+ sockets_windows.cpp \
# Shared and static library for host
# Note: when linking this library on Windows, you must also link to Winsock2
diff --git a/libcutils/ashmem-dev.c b/libcutils/ashmem-dev.c
index 3089a94..a5203e1 100644
--- a/libcutils/ashmem-dev.c
+++ b/libcutils/ashmem-dev.c
@@ -20,17 +20,19 @@
* used by the simulator.
*/
-#include <unistd.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/ioctl.h>
+#include <errno.h>
#include <fcntl.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
#include <linux/ashmem.h>
+
#include <cutils/ashmem.h>
-#define ASHMEM_DEVICE "/dev/ashmem"
+#define ASHMEM_DEVICE "/dev/ashmem"
/*
* ashmem_create_region - creates a new ashmem region and returns the file
@@ -41,50 +43,55 @@
*/
int ashmem_create_region(const char *name, size_t size)
{
- int fd, ret;
+ int ret, save_errno;
- fd = open(ASHMEM_DEVICE, O_RDWR);
- if (fd < 0)
- return fd;
+ int fd = TEMP_FAILURE_RETRY(open(ASHMEM_DEVICE, O_RDWR));
+ if (fd < 0) {
+ return fd;
+ }
- if (name) {
- char buf[ASHMEM_NAME_LEN] = {0};
+ if (name) {
+ char buf[ASHMEM_NAME_LEN] = {0};
- strlcpy(buf, name, sizeof(buf));
- ret = ioctl(fd, ASHMEM_SET_NAME, buf);
- if (ret < 0)
- goto error;
- }
+ strlcpy(buf, name, sizeof(buf));
+ ret = TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_NAME, buf));
+ if (ret < 0) {
+ goto error;
+ }
+ }
- ret = ioctl(fd, ASHMEM_SET_SIZE, size);
- if (ret < 0)
- goto error;
+ ret = TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_SIZE, size));
+ if (ret < 0) {
+ goto error;
+ }
- return fd;
+ return fd;
error:
- close(fd);
- return ret;
+ save_errno = errno;
+ close(fd);
+ errno = save_errno;
+ return ret;
}
int ashmem_set_prot_region(int fd, int prot)
{
- return ioctl(fd, ASHMEM_SET_PROT_MASK, prot);
+ return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_PROT_MASK, prot));
}
int ashmem_pin_region(int fd, size_t offset, size_t len)
{
- struct ashmem_pin pin = { offset, len };
- return ioctl(fd, ASHMEM_PIN, &pin);
+ struct ashmem_pin pin = { offset, len };
+ return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_PIN, &pin));
}
int ashmem_unpin_region(int fd, size_t offset, size_t len)
{
- struct ashmem_pin pin = { offset, len };
- return ioctl(fd, ASHMEM_UNPIN, &pin);
+ struct ashmem_pin pin = { offset, len };
+ return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_UNPIN, &pin));
}
int ashmem_get_size_region(int fd)
{
- return ioctl(fd, ASHMEM_GET_SIZE, NULL);
+ return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_GET_SIZE, NULL));
}
diff --git a/libcutils/ashmem-host.c b/libcutils/ashmem-host.c
index 15dd43e..c85f06b 100644
--- a/libcutils/ashmem-host.c
+++ b/libcutils/ashmem-host.c
@@ -78,8 +78,11 @@
return -1;
}
- // Check if this is an "ashmem" region.
- // TODO: This is very hacky, and can easily break. We need some reliable indicator.
+ /*
+ * Check if this is an "ashmem" region.
+ * TODO: This is very hacky, and can easily break.
+ * We need some reliable indicator.
+ */
if (!(buf.st_nlink == 0 && S_ISREG(buf.st_mode))) {
errno = ENOTTY;
return -1;
diff --git a/libcutils/sockets.cpp b/libcutils/sockets.cpp
new file mode 100644
index 0000000..d9ab146
--- /dev/null
+++ b/libcutils/sockets.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2016 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 contains socket implementation that can be shared between
+// platforms as long as the correct headers are included.
+
+#include <cutils/sockets.h>
+
+#if !defined(_WIN32)
+#include <netinet/in.h>
+#endif
+
+int socket_get_local_port(cutils_socket_t sock) {
+ sockaddr_storage addr;
+ socklen_t addr_size = sizeof(addr);
+
+ if (getsockname(sock, reinterpret_cast<sockaddr*>(&addr), &addr_size) == 0) {
+ // sockaddr_in and sockaddr_in6 always overlap the port field.
+ return ntohs(reinterpret_cast<sockaddr_in*>(&addr)->sin_port);
+ }
+ return -1;
+}
diff --git a/libcutils/sockets_unix.c b/libcutils/sockets_unix.c
deleted file mode 100644
index 5eddc4b..0000000
--- a/libcutils/sockets_unix.c
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2011 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 <cutils/sockets.h>
-#include <log/log.h>
-
-#if defined(__ANDROID__)
-/* For the socket trust (credentials) check */
-#include <private/android_filesystem_config.h>
-#define __android_unused
-#else
-#define __android_unused __attribute__((__unused__))
-#endif
-
-bool socket_peer_is_trusted(int fd __android_unused)
-{
-#if defined(__ANDROID__)
- struct ucred cr;
- socklen_t len = sizeof(cr);
- int n = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len);
-
- if (n != 0) {
- ALOGE("could not get socket credentials: %s\n", strerror(errno));
- return false;
- }
-
- if ((cr.uid != AID_ROOT) && (cr.uid != AID_SHELL)) {
- ALOGE("untrusted userid on other end of socket: userid %d\n", cr.uid);
- return false;
- }
-#endif
-
- return true;
-}
-
-int socket_close(int sock) {
- return close(sock);
-}
-
-int socket_set_receive_timeout(cutils_socket_t sock, int timeout_ms) {
- struct timeval tv;
- tv.tv_sec = timeout_ms / 1000;
- tv.tv_usec = (timeout_ms % 1000) * 1000;
- return setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
-}
diff --git a/libcutils/sockets_unix.cpp b/libcutils/sockets_unix.cpp
new file mode 100644
index 0000000..8747d69
--- /dev/null
+++ b/libcutils/sockets_unix.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2011 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 <cutils/sockets.h>
+
+#include <sys/uio.h>
+
+#include <log/log.h>
+
+#if defined(__ANDROID__)
+/* For the socket trust (credentials) check */
+#include <private/android_filesystem_config.h>
+#define __android_unused
+#else
+#define __android_unused __attribute__((__unused__))
+#endif
+
+bool socket_peer_is_trusted(int fd __android_unused) {
+#if defined(__ANDROID__)
+ ucred cr;
+ socklen_t len = sizeof(cr);
+ int n = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len);
+
+ if (n != 0) {
+ ALOGE("could not get socket credentials: %s\n", strerror(errno));
+ return false;
+ }
+
+ if ((cr.uid != AID_ROOT) && (cr.uid != AID_SHELL)) {
+ ALOGE("untrusted userid on other end of socket: userid %d\n", cr.uid);
+ return false;
+ }
+#endif
+
+ return true;
+}
+
+int socket_close(int sock) {
+ return close(sock);
+}
+
+int socket_set_receive_timeout(cutils_socket_t sock, int timeout_ms) {
+ timeval tv;
+ tv.tv_sec = timeout_ms / 1000;
+ tv.tv_usec = (timeout_ms % 1000) * 1000;
+ return setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
+}
+
+ssize_t socket_send_buffers(cutils_socket_t sock,
+ const cutils_socket_buffer_t* buffers,
+ size_t num_buffers) {
+ if (num_buffers > SOCKET_SEND_BUFFERS_MAX_BUFFERS) {
+ return -1;
+ }
+
+ iovec iovec_buffers[SOCKET_SEND_BUFFERS_MAX_BUFFERS];
+ for (size_t i = 0; i < num_buffers; ++i) {
+ // It's safe to cast away const here; iovec declares non-const
+ // void* because it's used for both send and receive, but since
+ // we're only sending, the data won't be modified.
+ iovec_buffers[i].iov_base = const_cast<void*>(buffers[i].data);
+ iovec_buffers[i].iov_len = buffers[i].length;
+ }
+
+ return writev(sock, iovec_buffers, num_buffers);
+}
diff --git a/libcutils/sockets_windows.c b/libcutils/sockets_windows.cpp
similarity index 69%
rename from libcutils/sockets_windows.c
rename to libcutils/sockets_windows.cpp
index 1bf2933..ed6b1a7 100644
--- a/libcutils/sockets_windows.c
+++ b/libcutils/sockets_windows.cpp
@@ -37,7 +37,7 @@
// Both adb (1) and Chrome (2) purposefully avoid WSACleanup() with no issues.
// (1) https://android.googlesource.com/platform/system/core.git/+/master/adb/sysdeps_win32.cpp
// (2) https://code.google.com/p/chromium/codesearch#chromium/src/net/base/winsock_init.cc
-bool initialize_windows_sockets() {
+extern "C" bool initialize_windows_sockets() {
// There's no harm in calling WSAStartup() multiple times but no benefit
// either, we may as well skip it after the first.
static bool init_success = false;
@@ -55,6 +55,32 @@
}
int socket_set_receive_timeout(cutils_socket_t sock, int timeout_ms) {
- return setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout_ms,
- sizeof(timeout_ms));
+ return setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,
+ reinterpret_cast<char*>(&timeout_ms), sizeof(timeout_ms));
+}
+
+ssize_t socket_send_buffers(cutils_socket_t sock,
+ const cutils_socket_buffer_t* buffers,
+ size_t num_buffers) {
+ if (num_buffers > SOCKET_SEND_BUFFERS_MAX_BUFFERS) {
+ return -1;
+ }
+
+ WSABUF wsa_buffers[SOCKET_SEND_BUFFERS_MAX_BUFFERS];
+ for (size_t i = 0; i < num_buffers; ++i) {
+ // It's safe to cast away const here; WSABUF declares non-const
+ // void* because it's used for both send and receive, but since
+ // we're only sending, the data won't be modified.
+ wsa_buffers[i].buf =
+ reinterpret_cast<char*>(const_cast<void*>(buffers[i].data));
+ wsa_buffers[i].len = buffers[i].length;
+ }
+
+ DWORD bytes_sent = 0;
+ if (WSASend(sock, wsa_buffers, num_buffers, &bytes_sent, 0, nullptr,
+ nullptr) != SOCKET_ERROR) {
+ return bytes_sent;
+ }
+
+ return -1;
}
diff --git a/libcutils/tests/sockets_test.cpp b/libcutils/tests/sockets_test.cpp
index 966dfe7..0f682a2 100644
--- a/libcutils/tests/sockets_test.cpp
+++ b/libcutils/tests/sockets_test.cpp
@@ -15,9 +15,8 @@
*/
// Tests socket functionality using loopback connections. Requires IPv4 and
-// IPv6 capabilities, and that kTestPort is available for loopback
-// communication. These tests also assume that no UDP packets are lost,
-// which should be the case for loopback communication, but is not guaranteed.
+// IPv6 capabilities. These tests assume that no UDP packets are lost, which
+// should be the case for loopback communication, but is not guaranteed.
#include <cutils/sockets.h>
@@ -25,11 +24,6 @@
#include <gtest/gtest.h>
-enum {
- // This port must be available for loopback communication.
- kTestPort = 54321
-};
-
// Makes sure the passed sockets are valid, sends data between them, and closes
// them. Any failures are logged with gtest.
//
@@ -40,30 +34,39 @@
ASSERT_NE(INVALID_SOCKET, server);
ASSERT_NE(INVALID_SOCKET, client);
- char buffer[3];
+ char buffer[128];
sockaddr_storage addr;
socklen_t addr_size = sizeof(addr);
// Send client -> server first to get the UDP client's address.
ASSERT_EQ(3, send(client, "foo", 3, 0));
if (type == SOCK_DGRAM) {
- EXPECT_EQ(3, recvfrom(server, buffer, 3, 0,
- reinterpret_cast<sockaddr*>(&addr), &addr_size));
+ EXPECT_EQ(3, recvfrom(server, buffer, sizeof(buffer), 0,
+ reinterpret_cast<sockaddr*>(&addr), &addr_size));
} else {
- EXPECT_EQ(3, recv(server, buffer, 3, 0));
+ EXPECT_EQ(3, recv(server, buffer, sizeof(buffer), 0));
}
EXPECT_EQ(0, memcmp(buffer, "foo", 3));
// Now send server -> client.
if (type == SOCK_DGRAM) {
- ASSERT_EQ(3, sendto(server, "bar", 3, 0,
- reinterpret_cast<sockaddr*>(&addr), addr_size));
+ ASSERT_EQ(3, sendto(server, "bar", 3, 0,
+ reinterpret_cast<sockaddr*>(&addr), addr_size));
} else {
- ASSERT_EQ(3, send(server, "bar", 3, 0));
+ ASSERT_EQ(3, send(server, "bar", 3, 0));
}
- EXPECT_EQ(3, recv(client, buffer, 3, 0));
+ EXPECT_EQ(3, recv(client, buffer, sizeof(buffer), 0));
EXPECT_EQ(0, memcmp(buffer, "bar", 3));
+ // Send multiple buffers using socket_send_buffers().
+ std::string data[] = {"foo", "bar", "12345"};
+ cutils_socket_buffer_t socket_buffers[] = { {data[0].data(), data[0].length()},
+ {data[1].data(), data[1].length()},
+ {data[2].data(), data[2].length()} };
+ EXPECT_EQ(11, socket_send_buffers(client, socket_buffers, 3));
+ EXPECT_EQ(11, recv(server, buffer, sizeof(buffer), 0));
+ EXPECT_EQ(0, memcmp(buffer, "foobar12345", 11));
+
EXPECT_EQ(0, socket_close(server));
EXPECT_EQ(0, socket_close(client));
}
@@ -87,22 +90,43 @@
EXPECT_LE(1.0, difftime(time(nullptr), start_time));
}
+// Tests socket_get_local_port().
+TEST(SocketsTest, TestGetLocalPort) {
+ cutils_socket_t server;
+
+ // Check a bunch of ports so that we can ignore any conflicts in case
+ // of ports already being taken, but if a server is able to start up we
+ // should always be able to read its port.
+ for (int port : {10000, 12345, 15999, 20202, 25000}) {
+ for (int type : {SOCK_DGRAM, SOCK_STREAM}) {
+ server = socket_inaddr_any_server(port, SOCK_DGRAM);
+ if (server != INVALID_SOCKET) {
+ EXPECT_EQ(port, socket_get_local_port(server));
+ }
+ socket_close(server);
+ }
+ }
+
+ // Check expected failure for an invalid socket.
+ EXPECT_EQ(-1, socket_get_local_port(INVALID_SOCKET));
+}
+
// Tests socket_inaddr_any_server() and socket_network_client() for IPv4 UDP.
TEST(SocketsTest, TestIpv4UdpLoopback) {
- cutils_socket_t server = socket_inaddr_any_server(kTestPort, SOCK_DGRAM);
- cutils_socket_t client = socket_network_client("127.0.0.1", kTestPort,
- SOCK_DGRAM);
+ cutils_socket_t server = socket_inaddr_any_server(0, SOCK_DGRAM);
+ cutils_socket_t client = socket_network_client(
+ "127.0.0.1", socket_get_local_port(server), SOCK_DGRAM);
TestConnectedSockets(server, client, SOCK_DGRAM);
}
// Tests socket_inaddr_any_server() and socket_network_client() for IPv4 TCP.
TEST(SocketsTest, TestIpv4TcpLoopback) {
- cutils_socket_t server = socket_inaddr_any_server(kTestPort, SOCK_STREAM);
+ cutils_socket_t server = socket_inaddr_any_server(0, SOCK_STREAM);
ASSERT_NE(INVALID_SOCKET, server);
- cutils_socket_t client = socket_network_client("127.0.0.1", kTestPort,
- SOCK_STREAM);
+ cutils_socket_t client = socket_network_client(
+ "127.0.0.1", socket_get_local_port(server), SOCK_STREAM);
cutils_socket_t handler = accept(server, nullptr, nullptr);
EXPECT_EQ(0, socket_close(server));
@@ -111,20 +135,20 @@
// Tests socket_inaddr_any_server() and socket_network_client() for IPv6 UDP.
TEST(SocketsTest, TestIpv6UdpLoopback) {
- cutils_socket_t server = socket_inaddr_any_server(kTestPort, SOCK_DGRAM);
- cutils_socket_t client = socket_network_client("::1", kTestPort,
- SOCK_DGRAM);
+ cutils_socket_t server = socket_inaddr_any_server(0, SOCK_DGRAM);
+ cutils_socket_t client = socket_network_client(
+ "::1", socket_get_local_port(server), SOCK_DGRAM);
TestConnectedSockets(server, client, SOCK_DGRAM);
}
// Tests socket_inaddr_any_server() and socket_network_client() for IPv6 TCP.
TEST(SocketsTest, TestIpv6TcpLoopback) {
- cutils_socket_t server = socket_inaddr_any_server(kTestPort, SOCK_STREAM);
+ cutils_socket_t server = socket_inaddr_any_server(0, SOCK_STREAM);
ASSERT_NE(INVALID_SOCKET, server);
- cutils_socket_t client = socket_network_client("::1", kTestPort,
- SOCK_STREAM);
+ cutils_socket_t client = socket_network_client(
+ "::1", socket_get_local_port(server), SOCK_STREAM);
cutils_socket_t handler = accept(server, nullptr, nullptr);
EXPECT_EQ(0, socket_close(server));
@@ -133,7 +157,7 @@
// Tests setting a receive timeout for UDP sockets.
TEST(SocketsTest, TestUdpReceiveTimeout) {
- cutils_socket_t sock = socket_inaddr_any_server(kTestPort, SOCK_DGRAM);
+ cutils_socket_t sock = socket_inaddr_any_server(0, SOCK_DGRAM);
ASSERT_NE(INVALID_SOCKET, sock);
TestReceiveTimeout(sock);
@@ -143,11 +167,11 @@
// Tests setting a receive timeout for TCP sockets.
TEST(SocketsTest, TestTcpReceiveTimeout) {
- cutils_socket_t server = socket_inaddr_any_server(kTestPort, SOCK_STREAM);
+ cutils_socket_t server = socket_inaddr_any_server(0, SOCK_STREAM);
ASSERT_NE(INVALID_SOCKET, server);
- cutils_socket_t client = socket_network_client("localhost", kTestPort,
- SOCK_STREAM);
+ cutils_socket_t client = socket_network_client(
+ "localhost", socket_get_local_port(server), SOCK_STREAM);
cutils_socket_t handler = accept(server, nullptr, nullptr);
EXPECT_EQ(0, socket_close(server));
@@ -156,3 +180,8 @@
EXPECT_EQ(0, socket_close(client));
EXPECT_EQ(0, socket_close(handler));
}
+
+// Tests socket_send_buffers() failure.
+TEST(SocketsTest, TestSocketSendBuffersFailure) {
+ EXPECT_EQ(-1, socket_send_buffers(INVALID_SOCKET, nullptr, 0));
+}
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 6e6b0b9..209ff1c 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -42,6 +42,8 @@
"libGLESv1_CM.so:"
"libGLESv2.so:"
"libGLESv3.so:"
+ "libicui18n.so:"
+ "libicuuc.so:"
"libjnigraphics.so:"
"liblog.so:"
"libmediandk.so:"
diff --git a/logd/logd.rc b/logd/logd.rc
index 10f3553..31ed4df 100644
--- a/logd/logd.rc
+++ b/logd/logd.rc
@@ -1,5 +1,4 @@
service logd /system/bin/logd
- class core
socket logd stream 0666 logd logd
socket logdr seqpacket 0666 logd logd
socket logdw dgram 0222 logd logd
diff --git a/rootdir/init.rc b/rootdir/init.rc
index f617d72..1f63fcf 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -297,8 +297,11 @@
# We restorecon /data in case the userdata partition has been reset.
restorecon /data
- # Make sure we have the device encryption key
- start logd
+ # start debuggerd to make debugging early-boot crashes easier.
+ start debuggerd
+ start debuggerd64
+
+ # Make sure we have the device encryption key.
start vold
installkey /data