#include "pdx/client.h" #include #include namespace android { namespace pdx { void Client::EnableAutoReconnect(int64_t reconnect_timeout_ms) { if (channel_factory_) { reconnect_timeout_ms_ = reconnect_timeout_ms; auto_reconnect_enabled_ = true; } } void Client::DisableAutoReconnect() { auto_reconnect_enabled_ = false; } bool Client::IsConnected() const { return channel_.get() != nullptr; } Status Client::CheckReconnect() { Status ret; bool was_disconnected = !IsConnected(); if (auto_reconnect_enabled_ && was_disconnected && channel_factory_) { auto status = channel_factory_->Connect(reconnect_timeout_ms_); if (!status) { error_ = -status.error(); ret.SetError(status.error()); return ret; } channel_ = status.take(); } if (!IsConnected()) { ret.SetError(ESHUTDOWN); } else { // Call the subclass OnConnect handler. The subclass may choose to close the // connection in the handler, in which case error_ will be non-zero. if (was_disconnected) OnConnect(); if (!IsConnected()) ret.SetError(-error_); else ret.SetValue(); } return ret; } bool Client::NeedToDisconnectChannel(int error) const { return error == ESHUTDOWN && auto_reconnect_enabled_; } void Client::CheckDisconnect(int error) { if (NeedToDisconnectChannel(error)) Close(error); } Client::Client(std::unique_ptr channel) : channel_{std::move(channel)} {} Client::Client(std::unique_ptr channel_factory, int64_t timeout_ms) : channel_factory_{std::move(channel_factory)} { auto status = channel_factory_->Connect(timeout_ms); if (!status) { ALOGE("Client::Client: Failed to connect to service because: %s", status.GetErrorMessage().c_str()); error_ = -status.error(); } else { channel_ = status.take(); } } bool Client::IsInitialized() const { return IsConnected() || (channel_factory_ && auto_reconnect_enabled_); } void Client::OnConnect() {} int Client::error() const { return error_; } Status Client::SendImpulse(int opcode) { PDX_TRACE_NAME("Client::SendImpulse"); auto status = CheckReconnect(); if (!status) return status; status = channel_->SendImpulse(opcode, nullptr, 0); CheckDisconnect(status); return status; } Status Client::SendImpulse(int opcode, const void* buffer, size_t length) { PDX_TRACE_NAME("Client::SendImpulse"); auto status = CheckReconnect(); if (!status) return status; status = channel_->SendImpulse(opcode, buffer, length); CheckDisconnect(status); return status; } void Client::Close(int error) { channel_.reset(); // Normalize error codes to negative integer space. error_ = error <= 0 ? error : -error; } int Client::event_fd() const { return IsConnected() ? channel_->event_fd() : -1; } LocalChannelHandle& Client::GetChannelHandle() { return channel_->GetChannelHandle(); } const LocalChannelHandle& Client::GetChannelHandle() const { return channel_->GetChannelHandle(); } ///////////////////////////// Transaction implementation ////////////////////// Transaction::Transaction(Client& client) : client_{client} {} Transaction::~Transaction() { if (state_allocated_ && client_.GetChannel()) client_.GetChannel()->FreeTransactionState(state_); } bool Transaction::EnsureStateAllocated() { if (!state_allocated_ && client_.GetChannel()) { state_ = client_.GetChannel()->AllocateTransactionState(); state_allocated_ = true; } return state_allocated_; } void Transaction::SendTransaction(int opcode, Status* ret, const iovec* send_vector, size_t send_count, const iovec* receive_vector, size_t receive_count) { *ret = client_.CheckReconnect(); if (!*ret) return; if (!EnsureStateAllocated()) { ret->SetError(ESHUTDOWN); return; } auto status = client_.GetChannel()->SendWithInt( state_, opcode, send_vector, send_count, receive_vector, receive_count); if (status) { ret->SetValue(); } else { ret->SetError(status.error()); } CheckDisconnect(status); } void Transaction::SendTransaction(int opcode, Status* ret, const iovec* send_vector, size_t send_count, const iovec* receive_vector, size_t receive_count) { auto status = client_.CheckReconnect(); if (!status) { ret->SetError(status.error()); return; } if (!EnsureStateAllocated()) { ret->SetError(ESHUTDOWN); return; } *ret = client_.GetChannel()->SendWithInt( state_, opcode, send_vector, send_count, receive_vector, receive_count); CheckDisconnect(*ret); } void Transaction::SendTransaction(int opcode, Status* ret, const iovec* send_vector, size_t send_count, const iovec* receive_vector, size_t receive_count) { auto status = client_.CheckReconnect(); if (!status) { ret->SetError(status.error()); return; } if (!EnsureStateAllocated()) { ret->SetError(ESHUTDOWN); return; } *ret = client_.GetChannel()->SendWithFileHandle( state_, opcode, send_vector, send_count, receive_vector, receive_count); CheckDisconnect(*ret); } void Transaction::SendTransaction(int opcode, Status* ret, const iovec* send_vector, size_t send_count, const iovec* receive_vector, size_t receive_count) { auto status = client_.CheckReconnect(); if (!status) { ret->SetError(status.error()); return; } if (!EnsureStateAllocated()) { ret->SetError(ESHUTDOWN); return; } *ret = client_.GetChannel()->SendWithChannelHandle( state_, opcode, send_vector, send_count, receive_vector, receive_count); CheckDisconnect(*ret); } Status Transaction::PushFileHandle(const LocalHandle& handle) { if (client_.CheckReconnect() && EnsureStateAllocated()) return client_.GetChannel()->PushFileHandle(state_, handle); return ErrorStatus{ESHUTDOWN}; } Status Transaction::PushFileHandle( const BorrowedHandle& handle) { if (client_.CheckReconnect() && EnsureStateAllocated()) return client_.GetChannel()->PushFileHandle(state_, handle); return ErrorStatus{ESHUTDOWN}; } Status Transaction::PushFileHandle(const RemoteHandle& handle) { return handle.Get(); } Status Transaction::PushChannelHandle( const LocalChannelHandle& handle) { if (client_.CheckReconnect() && EnsureStateAllocated()) return client_.GetChannel()->PushChannelHandle(state_, handle); return ErrorStatus{ESHUTDOWN}; } Status Transaction::PushChannelHandle( const BorrowedChannelHandle& handle) { if (client_.CheckReconnect() && EnsureStateAllocated()) return client_.GetChannel()->PushChannelHandle(state_, handle); return ErrorStatus{ESHUTDOWN}; } Status Transaction::PushChannelHandle( const RemoteChannelHandle& handle) { return handle.value(); } bool Transaction::GetFileHandle(FileReference ref, LocalHandle* handle) { return client_.CheckReconnect() && EnsureStateAllocated() && client_.GetChannel()->GetFileHandle(state_, ref, handle); } bool Transaction::GetChannelHandle(ChannelReference ref, LocalChannelHandle* handle) { return client_.CheckReconnect() && EnsureStateAllocated() && client_.GetChannel()->GetChannelHandle(state_, ref, handle); } void Transaction::CheckDisconnect(int error) { if (client_.NeedToDisconnectChannel(error)) { if (state_allocated_) { if (client_.GetChannel()) client_.GetChannel()->FreeTransactionState(state_); state_ = nullptr; state_allocated_ = false; } client_.Close(error); } } } // namespace pdx } // namespace android