322 lines
11 KiB
C++
322 lines
11 KiB
C++
#include "uds/channel_parcelable.h"
|
|
#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.pollin_event_fd),
|
|
std::move(channel_info.pollhup_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_data->data_fd(), channel_data->pollin_event_fd(),
|
|
channel_data->pollhup_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);
|
|
if (send_len == 0) {
|
|
send_vector = nullptr;
|
|
send_count = 0;
|
|
}
|
|
return SendData(socket_fd, transaction_state->request, send_vector,
|
|
send_count);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
std::unique_ptr<pdx::ChannelParcelable> ClientChannel::TakeChannelParcelable()
|
|
{
|
|
if (!channel_handle_)
|
|
return nullptr;
|
|
|
|
if (auto* channel_data =
|
|
ChannelManager::Get().GetChannelData(channel_handle_.value())) {
|
|
auto fds = channel_data->TakeFds();
|
|
auto parcelable = std::make_unique<ChannelParcelable>(
|
|
std::move(std::get<0>(fds)), std::move(std::get<1>(fds)),
|
|
std::move(std::get<2>(fds)));
|
|
|
|
// Here we need to explicitly close the channel handle so that the channel
|
|
// won't get shutdown in the destructor, while the FDs in ChannelParcelable
|
|
// can keep the channel alive so that new client can be created from it
|
|
// later.
|
|
channel_handle_.Close();
|
|
return parcelable;
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
} // namespace uds
|
|
} // namespace pdx
|
|
} // namespace android
|