| #ifndef ANDROID_PDX_SERVICE_H_ |
| #define ANDROID_PDX_SERVICE_H_ |
| |
| #include <errno.h> |
| #include <log/log.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| |
| #include <algorithm> |
| #include <memory> |
| #include <mutex> |
| #include <string> |
| #include <unordered_map> |
| #include <vector> |
| |
| #include "pdx/channel_handle.h" |
| #include "pdx/file_handle.h" |
| #include "pdx/message_reader.h" |
| #include "pdx/message_writer.h" |
| #include "pdx/service_endpoint.h" |
| |
| namespace android { |
| namespace pdx { |
| |
| class Service; |
| |
| namespace opcodes { |
| |
| /* |
| * Reserved message opcodes used by libpdx. The reserved opcodes start at the |
| * max positive signed integer for the system and go down. |
| * In contrast, service opcodes start at zero and go up. This scheme leaves |
| * most of the positive integer space for services, a tiny fraction of the |
| * positive integer space for the framework, and the entire negative integer |
| * space for the kernel. |
| */ |
| #define PDX_OPCODE(name, n) name = ((-1U >> 1) - (n)) // 0x7fff..ffff - n |
| |
| enum { |
| // System message sent when a new client channel is open. |
| CHANNEL_OPEN = -1, |
| // System message sent when a channel is closed. |
| CHANNEL_CLOSE = -2, |
| // Request the service to reload system properties. |
| PDX_OPCODE(REPORT_SYSPROP_CHANGE, 0), |
| // Request the service to dump state and return it in a text buffer. |
| PDX_OPCODE(DUMP_STATE, 1), |
| }; |
| |
| } // namespace opcodes |
| |
| /* |
| * Base class of service-side per-channel context classes. |
| */ |
| class Channel : public std::enable_shared_from_this<Channel> { |
| public: |
| Channel() {} |
| virtual ~Channel() {} |
| |
| /* |
| * Utility to get a shared_ptr reference from the channel context pointer. |
| */ |
| static std::shared_ptr<Channel> GetFromMessageInfo(const MessageInfo& info); |
| }; |
| |
| /* |
| * Message class represents an RPC message, and implicitly the blocked sender |
| * waiting for a response. Every message should get a reply, at some point |
| * (unless the endpoint is closed), to prevent clients from blocking |
| * indefinitely. In order to enforce this and prevent leaking message ids, |
| * Message automatically replies with an error to the client on destruction, |
| * unless one of two things happens: |
| * |
| * 1. The service calls one of the reply methods before the Message is |
| * destroyed. |
| * 2. The responsibility for the message is moved to another instance of |
| * Message, using either move construction or move assignment. |
| * |
| * The second case is useful for services that need to delay responding to a |
| * sender until a later time. In this situation the service can move the |
| * Message to another instance in a suitable data structure for later use. The |
| * moved-to Message then takes on the same behavior and responsibilities |
| * described above. |
| */ |
| class Message : public OutputResourceMapper, public InputResourceMapper { |
| public: |
| Message(); |
| Message(const MessageInfo& info); |
| ~Message(); |
| |
| /* |
| * Message objects support move construction and assignment. |
| */ |
| Message(Message&& other); |
| Message& operator=(Message&& other); |
| |
| /* |
| * Read/write payload, in either single buffer or iovec form. |
| */ |
| Status<size_t> ReadVector(const iovec* vector, size_t vector_length); |
| Status<size_t> Read(void* buffer, size_t length); |
| Status<size_t> WriteVector(const iovec* vector, size_t vector_length); |
| Status<size_t> Write(const void* buffer, size_t length); |
| |
| template <size_t N> |
| inline Status<size_t> ReadVector(const iovec (&vector)[N]) { |
| return ReadVector(vector, N); |
| } |
| |
| template <size_t N> |
| inline Status<size_t> WriteVector(const iovec (&vector)[N]) { |
| return WriteVector(vector, N); |
| } |
| |
| // Helper functions to read/write all requested bytes, and return EIO if not |
| // all were read/written. |
| Status<void> ReadVectorAll(const iovec* vector, size_t vector_length); |
| Status<void> WriteVectorAll(const iovec* vector, size_t vector_length); |
| |
| inline Status<void> ReadAll(void* buffer, size_t length) { |
| Status<size_t> status = Read(buffer, length); |
| if (status && status.get() < length) |
| status.SetError(EIO); |
| Status<void> ret; |
| ret.PropagateError(status); |
| return ret; |
| } |
| inline Status<void> WriteAll(const void* buffer, size_t length) { |
| Status<size_t> status = Write(buffer, length); |
| if (status && status.get() < length) |
| status.SetError(EIO); |
| Status<void> ret; |
| ret.PropagateError(status); |
| return ret; |
| } |
| |
| template <size_t N> |
| inline Status<void> ReadVectorAll(const iovec (&vector)[N]) { |
| return ReadVectorAll(vector, N); |
| } |
| |
| template <size_t N> |
| inline Status<void> WriteVectorAll(const iovec (&vector)[N]) { |
| return WriteVectorAll(vector, N); |
| } |
| |
| // OutputResourceMapper |
| Status<FileReference> PushFileHandle(const LocalHandle& handle) override; |
| Status<FileReference> PushFileHandle(const BorrowedHandle& handle) override; |
| Status<FileReference> PushFileHandle(const RemoteHandle& handle) override; |
| Status<ChannelReference> PushChannelHandle( |
| const LocalChannelHandle& handle) override; |
| Status<ChannelReference> PushChannelHandle( |
| const BorrowedChannelHandle& handle) override; |
| Status<ChannelReference> PushChannelHandle( |
| const RemoteChannelHandle& handle) override; |
| |
| // InputResourceMapper |
| bool GetFileHandle(FileReference ref, LocalHandle* handle) override; |
| bool GetChannelHandle(ChannelReference ref, |
| LocalChannelHandle* handle) override; |
| |
| /* |
| * Various ways to reply to a message. |
| */ |
| Status<void> Reply(int return_code); |
| Status<void> ReplyError(unsigned int error); |
| Status<void> ReplyFileDescriptor(unsigned int fd); |
| Status<void> Reply(const LocalHandle& handle); |
| Status<void> Reply(const BorrowedHandle& handle); |
| Status<void> Reply(const RemoteHandle& handle); |
| Status<void> Reply(const LocalChannelHandle& handle); |
| Status<void> Reply(const BorrowedChannelHandle& handle); |
| Status<void> Reply(const RemoteChannelHandle& handle); |
| |
| template <typename T> |
| inline Status<void> Reply(const Status<T>& status) { |
| return status ? Reply(status.get()) : ReplyError(status.error()); |
| } |
| |
| inline Status<void> Reply(const Status<void>& status) { |
| return status ? Reply(0) : ReplyError(status.error()); |
| } |
| |
| /* |
| * Update the channel event bits with the given clear and set masks. |
| */ |
| Status<void> ModifyChannelEvents(int clear_mask, int set_mask); |
| |
| /* |
| * Create a new channel and push it as a file descriptor to the client. See |
| * Service::PushChannel() for a detail description of this method's operation. |
| */ |
| Status<RemoteChannelHandle> PushChannel( |
| int flags, const std::shared_ptr<Channel>& channel, int* channel_id); |
| |
| /* |
| * Create a new channel and push it as a file descriptor to the client. See |
| * Service::PushChannel() for a detail description of this method's operation. |
| */ |
| Status<RemoteChannelHandle> PushChannel( |
| Service* service, int flags, const std::shared_ptr<Channel>& channel, |
| int* channel_id); |
| |
| /* |
| * Check whether the |ref| is a reference to channel to this service. |
| * If the channel reference in question is valid, the Channel object is |
| * returned in |channel| when non-nullptr. |
| * |
| * Return values: |
| * channel_id - id of the channel if the |ref| is a valid reference to |
| * this service's channel. |
| * Errors: |
| * EOPNOTSUPP - the file descriptor is not a channel or is a channel to |
| * another service. |
| * EBADF - the file descriptor is invalid. |
| * FAULT - |channel_id| or |channel| are non-nullptr and point to invalid |
| * memory addresses. |
| * EINVAL - the value of |ref| is invalid or the message id for this |
| * message is no longer valid. |
| */ |
| Status<int> CheckChannel(ChannelReference ref, |
| std::shared_ptr<Channel>* channel) const; |
| |
| /* |
| * Overload of CheckChannel() that checks whether the channel reference is for |
| * a channel to the service |service|. |
| */ |
| Status<int> CheckChannel(const Service* service, ChannelReference ref, |
| std::shared_ptr<Channel>* channel) const; |
| |
| /* |
| * Overload of CheckChannel() that automatically converts to shared pointers |
| * to types derived from Channel. |
| */ |
| template <class C> |
| Status<int> CheckChannel(ChannelReference ref, |
| std::shared_ptr<C>* channel) const { |
| std::shared_ptr<Channel> base_pointer; |
| const Status<int> ret = |
| CheckChannel(ref, channel ? &base_pointer : nullptr); |
| if (channel) |
| *channel = std::static_pointer_cast<C>(base_pointer); |
| return ret; |
| } |
| |
| template <class C> |
| Status<int> CheckChannel(const Service* service, ChannelReference ref, |
| std::shared_ptr<C>* channel) const { |
| std::shared_ptr<Channel> base_pointer; |
| const Status<int> ret = |
| CheckChannel(service, ref, channel ? &base_pointer : nullptr); |
| if (channel) |
| *channel = std::static_pointer_cast<C>(base_pointer); |
| return ret; |
| } |
| |
| /* |
| * MessageInfo accessors. |
| */ |
| pid_t GetProcessId() const; |
| pid_t GetThreadId() const; |
| uid_t GetEffectiveUserId() const; |
| gid_t GetEffectiveGroupId() const; |
| int GetChannelId() const; |
| int GetMessageId() const; |
| int GetOp() const; |
| int GetFlags() const; |
| size_t GetSendLength() const; |
| size_t GetReceiveLength() const; |
| size_t GetFileDescriptorCount() const; |
| |
| /* |
| * Impulses are asynchronous and cannot be replied to. All impulses have this |
| * invalid message id. |
| */ |
| enum { IMPULSE_MESSAGE_ID = -1 }; |
| |
| /* |
| * Returns true if this Message describes an asynchronous "impulse" message. |
| */ |
| bool IsImpulse() const { return GetMessageId() == IMPULSE_MESSAGE_ID; } |
| |
| /* |
| * Returns a pointer to the impulse payload. Impulses are a maximum of 32 |
| * bytes in size and the start of the impulse payload is guaranteed to be |
| * 8-byte aligned. Use GetSendLength() to determine the payload size. |
| */ |
| const std::uint8_t* ImpulseBegin() const; |
| |
| /* |
| * Returns one byte past the end of the impulse payload, as conventional for |
| * STL iterators. |
| */ |
| const std::uint8_t* ImpulseEnd() const; |
| |
| /* |
| * Get/set the Channel object for the channel associated |
| * with this message. It is up to the caller to synchronize |
| * these in multi-threaded services. |
| */ |
| std::shared_ptr<Channel> GetChannel() const; |
| Status<void> SetChannel(const std::shared_ptr<Channel>& channnel); |
| |
| /* |
| * Get the Channel object for the channel associated with this message, |
| * automatically converted to the desired subclass of Channel. |
| */ |
| template <class C> |
| std::shared_ptr<C> GetChannel() const { |
| return std::static_pointer_cast<C>(GetChannel()); |
| } |
| |
| /* |
| * Gets the service this message was received on. Returns nullptr if the |
| * service was destroyed. |
| */ |
| std::shared_ptr<Service> GetService() const; |
| |
| /* |
| * Raw access to the MessageInfo for this message. |
| */ |
| const MessageInfo& GetInfo() const; |
| |
| bool replied() const { return replied_; } |
| bool IsChannelExpired() const { return channel_.expired(); } |
| bool IsServiceExpired() const { return service_.expired(); } |
| |
| /* |
| * Returns true if the message is non-empty; that is the message can be |
| * replied to using this instance. |
| */ |
| explicit operator bool() const { return !replied_; } |
| |
| const void* GetState() const { return state_; } |
| void* GetState() { return state_; } |
| |
| private: |
| friend class Service; |
| |
| Message(const Message&) = delete; |
| void operator=(const Message&) = delete; |
| void Destroy(); |
| |
| std::weak_ptr<Service> service_; |
| std::weak_ptr<Channel> channel_; |
| MessageInfo info_; |
| void* state_{nullptr}; |
| bool replied_; |
| }; |
| |
| // Base class for RPC services. |
| class Service : public std::enable_shared_from_this<Service> { |
| public: |
| Service(const std::string& name, std::unique_ptr<Endpoint> endpoint); |
| virtual ~Service(); |
| |
| /* |
| * Utility to get a shared_ptr reference from the service context pointer. |
| */ |
| static std::shared_ptr<Service> GetFromMessageInfo(const MessageInfo& info); |
| |
| /* |
| * Returns whether initialization was successful. Subclasses that override |
| * this must call this base method and AND the results with their own. This |
| * method is not intended to do any initialization work itself, only to |
| * signal success or failure. |
| */ |
| virtual bool IsInitialized() const; |
| |
| /* |
| * Called by defaultHandleMessage in response to a CHANNEL_OPEN message. |
| * This gives subclasses of Service a convenient hook to create per-channel |
| * context in the form of a Channel subclass. |
| * |
| * The Channel instance returned by this is used to set the channel context |
| * pointer for the channel that was just opened. |
| */ |
| virtual std::shared_ptr<Channel> OnChannelOpen(Message& message); |
| |
| /* |
| * Called by defaultHandleMessage in response to a CHANNEL_CLOSE message. |
| * This give subclasses of Service a convenient hook to clean up per-channel |
| * context. |
| */ |
| virtual void OnChannelClose(Message& message, |
| const std::shared_ptr<Channel>& channel); |
| |
| /* |
| * Set the channel context for the given channel. This keeps a reference to |
| * the Channel object until the channel is closed or another call replaces |
| * the current value. |
| */ |
| Status<void> SetChannel(int channel_id, |
| const std::shared_ptr<Channel>& channel); |
| |
| /* |
| * Get the channel context for the given channel id. This method should be |
| * used sparingly because of the performance characteristics of the underlying |
| * map; it is intended for limited, non-critical path access from outside of |
| * message dispatch. In most cases lookup by id should be unnecessary in a |
| * properly designed service; Message::GetChannel() should be used instead |
| * whenever an operation is in the context of a message. |
| * |
| * Again, if you lookup a channel context object for a service by id in a |
| * message handling path for the same service, you're probably doing something |
| * wrong. |
| */ |
| std::shared_ptr<Channel> GetChannel(int channel_id) const; |
| |
| /* |
| * Get a snapshot of the active channels for this service. This is the |
| * preferred way to access the set of channels because it avoids potential |
| * deadlocks and race conditions that may occur when operating on the channel |
| * map directly. However, it is more expensive than direct iteration because |
| * of dynamic memory allocation and shared pointer reference costs. |
| * |
| * Automatically converts returned items to shared pointers of the type |
| * std::shared_ptr<C>, where C is the subclass of Channel used by the service. |
| */ |
| template <class C> |
| std::vector<std::shared_ptr<C>> GetChannels() const { |
| std::lock_guard<std::mutex> autolock(channels_mutex_); |
| std::vector<std::shared_ptr<C>> items; |
| items.reserve(channels_.size()); |
| |
| for (const auto& pair : channels_) { |
| items.push_back(std::static_pointer_cast<C>(pair.second)); |
| } |
| |
| return items; |
| } |
| |
| /* |
| * Close a channel, signaling the client file object and freeing the channel |
| * id. Once closed, the client side of the channel always returns the error |
| * ESHUTDOWN and signals the poll/epoll events POLLHUP and POLLFREE. |
| * |
| * The internal reference to the Channel instance associated with the channel |
| * is removed, which may result in the Channel object being freed. |
| * |
| * OnChannelClosed is not called in response to this method call. |
| */ |
| Status<void> CloseChannel(int channel_id); |
| |
| /* |
| * Update the event bits for the given channel (given by id), using the |
| * given clear and set masks. |
| * |
| * This is useful for asynchronously signaling events that clients may be |
| * waiting for using select/poll/epoll. |
| */ |
| Status<void> ModifyChannelEvents(int channel_id, int clear_mask, |
| int set_mask); |
| |
| /* |
| * Create a new channel and push it as a file descriptor to the process |
| * sending the |message|. |flags| may be set to O_NONBLOCK and/or |
| * O_CLOEXEC to control the initial behavior of the new file descriptor (the |
| * sending process may change these later using fcntl()). The internal Channel |
| * instance associated with this channel is set to |channel|, which may be |
| * nullptr. The new channel id allocated for this channel is returned in |
| * |channel_id|, which may also be nullptr if not needed. |
| * |
| * On success, returns the remote channel handle for the new channel in the |
| * sending process' handle space. This MUST be returned to the sender via |
| * Message::Reply(), Message::Write(), or Message::WriteVector(). |
| * |
| * On error, returns an errno code describing the cause of the error. |
| * |
| * Service::OnChannelCreate() is not called in response to the creation of the |
| * new channel. |
| */ |
| Status<RemoteChannelHandle> PushChannel( |
| Message* message, int flags, const std::shared_ptr<Channel>& channel, |
| int* channel_id); |
| |
| /* |
| * Check whether the |ref| is a reference to a channel to this service. |
| * If the channel reference in question is valid, the Channel object is |
| * returned in |channel| when non-nullptr. |
| * |
| * Return values: |
| * channel_id - id of the channel if the channel reference. |
| * Errors: |
| * EOPNOTSUPP - the file descriptor is not a channel or is a channel to |
| * another service. |
| * EBADF - the file descriptor is invalid. |
| * FAULT - |channel_id| or |channel| are non-nullptr and point to invalid |
| * memory addresses. |
| * EINVAL - the value of |ref| is invalid or the message id for this |
| * message is no longer valid. |
| */ |
| Status<int> CheckChannel(const Message* message, ChannelReference ref, |
| std::shared_ptr<Channel>* channel) const; |
| |
| /* |
| * Overload of CheckChannel() that automatically converts to shared pointers |
| * of types derived from Channel. |
| */ |
| template <class C> |
| Status<int> CheckChannel(const Message* message, ChannelReference ref, |
| std::shared_ptr<C>* channel) const { |
| std::shared_ptr<Channel> base_pointer; |
| const Status<int> ret = |
| CheckChannel(message, ref, channel ? &base_pointer : nullptr); |
| if (channel) |
| *channel = std::static_pointer_cast<C>(base_pointer); |
| return ret; |
| } |
| |
| /* |
| * Handle a message. Subclasses override this to receive messages and decide |
| * how to dispatch them. |
| * |
| * The default implementation simply calls defaultHandleMessage(). |
| * Subclasses should call the same for any unrecognized message opcodes. |
| */ |
| virtual Status<void> HandleMessage(Message& message); |
| |
| /* |
| * Handle an asynchronous message. Subclasses override this to receive |
| * asynchronous "impulse" messages. Impulses have a limited-size payload that |
| * is transferred upfront with the message description. |
| */ |
| virtual void HandleImpulse(Message& impulse); |
| |
| /* |
| * The default message handler. It is important that all messages |
| * (eventually) get a reply. This method should be called by subclasses for |
| * any unrecognized opcodes or otherwise unhandled messages to prevent |
| * erroneous requests from blocking indefinitely. |
| * |
| * Provides default handling of CHANNEL_OPEN and CHANNEL_CLOSE, calling |
| * OnChannelOpen() and OnChannelClose(), respectively. |
| * |
| * For all other message opcodes, this method replies with ENOTSUP. |
| */ |
| Status<void> DefaultHandleMessage(Message& message); |
| |
| /* |
| * Called when system properties have changed. Subclasses should implement |
| * this method if they need to handle when system properties change. |
| */ |
| virtual void OnSysPropChange(); |
| |
| /* |
| * Get the endpoint for the service. |
| */ |
| Endpoint* endpoint() const { return endpoint_.get(); } |
| |
| /* |
| * Cancels the endpoint, unblocking any receiver threads waiting in |
| * ReceiveAndDispatch(). |
| */ |
| Status<void> Cancel(); |
| |
| /* |
| * Iterator type for Channel map iterators. |
| */ |
| using ChannelIterator = |
| std::unordered_map<int, std::shared_ptr<Channel>>::iterator; |
| |
| /* |
| * Iterates over the Channel map and performs the action given by |action| on |
| * each channel map item (const ChannelIterator::value_type). |
| * |channels_mutex_| is not held; it is the responsibility of the caller to |
| * ensure serialization between threads that modify or iterate over the |
| * Channel map. |
| */ |
| template <class A> |
| void ForEachChannelUnlocked(A action) const { |
| std::for_each(channels_.begin(), channels_.end(), action); |
| } |
| |
| /* |
| * Iterates over the Channel map and performs the action given by |action| on |
| * each channel map item (const ChannelIterator::value_type). |
| * |channels_mutex_| is held to serialize access to the map; care must be |
| * taken to avoid recursively acquiring the mutex, for example, by calling |
| * Service::{GetChannel,SetChannel,CloseChannel,PushChannel}() or |
| * Message::SetChannel() in the action. |
| */ |
| template <class A> |
| void ForEachChannel(A action) const { |
| std::lock_guard<std::mutex> autolock(channels_mutex_); |
| ForEachChannelUnlocked(action); |
| } |
| |
| /* |
| * Return true if a channel with the given ID exists in the Channel map. |
| */ |
| bool HasChannelId(int channel_id) const { |
| std::lock_guard<std::mutex> autolock(channels_mutex_); |
| return channels_.find(channel_id) != channels_.end(); |
| } |
| |
| /* |
| * Subclasses of Service may override this method to provide a text string |
| * describing the state of the service. This method is called by |
| * HandleSystemMessage in response to the standard |
| * DUMP_STATE message. The string returned to the dump state client is |
| * truncated to |max_length| and reflects the maximum size the client can |
| * handle. |
| */ |
| virtual std::string DumpState(size_t max_length); |
| |
| /* |
| * Receives a message on this Service instance's endpoint and dispatches it. |
| * If the endpoint is in blocking mode this call blocks until a message is |
| * received, a signal is delivered to this thread, or the service is canceled. |
| * If the endpoint is in non-blocking mode and a message is not pending this |
| * call returns immediately with ETIMEDOUT. |
| */ |
| Status<void> ReceiveAndDispatch(); |
| |
| private: |
| friend class Message; |
| |
| Status<void> HandleSystemMessage(Message& message); |
| |
| Service(const Service&); |
| void operator=(const Service&) = delete; |
| |
| const std::string name_; |
| std::unique_ptr<Endpoint> endpoint_; |
| |
| /* |
| * Maintains references to active channels. |
| */ |
| mutable std::mutex channels_mutex_; |
| std::unordered_map<int, std::shared_ptr<Channel>> channels_; |
| }; |
| |
| /* |
| * Utility base class for services. This template handles allocation and |
| * initialization checks, reducing boiler plate code. |
| */ |
| template <typename TYPE> |
| class ServiceBase : public Service { |
| public: |
| /* |
| * Static service allocation method that check for initialization errors. |
| * If errors are encountered these automatically clean up and return |
| * nullptr. |
| */ |
| template <typename... Args> |
| static inline std::shared_ptr<TYPE> Create(Args&&... args) { |
| std::shared_ptr<TYPE> service(new TYPE(std::forward<Args>(args)...)); |
| if (service->IsInitialized()) |
| return service; |
| else |
| return nullptr; |
| } |
| |
| protected: |
| /* |
| * Shorthand for subclasses to refer to this base, particularly |
| * to call the base class constructor. |
| */ |
| typedef ServiceBase<TYPE> BASE; |
| |
| ServiceBase(const std::string& name, std::unique_ptr<Endpoint> endpoint) |
| : Service(name, std::move(endpoint)) {} |
| }; |
| |
| #ifndef STRINGIFY |
| #define STRINGIFY2(s) #s |
| #define STRINGIFY(s) STRINGIFY2(s) |
| #endif |
| |
| #define PDX_ERROR_PREFIX "[" __FILE__ ":" STRINGIFY(__LINE__) "]" |
| |
| /* |
| * Macros for replying to messages. Error handling can be tedious; |
| * these macros make things a little cleaner. |
| */ |
| #define CHECK_ERROR(cond, error, fmt, ...) \ |
| do { \ |
| if ((cond)) { \ |
| ALOGE(fmt, ##__VA_ARGS__); \ |
| goto error; \ |
| } \ |
| } while (0) |
| |
| #define REPLY_ERROR(message, error, error_label) \ |
| do { \ |
| auto __status = message.ReplyError(error); \ |
| CHECK_ERROR(!__status, error_label, \ |
| PDX_ERROR_PREFIX " Failed to reply to message because: %s\n", \ |
| __status.GetErrorMessage().c_str()); \ |
| goto error_label; \ |
| } while (0) |
| |
| #define REPLY_ERROR_RETURN(message, error, ...) \ |
| do { \ |
| auto __status = message.ReplyError(error); \ |
| ALOGE_IF(!__status, \ |
| PDX_ERROR_PREFIX " Failed to reply to message because: %s", \ |
| __status.GetErrorMessage().c_str()); \ |
| return __VA_ARGS__; \ |
| } while (0) |
| |
| #define REPLY_MESSAGE(message, message_return_code, error_label) \ |
| do { \ |
| auto __status = message.Reply(message_return_code); \ |
| CHECK_ERROR(!__status, error_label, \ |
| PDX_ERROR_PREFIX " Failed to reply to message because: %s\n", \ |
| __status.GetErrorMessage().c_str()); \ |
| goto error_label; \ |
| } while (0) |
| |
| #define REPLY_SUCCESS(message, message_return_code, error_label) \ |
| REPLY_MESSAGE(message, message_return_code, error_label) |
| |
| #define REPLY_MESSAGE_RETURN(message, message_return_code, ...) \ |
| do { \ |
| auto __status = message.Reply(message_return_code); \ |
| ALOGE_IF(!__status, \ |
| PDX_ERROR_PREFIX " Failed to reply to message because: %s", \ |
| __status.GetErrorMessage().c_str()); \ |
| return __VA_ARGS__; \ |
| } while (0) |
| |
| #define REPLY_SUCCESS_RETURN(message, message_return_code, ...) \ |
| REPLY_MESSAGE_RETURN(message, message_return_code, __VA_ARGS__) |
| |
| #define REPLY_FD(message, push_fd, error_label) \ |
| do { \ |
| auto __status = message.ReplyFileDescriptor(push_fd); \ |
| CHECK_ERROR(!__status, error_label, \ |
| PDX_ERROR_PREFIX " Failed to reply to message because: %s\n", \ |
| __status.GetErrorMessage().c_str()); \ |
| goto error_label; \ |
| } while (0) |
| |
| #define REPLY_FD_RETURN(message, push_fd, ...) \ |
| do { \ |
| auto __status = message.ReplyFileDescriptor(push_fd); \ |
| ALOGE_IF(__status < 0, \ |
| PDX_ERROR_PREFIX " Failed to reply to message because: %s", \ |
| __status.GetErrorMessage().c_str()); \ |
| return __VA_ARGS__; \ |
| } while (0) |
| |
| } // namespace pdx |
| } // namespace android |
| |
| #endif // ANDROID_PDX_SERVICE_H_ |