1#include "pdx/client.h"
2
3#include <log/log.h>
4
5#include <pdx/trace.h>
6
7namespace android {
8namespace pdx {
9
10void Client::EnableAutoReconnect(int64_t reconnect_timeout_ms) {
11  if (channel_factory_) {
12    reconnect_timeout_ms_ = reconnect_timeout_ms;
13    auto_reconnect_enabled_ = true;
14  }
15}
16
17void Client::DisableAutoReconnect() { auto_reconnect_enabled_ = false; }
18
19bool Client::IsConnected() const { return channel_.get() != nullptr; }
20
21Status<void> Client::CheckReconnect() {
22  Status<void> ret;
23  bool was_disconnected = !IsConnected();
24  if (auto_reconnect_enabled_ && was_disconnected && channel_factory_) {
25    auto status = channel_factory_->Connect(reconnect_timeout_ms_);
26    if (!status) {
27      error_ = -status.error();
28      ret.SetError(status.error());
29      return ret;
30    }
31    channel_ = status.take();
32  }
33
34  if (!IsConnected()) {
35    ret.SetError(ESHUTDOWN);
36  } else {
37    // Call the subclass OnConnect handler. The subclass may choose to close the
38    // connection in the handler, in which case error_ will be non-zero.
39    if (was_disconnected)
40      OnConnect();
41    if (!IsConnected())
42      ret.SetError(-error_);
43    else
44      ret.SetValue();
45  }
46
47  return ret;
48}
49
50bool Client::NeedToDisconnectChannel(int error) const {
51  return error == ESHUTDOWN && auto_reconnect_enabled_;
52}
53
54void Client::CheckDisconnect(int error) {
55  if (NeedToDisconnectChannel(error))
56    Close(error);
57}
58
59Client::Client(std::unique_ptr<ClientChannel> channel)
60    : channel_{std::move(channel)} {}
61
62Client::Client(std::unique_ptr<ClientChannelFactory> channel_factory,
63               int64_t timeout_ms)
64    : channel_factory_{std::move(channel_factory)} {
65  auto status = channel_factory_->Connect(timeout_ms);
66  if (!status) {
67    ALOGE("Client::Client: Failed to connect to service because: %s",
68          status.GetErrorMessage().c_str());
69    error_ = -status.error();
70  } else {
71    channel_ = status.take();
72  }
73}
74
75bool Client::IsInitialized() const {
76  return IsConnected() || (channel_factory_ && auto_reconnect_enabled_);
77}
78
79void Client::OnConnect() {}
80
81int Client::error() const { return error_; }
82
83Status<void> Client::SendImpulse(int opcode) {
84  PDX_TRACE_NAME("Client::SendImpulse");
85
86  auto status = CheckReconnect();
87  if (!status)
88    return status;
89
90  status = channel_->SendImpulse(opcode, nullptr, 0);
91  CheckDisconnect(status);
92  return status;
93}
94
95Status<void> Client::SendImpulse(int opcode, const void* buffer,
96                                 size_t length) {
97  PDX_TRACE_NAME("Client::SendImpulse");
98
99  auto status = CheckReconnect();
100  if (!status)
101    return status;
102
103  status = channel_->SendImpulse(opcode, buffer, length);
104  CheckDisconnect(status);
105  return status;
106}
107
108void Client::Close(int error) {
109  channel_.reset();
110  // Normalize error codes to negative integer space.
111  error_ = error <= 0 ? error : -error;
112}
113
114int Client::event_fd() const {
115  return IsConnected() ? channel_->event_fd() : -1;
116}
117
118LocalChannelHandle& Client::GetChannelHandle() {
119  return channel_->GetChannelHandle();
120}
121
122const LocalChannelHandle& Client::GetChannelHandle() const {
123  return channel_->GetChannelHandle();
124}
125
126///////////////////////////// Transaction implementation //////////////////////
127
128Transaction::Transaction(Client& client) : client_{client} {}
129
130Transaction::~Transaction() {
131  if (state_allocated_ && client_.GetChannel())
132    client_.GetChannel()->FreeTransactionState(state_);
133}
134
135bool Transaction::EnsureStateAllocated() {
136  if (!state_allocated_ && client_.GetChannel()) {
137    state_ = client_.GetChannel()->AllocateTransactionState();
138    state_allocated_ = true;
139  }
140  return state_allocated_;
141}
142
143void Transaction::SendTransaction(int opcode, Status<void>* ret,
144                                  const iovec* send_vector, size_t send_count,
145                                  const iovec* receive_vector,
146                                  size_t receive_count) {
147  *ret = client_.CheckReconnect();
148  if (!*ret)
149    return;
150
151  if (!EnsureStateAllocated()) {
152    ret->SetError(ESHUTDOWN);
153    return;
154  }
155
156  auto status = client_.GetChannel()->SendWithInt(
157      state_, opcode, send_vector, send_count, receive_vector, receive_count);
158
159  if (status) {
160    ret->SetValue();
161  } else {
162    ret->SetError(status.error());
163  }
164  CheckDisconnect(status);
165}
166
167void Transaction::SendTransaction(int opcode, Status<int>* ret,
168                                  const iovec* send_vector, size_t send_count,
169                                  const iovec* receive_vector,
170                                  size_t receive_count) {
171  auto status = client_.CheckReconnect();
172  if (!status) {
173    ret->SetError(status.error());
174    return;
175  }
176
177  if (!EnsureStateAllocated()) {
178    ret->SetError(ESHUTDOWN);
179    return;
180  }
181
182  *ret = client_.GetChannel()->SendWithInt(
183      state_, opcode, send_vector, send_count, receive_vector, receive_count);
184
185  CheckDisconnect(*ret);
186}
187
188void Transaction::SendTransaction(int opcode, Status<LocalHandle>* ret,
189                                  const iovec* send_vector, size_t send_count,
190                                  const iovec* receive_vector,
191                                  size_t receive_count) {
192  auto status = client_.CheckReconnect();
193  if (!status) {
194    ret->SetError(status.error());
195    return;
196  }
197
198  if (!EnsureStateAllocated()) {
199    ret->SetError(ESHUTDOWN);
200    return;
201  }
202
203  *ret = client_.GetChannel()->SendWithFileHandle(
204      state_, opcode, send_vector, send_count, receive_vector, receive_count);
205
206  CheckDisconnect(*ret);
207}
208
209void Transaction::SendTransaction(int opcode, Status<LocalChannelHandle>* ret,
210                                  const iovec* send_vector, size_t send_count,
211                                  const iovec* receive_vector,
212                                  size_t receive_count) {
213  auto status = client_.CheckReconnect();
214  if (!status) {
215    ret->SetError(status.error());
216    return;
217  }
218
219  if (!EnsureStateAllocated()) {
220    ret->SetError(ESHUTDOWN);
221    return;
222  }
223
224  *ret = client_.GetChannel()->SendWithChannelHandle(
225      state_, opcode, send_vector, send_count, receive_vector, receive_count);
226
227  CheckDisconnect(*ret);
228}
229
230Status<FileReference> Transaction::PushFileHandle(const LocalHandle& handle) {
231  if (client_.CheckReconnect() && EnsureStateAllocated())
232    return client_.GetChannel()->PushFileHandle(state_, handle);
233  return ErrorStatus{ESHUTDOWN};
234}
235
236Status<FileReference> Transaction::PushFileHandle(
237    const BorrowedHandle& handle) {
238  if (client_.CheckReconnect() && EnsureStateAllocated())
239    return client_.GetChannel()->PushFileHandle(state_, handle);
240  return ErrorStatus{ESHUTDOWN};
241}
242
243Status<FileReference> Transaction::PushFileHandle(const RemoteHandle& handle) {
244  return handle.Get();
245}
246
247Status<ChannelReference> Transaction::PushChannelHandle(
248    const LocalChannelHandle& handle) {
249  if (client_.CheckReconnect() && EnsureStateAllocated())
250    return client_.GetChannel()->PushChannelHandle(state_, handle);
251  return ErrorStatus{ESHUTDOWN};
252}
253
254Status<ChannelReference> Transaction::PushChannelHandle(
255    const BorrowedChannelHandle& handle) {
256  if (client_.CheckReconnect() && EnsureStateAllocated())
257    return client_.GetChannel()->PushChannelHandle(state_, handle);
258  return ErrorStatus{ESHUTDOWN};
259}
260
261Status<ChannelReference> Transaction::PushChannelHandle(
262    const RemoteChannelHandle& handle) {
263  return handle.value();
264}
265
266bool Transaction::GetFileHandle(FileReference ref, LocalHandle* handle) {
267  return client_.CheckReconnect() && EnsureStateAllocated() &&
268         client_.GetChannel()->GetFileHandle(state_, ref, handle);
269}
270
271bool Transaction::GetChannelHandle(ChannelReference ref,
272                                   LocalChannelHandle* handle) {
273  return client_.CheckReconnect() && EnsureStateAllocated() &&
274         client_.GetChannel()->GetChannelHandle(state_, ref, handle);
275}
276
277void Transaction::CheckDisconnect(int error) {
278  if (client_.NeedToDisconnectChannel(error)) {
279    if (state_allocated_) {
280      if (client_.GetChannel())
281        client_.GetChannel()->FreeTransactionState(state_);
282      state_ = nullptr;
283      state_allocated_ = false;
284    }
285    client_.Close(error);
286  }
287}
288
289}  // namespace pdx
290}  // namespace android
291