blob: 9d9161784a14757dd1c078b3af7171dad1f022a0 [file] [log] [blame]
#include "uds/client_channel.h"
#include <errno.h>
#include <log/log.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <pdx/client.h>
#include <pdx/service_endpoint.h>
#include <uds/ipc_helper.h>
namespace android {
namespace pdx {
namespace uds {
namespace {
struct TransactionState {
bool GetLocalFileHandle(int index, LocalHandle* handle) {
if (index < 0) {
handle->Reset(index);
} else if (static_cast<size_t>(index) < response.file_descriptors.size()) {
*handle = std::move(response.file_descriptors[index]);
} else {
return false;
}
return true;
}
bool GetLocalChannelHandle(int index, LocalChannelHandle* handle) {
if (index < 0) {
*handle = LocalChannelHandle{nullptr, index};
} else if (static_cast<size_t>(index) < response.channels.size()) {
auto& channel_info = response.channels[index];
*handle = ChannelManager::Get().CreateHandle(
std::move(channel_info.data_fd), std::move(channel_info.event_fd));
} else {
return false;
}
return true;
}
FileReference PushFileHandle(BorrowedHandle handle) {
if (!handle)
return handle.Get();
request.file_descriptors.push_back(std::move(handle));
return request.file_descriptors.size() - 1;
}
ChannelReference PushChannelHandle(BorrowedChannelHandle handle) {
if (!handle)
return handle.value();
if (auto* channel_data =
ChannelManager::Get().GetChannelData(handle.value())) {
ChannelInfo<BorrowedHandle> channel_info;
channel_info.data_fd.Reset(handle.value());
channel_info.event_fd = channel_data->event_receiver.event_fd();
request.channels.push_back(std::move(channel_info));
return request.channels.size() - 1;
} else {
return -1;
}
}
RequestHeader<BorrowedHandle> request;
ResponseHeader<LocalHandle> response;
};
Status<void> ReadAndDiscardData(const BorrowedHandle& socket_fd, size_t size) {
while (size > 0) {
// If there is more data to read in the message than the buffers provided
// by the caller, read and discard the extra data from the socket.
char buffer[1024];
size_t size_to_read = std::min(sizeof(buffer), size);
auto status = ReceiveData(socket_fd, buffer, size_to_read);
if (!status)
return status;
size -= size_to_read;
}
// We still want to return EIO error to the caller in case we had unexpected
// data in the socket stream.
return ErrorStatus(EIO);
}
Status<void> SendRequest(const BorrowedHandle& socket_fd,
TransactionState* transaction_state, int opcode,
const iovec* send_vector, size_t send_count,
size_t max_recv_len) {
size_t send_len = CountVectorSize(send_vector, send_count);
InitRequest(&transaction_state->request, opcode, send_len, max_recv_len,
false);
auto status = SendData(socket_fd, transaction_state->request);
if (status && send_len > 0)
status = SendDataVector(socket_fd, send_vector, send_count);
return status;
}
Status<void> ReceiveResponse(const BorrowedHandle& socket_fd,
TransactionState* transaction_state,
const iovec* receive_vector, size_t receive_count,
size_t max_recv_len) {
auto status = ReceiveData(socket_fd, &transaction_state->response);
if (!status)
return status;
if (transaction_state->response.recv_len > 0) {
std::vector<iovec> read_buffers;
size_t size_remaining = 0;
if (transaction_state->response.recv_len != max_recv_len) {
// If the receive buffer not exactly the size of data available, recreate
// the vector list to consume the data exactly since ReceiveDataVector()
// validates that the number of bytes received equals the number of bytes
// requested.
size_remaining = transaction_state->response.recv_len;
for (size_t i = 0; i < receive_count && size_remaining > 0; i++) {
read_buffers.push_back(receive_vector[i]);
iovec& last_vec = read_buffers.back();
if (last_vec.iov_len > size_remaining)
last_vec.iov_len = size_remaining;
size_remaining -= last_vec.iov_len;
}
receive_vector = read_buffers.data();
receive_count = read_buffers.size();
}
status = ReceiveDataVector(socket_fd, receive_vector, receive_count);
if (status && size_remaining > 0)
status = ReadAndDiscardData(socket_fd, size_remaining);
}
return status;
}
} // anonymous namespace
ClientChannel::ClientChannel(LocalChannelHandle channel_handle)
: channel_handle_{std::move(channel_handle)} {
channel_data_ = ChannelManager::Get().GetChannelData(channel_handle_.value());
}
std::unique_ptr<pdx::ClientChannel> ClientChannel::Create(
LocalChannelHandle channel_handle) {
return std::unique_ptr<pdx::ClientChannel>{
new ClientChannel{std::move(channel_handle)}};
}
ClientChannel::~ClientChannel() {
if (channel_handle_)
shutdown(channel_handle_.value(), SHUT_WR);
}
void* ClientChannel::AllocateTransactionState() { return new TransactionState; }
void ClientChannel::FreeTransactionState(void* state) {
delete static_cast<TransactionState*>(state);
}
Status<void> ClientChannel::SendImpulse(int opcode, const void* buffer,
size_t length) {
std::unique_lock<std::mutex> lock(socket_mutex_);
Status<void> status;
android::pdx::uds::RequestHeader<BorrowedHandle> request;
if (length > request.impulse_payload.size() ||
(buffer == nullptr && length != 0)) {
status.SetError(EINVAL);
return status;
}
InitRequest(&request, opcode, length, 0, true);
memcpy(request.impulse_payload.data(), buffer, length);
return SendData(BorrowedHandle{channel_handle_.value()}, request);
}
Status<int> ClientChannel::SendAndReceive(void* transaction_state, int opcode,
const iovec* send_vector,
size_t send_count,
const iovec* receive_vector,
size_t receive_count) {
std::unique_lock<std::mutex> lock(socket_mutex_);
Status<int> result;
if ((send_vector == nullptr && send_count != 0) ||
(receive_vector == nullptr && receive_count != 0)) {
result.SetError(EINVAL);
return result;
}
auto* state = static_cast<TransactionState*>(transaction_state);
size_t max_recv_len = CountVectorSize(receive_vector, receive_count);
auto status = SendRequest(BorrowedHandle{channel_handle_.value()}, state,
opcode, send_vector, send_count, max_recv_len);
if (status) {
status = ReceiveResponse(BorrowedHandle{channel_handle_.value()}, state,
receive_vector, receive_count, max_recv_len);
}
if (!result.PropagateError(status)) {
const int return_code = state->response.ret_code;
if (return_code >= 0)
result.SetValue(return_code);
else
result.SetError(-return_code);
}
return result;
}
Status<int> ClientChannel::SendWithInt(void* transaction_state, int opcode,
const iovec* send_vector,
size_t send_count,
const iovec* receive_vector,
size_t receive_count) {
return SendAndReceive(transaction_state, opcode, send_vector, send_count,
receive_vector, receive_count);
}
Status<LocalHandle> ClientChannel::SendWithFileHandle(
void* transaction_state, int opcode, const iovec* send_vector,
size_t send_count, const iovec* receive_vector, size_t receive_count) {
Status<int> int_status =
SendAndReceive(transaction_state, opcode, send_vector, send_count,
receive_vector, receive_count);
Status<LocalHandle> status;
if (status.PropagateError(int_status))
return status;
auto* state = static_cast<TransactionState*>(transaction_state);
LocalHandle handle;
if (state->GetLocalFileHandle(int_status.get(), &handle)) {
status.SetValue(std::move(handle));
} else {
status.SetError(EINVAL);
}
return status;
}
Status<LocalChannelHandle> ClientChannel::SendWithChannelHandle(
void* transaction_state, int opcode, const iovec* send_vector,
size_t send_count, const iovec* receive_vector, size_t receive_count) {
Status<int> int_status =
SendAndReceive(transaction_state, opcode, send_vector, send_count,
receive_vector, receive_count);
Status<LocalChannelHandle> status;
if (status.PropagateError(int_status))
return status;
auto* state = static_cast<TransactionState*>(transaction_state);
LocalChannelHandle handle;
if (state->GetLocalChannelHandle(int_status.get(), &handle)) {
status.SetValue(std::move(handle));
} else {
status.SetError(EINVAL);
}
return status;
}
FileReference ClientChannel::PushFileHandle(void* transaction_state,
const LocalHandle& handle) {
auto* state = static_cast<TransactionState*>(transaction_state);
return state->PushFileHandle(handle.Borrow());
}
FileReference ClientChannel::PushFileHandle(void* transaction_state,
const BorrowedHandle& handle) {
auto* state = static_cast<TransactionState*>(transaction_state);
return state->PushFileHandle(handle.Duplicate());
}
ChannelReference ClientChannel::PushChannelHandle(
void* transaction_state, const LocalChannelHandle& handle) {
auto* state = static_cast<TransactionState*>(transaction_state);
return state->PushChannelHandle(handle.Borrow());
}
ChannelReference ClientChannel::PushChannelHandle(
void* transaction_state, const BorrowedChannelHandle& handle) {
auto* state = static_cast<TransactionState*>(transaction_state);
return state->PushChannelHandle(handle.Duplicate());
}
bool ClientChannel::GetFileHandle(void* transaction_state, FileReference ref,
LocalHandle* handle) const {
auto* state = static_cast<TransactionState*>(transaction_state);
return state->GetLocalFileHandle(ref, handle);
}
bool ClientChannel::GetChannelHandle(void* transaction_state,
ChannelReference ref,
LocalChannelHandle* handle) const {
auto* state = static_cast<TransactionState*>(transaction_state);
return state->GetLocalChannelHandle(ref, handle);
}
} // namespace uds
} // namespace pdx
} // namespace android