|  | #include "pdx/client.h" | 
|  |  | 
|  | #include <log/log.h> | 
|  |  | 
|  | #include <pdx/trace.h> | 
|  |  | 
|  | 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<void> Client::CheckReconnect() { | 
|  | Status<void> 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<ClientChannel> channel) | 
|  | : channel_{std::move(channel)} {} | 
|  |  | 
|  | Client::Client(std::unique_ptr<ClientChannelFactory> 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<void> 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<void> 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<void>* 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<int>* 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<LocalHandle>* 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<LocalChannelHandle>* 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<FileReference> Transaction::PushFileHandle(const LocalHandle& handle) { | 
|  | if (client_.CheckReconnect() && EnsureStateAllocated()) | 
|  | return client_.GetChannel()->PushFileHandle(state_, handle); | 
|  | return ErrorStatus{ESHUTDOWN}; | 
|  | } | 
|  |  | 
|  | Status<FileReference> Transaction::PushFileHandle( | 
|  | const BorrowedHandle& handle) { | 
|  | if (client_.CheckReconnect() && EnsureStateAllocated()) | 
|  | return client_.GetChannel()->PushFileHandle(state_, handle); | 
|  | return ErrorStatus{ESHUTDOWN}; | 
|  | } | 
|  |  | 
|  | Status<FileReference> Transaction::PushFileHandle(const RemoteHandle& handle) { | 
|  | return handle.Get(); | 
|  | } | 
|  |  | 
|  | Status<ChannelReference> Transaction::PushChannelHandle( | 
|  | const LocalChannelHandle& handle) { | 
|  | if (client_.CheckReconnect() && EnsureStateAllocated()) | 
|  | return client_.GetChannel()->PushChannelHandle(state_, handle); | 
|  | return ErrorStatus{ESHUTDOWN}; | 
|  | } | 
|  |  | 
|  | Status<ChannelReference> Transaction::PushChannelHandle( | 
|  | const BorrowedChannelHandle& handle) { | 
|  | if (client_.CheckReconnect() && EnsureStateAllocated()) | 
|  | return client_.GetChannel()->PushChannelHandle(state_, handle); | 
|  | return ErrorStatus{ESHUTDOWN}; | 
|  | } | 
|  |  | 
|  | Status<ChannelReference> 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 |