#ifndef ANDROID_PDX_CLIENT_H_ #define ANDROID_PDX_CLIENT_H_ #include #include #include #include #include #include #include #include #include #include #include #include #include namespace android { namespace pdx { class Transaction; /* * Base class of client-side service API classes. */ class Client { public: static const int64_t kInfiniteTimeout = -1; virtual ~Client() = default; /* * Returns true if the Client instance successfully initialized, false * otherwise. Subclasses that can fail to initialize must override this and * AND their initialization result with this base class method's result. * * This method is not intended to perform initialization, only to report * the status of the initialization. */ virtual bool IsInitialized() const; /* * Returns the error code describing the Client initialization failure, or 0 * if there was no failure. */ int error() const; // Returns a reference to IPC channel handle. LocalChannelHandle& GetChannelHandle(); const LocalChannelHandle& GetChannelHandle() const; protected: friend Transaction; explicit Client(std::unique_ptr channel); explicit Client(std::unique_ptr channel_factory, int64_t timeout_ms = kInfiniteTimeout); /* * Called by Client::Connect() after successfully connecting to the service * endpoint. Subclasses may override this method to perform additional setup, * including sending messages to complete the connection process. * * Subclasses may call Client::Close() within this method to terminate the * connection; Client::Connect() returns the negated error passed to * Client::Close() when this happens. */ virtual void OnConnect(); enum : size_t { MAX_IMPULSE_LENGTH = sizeof(uint64_t) * 4 }; Status SendImpulse(int opcode); Status SendImpulse(int opcode, const void* buffer, size_t length); /* * Remote method call API using pdx::rpc serialization. * Include pdx/rpc/remote_method.h to use these methods. */ template Status InvokeRemoteMethod(Args&&... args); template Status InvokeRemoteMethodInPlace(ReturnType* return_value, Args&&... args); /* * Close the endpoint file descriptor and optionally indicate an error, which * may be retrieved through error(). Subclasses may use this in their * constructor to signal failure during initialization or at other times * during operation. */ void Close(int error); /* * Returns true if the client is connected to the service, false otherwise. */ bool IsConnected() const; /* * Enables auto-reconnect with the given timeout. Use kInfiniteTimeout (-1) * for no timeout. Auto-reconnect can only be enabled if the Client class * was constructed with a ClientChannelFactory. */ void EnableAutoReconnect(int64_t reconnect_timeout_ms); /* * Disables auto-reconnect. */ void DisableAutoReconnect(); /* * Returns an fd that the client may use to check/wait for asynchronous * notifications to the channel. It is implementation dependent how the * transport backend handles this feature, however all implementations must * support at least POLLIN/EPOLLIN/readable. * * For uses that require more than one type of event, use * ClientChannel::GetEventMask() to distinguish between events. */ int event_fd() const; /* * Returns the underlying ClientChannel object. */ ClientChannel* GetChannel() const { return channel_.get(); } std::unique_ptr&& TakeChannel() { return std::move(channel_); } private: Client(const Client&) = delete; void operator=(const Client&) = delete; Status CheckReconnect(); bool NeedToDisconnectChannel(int error) const; void CheckDisconnect(int error); template inline void CheckDisconnect(const Status& status) { if (!status) CheckDisconnect(status.error()); } std::unique_ptr channel_; int error_{0}; // Reconnection state. std::unique_ptr channel_factory_; int64_t reconnect_timeout_ms_{0}; bool auto_reconnect_enabled_{false}; }; /* * Utility template base class for client-side service API classes. Handles * initialization checks during allocation and automatically cleans up on * failure. * * @tparam T Type of the class extending this one. * @tparam C Client class to wrap. Defaults to the Client class. */ template class ClientBase : public ParentClient { public: // Type of the client this class wraps. using ClientType = ParentClient; static_assert(std::is_base_of::value, "The provided parent client is not a Client subclass."); /* * Allocates a new instance of the superclass and checks for successful * initialization. * * The variadic arguments must expand to match one of type T's constructors * and are passed through unchanged. If a timeout is desired, subclasses are * responsible for passing this through to the appropriate ClientBase * constructor. * * Returns a unique_ptr to the new instance on success, or an empty unique_ptr * otherwise. */ template static inline std::unique_ptr Create(Args&&... args) { std::unique_ptr client(new T(std::forward(args)...)); if (client->IsInitialized()) return client; else return nullptr; } protected: /* * Type of the base class. Useful for referencing the base class type and * constructor in subclasses. Subclasses with non-public constructors * must declare BASE a friend. */ using BASE = ClientBase; /* * Type of the unique_ptr deleter. Useful for friend declarations. */ using deleter_type = typename std::unique_ptr::deleter_type; using ParentClient::ParentClient; }; class Transaction final : public OutputResourceMapper, public InputResourceMapper { public: explicit Transaction(Client& client); ~Transaction(); template Status Send(int opcode) { return SendVector(opcode, nullptr, 0, nullptr, 0); } template Status Send(int opcode, const void* send_buffer, size_t send_length, void* receive_buffer, size_t receive_length) { const bool send = (send_buffer && send_length); const bool receive = (receive_buffer && receive_length); const iovec send_vector = {const_cast(send_buffer), send_length}; const iovec receive_vector = {receive_buffer, receive_length}; return SendVector(opcode, send ? &send_vector : nullptr, send ? 1 : 0, receive ? &receive_vector : nullptr, receive ? 1 : 0); } template Status SendVector(int opcode, const iovec* send_vector, size_t send_count, const iovec* receive_vector, size_t receive_count) { Status ret; SendTransaction(opcode, &ret, send_vector, send_count, receive_vector, receive_count); return ret; } template Status SendVector(int opcode, const iovec (&send_vector)[send_count], const iovec (&receive_vector)[receive_count]) { return SendVector(opcode, send_vector, send_count, receive_vector, receive_count); } template Status SendVector(int opcode, const iovec (&send_vector)[send_count], std::nullptr_t) { return SendVector(opcode, send_vector, send_count, nullptr, 0); } template Status SendVector(int opcode, std::nullptr_t, const iovec (&receive_vector)[receive_count]) { return SendVector(opcode, nullptr, 0, receive_vector, receive_count); } // OutputResourceMapper Status PushFileHandle(const LocalHandle& handle) override; Status PushFileHandle(const BorrowedHandle& handle) override; Status PushFileHandle(const RemoteHandle& handle) override; Status PushChannelHandle( const LocalChannelHandle& handle) override; Status PushChannelHandle( const BorrowedChannelHandle& handle) override; Status PushChannelHandle( const RemoteChannelHandle& handle) override; // InputResourceMapper bool GetFileHandle(FileReference ref, LocalHandle* handle) override; bool GetChannelHandle(ChannelReference ref, LocalChannelHandle* handle) override; private: bool EnsureStateAllocated(); void SendTransaction(int opcode, Status* ret, const iovec* send_vector, size_t send_count, const iovec* receive_vector, size_t receive_count); void SendTransaction(int opcode, Status* ret, const iovec* send_vector, size_t send_count, const iovec* receive_vector, size_t receive_count); void SendTransaction(int opcode, Status* ret, const iovec* send_vector, size_t send_count, const iovec* receive_vector, size_t receive_count); void SendTransaction(int opcode, Status* ret, const iovec* send_vector, size_t send_count, const iovec* receive_vector, size_t receive_count); void CheckDisconnect(int error); template inline void CheckDisconnect(const Status& status) { if (!status) CheckDisconnect(status.error()); } Client& client_; void* state_{nullptr}; bool state_allocated_{false}; }; } // namespace pdx } // namespace android #endif // ANDROID_PDX_CLIENT_H_