summaryrefslogtreecommitdiff
path: root/emulator/Conn
diff options
context:
space:
mode:
authorYu Shan <shanyu@google.com>2022-01-19 14:45:48 -0800
committerYu Shan <shanyu@google.com>2022-01-21 17:37:40 -0800
commit7292d87621e2bf64a0ccf8e322e0c01d6a9763a9 (patch)
tree3f2438925112f3338cfda6e51b6a15977e474572 /emulator/Conn
parenta145334e60033ae04661b65f5d1946c7bcb83b20 (diff)
downloadcar-7292d87621e2bf64a0ccf8e322e0c01d6a9763a9.tar.gz
Move Connection libs outside vhal_v2_0.
The connection libs are going to be used by AIDL VHAL as well so move them to a common place. Bug: 215419573 Test: Presubmit Change-Id: Ibff7030089fa4fd891bfeaee5604d9dba3ae07df
Diffstat (limited to 'emulator/Conn')
-rw-r--r--emulator/Conn/CommConn/Android.bp33
-rw-r--r--emulator/Conn/CommConn/CommConn.cpp79
-rw-r--r--emulator/Conn/CommConn/include/CommConn.h116
-rw-r--r--emulator/Conn/PipeComm/Android.bp35
-rw-r--r--emulator/Conn/PipeComm/PipeComm.cpp105
-rw-r--r--emulator/Conn/PipeComm/include/PipeComm.h64
-rw-r--r--emulator/Conn/PipeComm/include/qemu_pipe.h65
-rw-r--r--emulator/Conn/PipeComm/qemu_pipe.cpp104
-rw-r--r--emulator/Conn/SocketComm/Android.bp34
-rw-r--r--emulator/Conn/SocketComm/SocketComm.cpp225
-rw-r--r--emulator/Conn/SocketComm/include/SocketComm.h124
11 files changed, 984 insertions, 0 deletions
diff --git a/emulator/Conn/CommConn/Android.bp b/emulator/Conn/CommConn/Android.bp
new file mode 100644
index 0000000..9de4e08
--- /dev/null
+++ b/emulator/Conn/CommConn/Android.bp
@@ -0,0 +1,33 @@
+// Copyright (C) 2022 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.
+
+package {
+ default_applicable_licenses: ["device_generic_car_license"],
+}
+
+cc_library {
+ name: "EmulatorCommConn",
+ vendor: true,
+ srcs: [
+ "*.cpp",
+ ],
+ shared_libs: [
+ "liblog",
+ "libprotobuf-cpp-lite",
+ ],
+ export_include_dirs: ["include"],
+ static_libs: [
+ "android.hardware.automotive.vehicle@2.0-libproto-native",
+ ],
+}
diff --git a/emulator/Conn/CommConn/CommConn.cpp b/emulator/Conn/CommConn/CommConn.cpp
new file mode 100644
index 0000000..800a539
--- /dev/null
+++ b/emulator/Conn/CommConn/CommConn.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "CommConn"
+
+#include <thread>
+
+#include <log/log.h>
+
+#include "CommConn.h"
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+
+namespace impl {
+
+void CommConn::start() {
+ mReadThread = std::make_unique<std::thread>(std::bind(&CommConn::readThread, this));
+}
+
+void CommConn::stop() {
+ if (mReadThread->joinable()) {
+ mReadThread->join();
+ }
+}
+
+void CommConn::sendMessage(vhal_proto::EmulatorMessage const& msg) {
+ int numBytes = msg.ByteSize();
+ std::vector<uint8_t> buffer(static_cast<size_t>(numBytes));
+ if (!msg.SerializeToArray(buffer.data(), numBytes)) {
+ ALOGE("%s: SerializeToString failed!", __func__);
+ return;
+ }
+
+ write(buffer);
+}
+
+void CommConn::readThread() {
+ std::vector<uint8_t> buffer;
+ while (isOpen()) {
+ buffer = read();
+ if (buffer.size() == 0) {
+ ALOGI("%s: Read returned empty message, exiting read loop.", __func__);
+ break;
+ }
+
+ vhal_proto::EmulatorMessage rxMsg;
+ if (rxMsg.ParseFromArray(buffer.data(), static_cast<int32_t>(buffer.size()))) {
+ vhal_proto::EmulatorMessage respMsg;
+ mMessageProcessor->processMessage(rxMsg, respMsg);
+
+ sendMessage(respMsg);
+ }
+ }
+}
+
+} // namespace impl
+
+} // namespace V2_0
+} // namespace vehicle
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/emulator/Conn/CommConn/include/CommConn.h b/emulator/Conn/CommConn/include/CommConn.h
new file mode 100644
index 0000000..cbc8281
--- /dev/null
+++ b/emulator/Conn/CommConn/include/CommConn.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_EMULATORCOMMCONN_COMMCONN_H
+#define ANDROID_EMULATORCOMMCONN_COMMCONN_H
+
+#include <string>
+#include <thread>
+#include <vector>
+
+#include "VehicleHalProto.pb.h"
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+
+namespace impl {
+
+/**
+ * MessageProcess is an interface implemented by VehicleEmulator to process messages received
+ * over a CommConn.
+ */
+class MessageProcessor {
+ public:
+ virtual ~MessageProcessor() = default;
+
+ /**
+ * Process a single message received over a CommConn. Populate the given respMsg with the reply
+ * message we should send.
+ */
+ virtual void processMessage(vhal_proto::EmulatorMessage const& rxMsg,
+ vhal_proto::EmulatorMessage& respMsg) = 0;
+};
+
+/**
+ * This is the interface that both PipeComm and SocketComm use to represent a connection. The
+ * connection will listen for commands on a separate 'read' thread.
+ */
+class CommConn {
+ public:
+ CommConn(MessageProcessor* messageProcessor) : mMessageProcessor(messageProcessor) {}
+
+ virtual ~CommConn() {}
+
+ /**
+ * Start the read thread reading messages from this connection.
+ */
+ virtual void start();
+
+ /**
+ * Closes a connection if it is open.
+ */
+ virtual void stop();
+
+ /**
+ * Returns true if the connection is open and available to send/receive.
+ */
+ virtual bool isOpen() = 0;
+
+ /**
+ * Blocking call to read data from the connection.
+ *
+ * @return std::vector<uint8_t> Serialized protobuf data received from emulator. This will be
+ * an empty vector if the connection was closed or some other error occurred.
+ */
+ virtual std::vector<uint8_t> read() = 0;
+
+ /**
+ * Transmits a string of data to the emulator.
+ *
+ * @param data Serialized protobuf data to transmit.
+ *
+ * @return int Number of bytes transmitted, or -1 if failed.
+ */
+ virtual int write(const std::vector<uint8_t>& data) = 0;
+
+ /**
+ * Serialized and send the given message to the other side.
+ */
+ void sendMessage(vhal_proto::EmulatorMessage const& msg);
+
+ protected:
+ std::unique_ptr<std::thread> mReadThread;
+ MessageProcessor* mMessageProcessor;
+
+ /**
+ * A thread that reads messages in a loop, and responds. You can stop this thread by calling
+ * stop().
+ */
+ void readThread();
+};
+
+} // namespace impl
+
+} // namespace V2_0
+} // namespace vehicle
+} // namespace automotive
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_EMULATORCOMMCONN_COMMCONN_H
diff --git a/emulator/Conn/PipeComm/Android.bp b/emulator/Conn/PipeComm/Android.bp
new file mode 100644
index 0000000..6f8e3b4
--- /dev/null
+++ b/emulator/Conn/PipeComm/Android.bp
@@ -0,0 +1,35 @@
+// Copyright (C) 2022 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.
+
+package {
+ default_applicable_licenses: ["device_generic_car_license"],
+}
+
+cc_library {
+ name: "EmulatorPipeComm",
+ vendor: true,
+ srcs: [
+ "*.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ "libprotobuf-cpp-lite",
+ ],
+ export_include_dirs: ["include"],
+ static_libs: [
+ "android.hardware.automotive.vehicle@2.0-libproto-native",
+ "EmulatorCommConn",
+ ],
+}
diff --git a/emulator/Conn/PipeComm/PipeComm.cpp b/emulator/Conn/PipeComm/PipeComm.cpp
new file mode 100644
index 0000000..c54bf9f
--- /dev/null
+++ b/emulator/Conn/PipeComm/PipeComm.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "PipeComm"
+
+#include <log/log.h>
+
+#include "qemu_pipe.h"
+
+#include "PipeComm.h"
+
+#define CAR_SERVICE_NAME "pipe:qemud:car"
+
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+
+namespace impl {
+
+PipeComm::PipeComm(MessageProcessor* messageProcessor) : CommConn(messageProcessor), mPipeFd(-1) {}
+
+void PipeComm::start() {
+ int fd = qemu_pipe_open(CAR_SERVICE_NAME);
+
+ if (fd < 0) {
+ ALOGE("%s: Could not open connection to service: %s %d", __FUNCTION__, strerror(errno), fd);
+ return;
+ }
+
+ ALOGI("%s: Starting pipe connection, fd=%d", __FUNCTION__, fd);
+ mPipeFd = fd;
+
+ CommConn::start();
+}
+
+void PipeComm::stop() {
+ if (mPipeFd > 0) {
+ ::close(mPipeFd);
+ mPipeFd = -1;
+ }
+ CommConn::stop();
+}
+
+std::vector<uint8_t> PipeComm::read() {
+ static constexpr int MAX_RX_MSG_SZ = 2048;
+ std::vector<uint8_t> msg = std::vector<uint8_t>(MAX_RX_MSG_SZ);
+ int numBytes;
+
+ numBytes = qemu_pipe_frame_recv(mPipeFd, msg.data(), msg.size());
+
+ if (numBytes == MAX_RX_MSG_SZ) {
+ ALOGE("%s: Received max size = %d", __FUNCTION__, MAX_RX_MSG_SZ);
+ } else if (numBytes > 0) {
+ msg.resize(numBytes);
+ return msg;
+ } else {
+ ALOGD("%s: Connection terminated on pipe %d, numBytes=%d", __FUNCTION__, mPipeFd, numBytes);
+ mPipeFd = -1;
+ }
+
+ return std::vector<uint8_t>();
+}
+
+int PipeComm::write(const std::vector<uint8_t>& data) {
+ int retVal = 0;
+
+ if (mPipeFd != -1) {
+ retVal = qemu_pipe_frame_send(mPipeFd, data.data(), data.size());
+ }
+
+ if (retVal < 0) {
+ retVal = -errno;
+ ALOGE("%s: send_cmd: (fd=%d): ERROR: %s", __FUNCTION__, mPipeFd, strerror(errno));
+ }
+
+ return retVal;
+}
+
+
+} // impl
+
+} // namespace V2_0
+} // namespace vehicle
+} // namespace automotive
+} // namespace hardware
+} // namespace android
+
+
+
diff --git a/emulator/Conn/PipeComm/include/PipeComm.h b/emulator/Conn/PipeComm/include/PipeComm.h
new file mode 100644
index 0000000..24dd686
--- /dev/null
+++ b/emulator/Conn/PipeComm/include/PipeComm.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_EMULATORPIPECOMM_PIPECOMM_H
+#define ANDROID_EMULATORPIPECOMM_PIPECOMM_H
+
+#include <mutex>
+#include <vector>
+
+#include "CommConn.h"
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+
+namespace impl {
+
+/**
+ * PipeComm opens a qemu pipe to connect to the emulator, allowing the emulator UI to access the
+ * Vehicle HAL and simulate changing properties.
+ *
+ * Since the pipe is a client, it directly implements CommConn, and only one PipeComm can be open
+ * at a time.
+ */
+class PipeComm : public CommConn {
+ public:
+ PipeComm(MessageProcessor* messageProcessor);
+
+ void start() override;
+ void stop() override;
+
+ std::vector<uint8_t> read() override;
+ int write(const std::vector<uint8_t>& data) override;
+
+ inline bool isOpen() override { return mPipeFd > 0; }
+
+ private:
+ int mPipeFd;
+};
+
+} // impl
+
+} // namespace V2_0
+} // namespace vehicle
+} // namespace automotive
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_EMULATORPIPECOMM_PIPECOMM_H
diff --git a/emulator/Conn/PipeComm/include/qemu_pipe.h b/emulator/Conn/PipeComm/include/qemu_pipe.h
new file mode 100644
index 0000000..bd3e71d
--- /dev/null
+++ b/emulator/Conn/PipeComm/include/qemu_pipe.h
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_EMULATORPIPECOMM_QEMU_PIPE_H
+#define ANDROID_EMULATORPIPECOMM_QEMU_PIPE_H
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+// Try to open a new Qemu fast-pipe. This function returns a file descriptor
+// that can be used to communicate with a named service managed by the
+// emulator.
+//
+// This file descriptor can be used as a standard pipe/socket descriptor.
+//
+// 'pipeName' is the name of the emulator service you want to connect to,
+// and should begin with 'pipe:' (e.g. 'pipe:camera' or 'pipe:opengles').
+// For backward compatibility, the 'pipe:' prefix can be omitted, and in
+// that case, qemu_pipe_open will add it for you.
+
+// On success, return a valid file descriptor, or -1/errno on failure. E.g.:
+//
+// EINVAL -> unknown/unsupported pipeName
+// ENOSYS -> fast pipes not available in this system.
+//
+// ENOSYS should never happen, except if you're trying to run within a
+// misconfigured emulator.
+//
+// You should be able to open several pipes to the same pipe service,
+// except for a few special cases (e.g. GSM modem), where EBUSY will be
+// returned if more than one client tries to connect to it.
+int qemu_pipe_open(const char* pipeName);
+
+// Send a framed message |buff| of |len| bytes through the |fd| descriptor.
+// This really adds a 4-hexchar prefix describing the payload size.
+// Returns 0 on success, and -1 on error.
+int qemu_pipe_frame_send(int fd, const void* buff, size_t len);
+
+// Read a frame message from |fd|, and store it into |buff| of |len| bytes.
+// If the framed message is larger than |len|, then this returns -1 and the
+// content is lost. Otherwise, this returns the size of the message. NOTE:
+// empty messages are possible in a framed wire protocol and do not mean
+// end-of-stream.
+int qemu_pipe_frame_recv(int fd, void* buff, size_t len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // ANDROID_EMULATORPIPECOMM_QEMU_PIPE_H
diff --git a/emulator/Conn/PipeComm/qemu_pipe.cpp b/emulator/Conn/PipeComm/qemu_pipe.cpp
new file mode 100644
index 0000000..1f541b5
--- /dev/null
+++ b/emulator/Conn/PipeComm/qemu_pipe.cpp
@@ -0,0 +1,104 @@
+/*
+ * 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 <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+
+#include "qemu_pipe.h"
+
+using android::base::ReadFully;
+using android::base::WriteFully;
+
+// Define QEMU_PIPE_DEBUG if you want to print error messages when an error
+// occurs during pipe operations. The macro should simply take a printf-style
+// formatting string followed by optional arguments.
+#ifndef QEMU_PIPE_DEBUG
+#define QEMU_PIPE_DEBUG(...) (void)0
+#endif
+
+int qemu_pipe_open(const char* pipeName) {
+ if (!pipeName) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ int fd = TEMP_FAILURE_RETRY(open("/dev/qemu_pipe", O_RDWR));
+ if (fd < 0) {
+ QEMU_PIPE_DEBUG("%s: Could not open /dev/qemu_pipe: %s", __FUNCTION__, strerror(errno));
+ return -1;
+ }
+
+ // Write the pipe name, *including* the trailing zero which is necessary.
+ size_t pipeNameLen = strlen(pipeName);
+ if (WriteFully(fd, pipeName, pipeNameLen + 1U)) {
+ return fd;
+ }
+
+ // now, add 'pipe:' prefix and try again
+ // Note: host side will wait for the trailing '\0' to start
+ // service lookup.
+ const char pipe_prefix[] = "pipe:";
+ if (WriteFully(fd, pipe_prefix, strlen(pipe_prefix)) &&
+ WriteFully(fd, pipeName, pipeNameLen + 1U)) {
+ return fd;
+ }
+ QEMU_PIPE_DEBUG("%s: Could not write to %s pipe service: %s", __FUNCTION__, pipeName,
+ strerror(errno));
+ close(fd);
+ return -1;
+}
+
+int qemu_pipe_frame_send(int fd, const void* buff, size_t len) {
+ char header[5];
+ snprintf(header, sizeof(header), "%04zx", len);
+ if (!WriteFully(fd, header, 4)) {
+ QEMU_PIPE_DEBUG("Can't write qemud frame header: %s", strerror(errno));
+ return -1;
+ }
+ if (!WriteFully(fd, buff, len)) {
+ QEMU_PIPE_DEBUG("Can't write qemud frame payload: %s", strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+int qemu_pipe_frame_recv(int fd, void* buff, size_t len) {
+ char header[5];
+ if (!ReadFully(fd, header, 4)) {
+ QEMU_PIPE_DEBUG("Can't read qemud frame header: %s", strerror(errno));
+ return -1;
+ }
+ header[4] = '\0';
+ size_t size;
+ if (sscanf(header, "%04zx", &size) != 1) {
+ QEMU_PIPE_DEBUG("Malformed qemud frame header: [%.*s]", 4, header);
+ return -1;
+ }
+ if (size > len) {
+ QEMU_PIPE_DEBUG("Oversized qemud frame (% bytes, expected <= %)", size, len);
+ return -1;
+ }
+ if (!ReadFully(fd, buff, size)) {
+ QEMU_PIPE_DEBUG("Could not read qemud frame payload: %s", strerror(errno));
+ return -1;
+ }
+ return size;
+}
diff --git a/emulator/Conn/SocketComm/Android.bp b/emulator/Conn/SocketComm/Android.bp
new file mode 100644
index 0000000..0a186d1
--- /dev/null
+++ b/emulator/Conn/SocketComm/Android.bp
@@ -0,0 +1,34 @@
+// Copyright (C) 2022 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.
+
+package {
+ default_applicable_licenses: ["device_generic_car_license"],
+}
+
+cc_library {
+ name: "EmulatorSocketComm",
+ vendor: true,
+ srcs: [
+ "*.cpp",
+ ],
+ shared_libs: [
+ "liblog",
+ "libprotobuf-cpp-lite",
+ ],
+ export_include_dirs: ["include"],
+ static_libs: [
+ "android.hardware.automotive.vehicle@2.0-libproto-native",
+ "EmulatorCommConn",
+ ],
+}
diff --git a/emulator/Conn/SocketComm/SocketComm.cpp b/emulator/Conn/SocketComm/SocketComm.cpp
new file mode 100644
index 0000000..1e9a9bd
--- /dev/null
+++ b/emulator/Conn/SocketComm/SocketComm.cpp
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "SocketComm"
+
+#include <android/log.h>
+#include <arpa/inet.h>
+#include <log/log.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+
+#include "SocketComm.h"
+
+// Socket to use when communicating with Host PC
+static constexpr int DEBUG_SOCKET = 33452;
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+
+namespace impl {
+
+SocketComm::SocketComm(MessageProcessor* messageProcessor)
+ : mListenFd(-1), mMessageProcessor(messageProcessor) {}
+
+SocketComm::~SocketComm() {
+}
+
+void SocketComm::start() {
+ if (!listen()) {
+ return;
+ }
+
+ mListenThread = std::make_unique<std::thread>(std::bind(&SocketComm::listenThread, this));
+}
+
+void SocketComm::stop() {
+ if (mListenFd > 0) {
+ ::close(mListenFd);
+ if (mListenThread->joinable()) {
+ mListenThread->join();
+ }
+ mListenFd = -1;
+ }
+}
+
+void SocketComm::sendMessage(vhal_proto::EmulatorMessage const& msg) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ for (std::unique_ptr<SocketConn> const& conn : mOpenConnections) {
+ conn->sendMessage(msg);
+ }
+}
+
+bool SocketComm::listen() {
+ int retVal;
+ struct sockaddr_in servAddr;
+
+ mListenFd = socket(AF_INET, SOCK_STREAM, 0);
+ if (mListenFd < 0) {
+ ALOGE("%s: socket() failed, mSockFd=%d, errno=%d", __FUNCTION__, mListenFd, errno);
+ mListenFd = -1;
+ return false;
+ }
+
+ memset(&servAddr, 0, sizeof(servAddr));
+ servAddr.sin_family = AF_INET;
+ servAddr.sin_addr.s_addr = INADDR_ANY;
+ servAddr.sin_port = htons(DEBUG_SOCKET);
+
+ retVal = bind(mListenFd, reinterpret_cast<struct sockaddr*>(&servAddr), sizeof(servAddr));
+ if(retVal < 0) {
+ ALOGE("%s: Error on binding: retVal=%d, errno=%d", __FUNCTION__, retVal, errno);
+ close(mListenFd);
+ mListenFd = -1;
+ return false;
+ }
+
+ ALOGI("%s: Listening for connections on port %d", __FUNCTION__, DEBUG_SOCKET);
+ if (::listen(mListenFd, 1) == -1) {
+ ALOGE("%s: Error on listening: errno: %d: %s", __FUNCTION__, errno, strerror(errno));
+ return false;
+ }
+ return true;
+}
+
+SocketConn* SocketComm::accept() {
+ sockaddr_in cliAddr;
+ socklen_t cliLen = sizeof(cliAddr);
+ int sfd = ::accept(mListenFd, reinterpret_cast<struct sockaddr*>(&cliAddr), &cliLen);
+
+ if (sfd > 0) {
+ char addr[INET_ADDRSTRLEN];
+ inet_ntop(AF_INET, &cliAddr.sin_addr, addr, INET_ADDRSTRLEN);
+
+ ALOGD("%s: Incoming connection received from %s:%d", __FUNCTION__, addr, cliAddr.sin_port);
+ return new SocketConn(mMessageProcessor, sfd);
+ }
+
+ return nullptr;
+}
+
+void SocketComm::listenThread() {
+ while (true) {
+ SocketConn* conn = accept();
+ if (conn == nullptr) {
+ return;
+ }
+
+ conn->start();
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mOpenConnections.push_back(std::unique_ptr<SocketConn>(conn));
+ }
+ }
+}
+
+/**
+ * Called occasionally to clean up connections that have been closed.
+ */
+void SocketComm::removeClosedConnections() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ std::remove_if(mOpenConnections.begin(), mOpenConnections.end(),
+ [](std::unique_ptr<SocketConn> const& c) { return !c->isOpen(); });
+}
+
+SocketConn::SocketConn(MessageProcessor* messageProcessor, int sfd)
+ : CommConn(messageProcessor), mSockFd(sfd) {}
+
+/**
+ * Reads, in a loop, exactly numBytes from the given fd. If the connection is closed, returns
+ * an empty buffer, otherwise will return exactly the given number of bytes.
+ */
+std::vector<uint8_t> readExactly(int fd, int numBytes) {
+ std::vector<uint8_t> buffer(numBytes);
+ int totalRead = 0;
+ int offset = 0;
+ while (totalRead < numBytes) {
+ int numRead = ::read(fd, &buffer.data()[offset], numBytes - offset);
+ if (numRead == 0) {
+ buffer.resize(0);
+ return buffer;
+ }
+
+ totalRead += numRead;
+ }
+ return buffer;
+}
+
+/**
+ * Reads an int, guaranteed to be non-zero, from the given fd. If the connection is closed, returns
+ * -1.
+ */
+int32_t readInt(int fd) {
+ std::vector<uint8_t> buffer = readExactly(fd, sizeof(int32_t));
+ if (buffer.size() == 0) {
+ return -1;
+ }
+
+ int32_t value = *reinterpret_cast<int32_t*>(buffer.data());
+ return ntohl(value);
+}
+
+std::vector<uint8_t> SocketConn::read() {
+ int32_t msgSize = readInt(mSockFd);
+ if (msgSize <= 0) {
+ ALOGD("%s: Connection terminated on socket %d", __FUNCTION__, mSockFd);
+ return std::vector<uint8_t>();
+ }
+
+ return readExactly(mSockFd, msgSize);
+}
+
+void SocketConn::stop() {
+ if (mSockFd > 0) {
+ close(mSockFd);
+ mSockFd = -1;
+ }
+}
+
+int SocketConn::write(const std::vector<uint8_t>& data) {
+ static constexpr int MSG_HEADER_LEN = 4;
+ int retVal = 0;
+ union {
+ uint32_t msgLen;
+ uint8_t msgLenBytes[MSG_HEADER_LEN];
+ };
+
+ // Prepare header for the message
+ msgLen = static_cast<uint32_t>(data.size());
+ msgLen = htonl(msgLen);
+
+ if (mSockFd > 0) {
+ retVal = ::write(mSockFd, msgLenBytes, MSG_HEADER_LEN);
+
+ if (retVal == MSG_HEADER_LEN) {
+ retVal = ::write(mSockFd, data.data(), data.size());
+ }
+ }
+
+ return retVal;
+}
+
+} // impl
+
+} // namespace V2_0
+} // namespace vehicle
+} // namespace automotive
+} // namespace hardware
+} // namespace android
+
diff --git a/emulator/Conn/SocketComm/include/SocketComm.h b/emulator/Conn/SocketComm/include/SocketComm.h
new file mode 100644
index 0000000..0664747
--- /dev/null
+++ b/emulator/Conn/SocketComm/include/SocketComm.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_EMULATORSOCKETCOMM_SOCKETCOMM_H
+#define ANDROID_EMULATORSOCKETCOMM_SOCKETCOMM_H
+
+#include <mutex>
+#include <thread>
+#include <vector>
+#include "CommConn.h"
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+
+namespace impl {
+
+class SocketConn;
+
+/**
+ * SocketComm opens a socket, and listens for connections from clients. Typically the client will be
+ * adb's TCP port-forwarding to enable a host PC to connect to the VehicleHAL.
+ */
+class SocketComm {
+ public:
+ SocketComm(MessageProcessor* messageProcessor);
+ virtual ~SocketComm();
+
+ void start();
+ void stop();
+
+ /**
+ * Serialized and send the given message to all connected clients.
+ */
+ void sendMessage(vhal_proto::EmulatorMessage const& msg);
+
+ private:
+ int mListenFd;
+ std::unique_ptr<std::thread> mListenThread;
+ std::vector<std::unique_ptr<SocketConn>> mOpenConnections;
+ MessageProcessor* mMessageProcessor;
+ std::mutex mMutex;
+
+ /**
+ * Opens the socket and begins listening.
+ *
+ * @return bool Returns true on success.
+ */
+ bool listen();
+
+ /**
+ * Blocks and waits for a connection from a client, returns a new SocketConn with the connection
+ * or null, if the connection has been closed.
+ *
+ * @return int Returns fd or socket number if connection is successful.
+ * Otherwise, returns -1 if no connection is available.
+ */
+ SocketConn* accept();
+
+ void listenThread();
+
+ void removeClosedConnections();
+};
+
+/**
+ * SocketConn represents a single connection to a client.
+ */
+class SocketConn : public CommConn {
+ public:
+ SocketConn(MessageProcessor* messageProcessor, int sfd);
+ virtual ~SocketConn() = default;
+
+ /**
+ * Blocking call to read data from the connection.
+ *
+ * @return std::vector<uint8_t> Serialized protobuf data received from emulator. This will be
+ * an empty vector if the connection was closed or some other error occurred.
+ */
+ std::vector<uint8_t> read() override;
+
+ /**
+ * Closes a connection if it is open.
+ */
+ void stop() override;
+
+ /**
+ * Transmits a string of data to the emulator.
+ *
+ * @param data Serialized protobuf data to transmit.
+ *
+ * @return int Number of bytes transmitted, or -1 if failed.
+ */
+ int write(const std::vector<uint8_t>& data) override;
+
+ inline bool isOpen() override { return mSockFd > 0; }
+
+ private:
+ int mSockFd;
+};
+
+} // impl
+
+} // namespace V2_0
+} // namespace vehicle
+} // namespace automotive
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_EMULATORSOCKETCOMM_SOCKETCOMM_H