#ifndef ANDROID_PDX_RPC_REMOTE_METHOD_H_ #define ANDROID_PDX_RPC_REMOTE_METHOD_H_ #include #include #include #include #include #include #include #include #include namespace android { namespace pdx { namespace rpc { #ifdef __clang__ // Stand-in type to avoid Clang compiler bug. Clang currently has a bug where // performing parameter pack expansion for arguments with empty packs causes a // compiler crash. Provide a substitute void type and specializations/overloads // of CheckArgumentTypes and DispatchRemoteMethod to work around this problem. struct Void {}; // Evaluates to true if the method type is (Void), false otherwise. template using IsVoidMethod = typename std::integral_constant< bool, RemoteMethodType::Traits::Arity == 1 && std::is_same, Void>::value>; // Utility to determine if a method is of type (Void). template using EnableIfVoidMethod = typename std::enable_if::value>::type; // Utility to determine if a method is not of type (Void). template using EnableIfNotVoidMethod = typename std::enable_if::value>::type; #else // GCC works fine with void argument types, always enable the regular // implementation of DispatchRemoteMethod. using Void = void; template using EnableIfVoidMethod = void; template using EnableIfNotVoidMethod = void; #endif // Helper type trait to specialize InvokeRemoteMethods for return types that // can be obtained directly from Transaction::Send() without deserializing // reply payload. template struct IsDirectReturn : std::false_type {}; template <> struct IsDirectReturn : std::true_type {}; template <> struct IsDirectReturn : std::true_type {}; template <> struct IsDirectReturn : std::true_type {}; template <> struct IsDirectReturn : std::true_type {}; template using EnableIfDirectReturn = typename std::enable_if::value, Type>::type; template using EnableIfNotDirectReturn = typename std::enable_if::value, Type>::type; // Utility class to invoke a method with arguments packed in a tuple. template class UnpackArguments; // Utility class to invoke a method with arguments packed in a tuple. template class UnpackArguments { public: using ArgsTupleType = std::tuple::type...>; using MethodType = Return (Class::*)(Message&, Args...); UnpackArguments(Class& instance, MethodType method, Message& message, ArgsTupleType& parameters) : instance_(instance), method_(method), message_(message), parameters_(parameters) {} // Invokes method_ on intance_ with the packed arguments from parameters_. Return Invoke() { constexpr auto Arity = sizeof...(Args); return static_cast(InvokeHelper(MakeIndexSequence{})); } private: Class& instance_; MethodType method_; Message& message_; ArgsTupleType& parameters_; template Return InvokeHelper(IndexSequence) { return static_cast((instance_.*method_)( message_, std::get(std::forward(parameters_))...)); } UnpackArguments(const UnpackArguments&) = delete; void operator=(const UnpackArguments&) = delete; }; // Returns an error code from a remote method to the client. May be called // either during dispatch of the remote method handler or at a later time if the // message is moved for delayed response. inline void RemoteMethodError(Message& message, int error_code) { const auto status = message.ReplyError(error_code); ALOGE_IF(!status, "RemoteMethodError: Failed to reply to message: %s", status.GetErrorMessage().c_str()); } // Returns a value from a remote method to the client. The usual method to // return a value during dispatch of a remote method is to simply use a return // statement in the handler. If the message is moved however, these methods may // be used to return a value at a later time, outside of initial dispatch. // Overload for direct return types. template EnableIfDirectReturn RemoteMethodReturn( Message& message, const Return& return_value) { const auto status = message.Reply(return_value); ALOGE_IF(!status, "RemoteMethodReturn: Failed to reply to message: %s", status.GetErrorMessage().c_str()); } // Overload for non-direct return types. template EnableIfNotDirectReturn RemoteMethodReturn( Message& message, const Return& return_value) { using Signature = typename RemoteMethodType::template RewriteReturn; rpc::ServicePayload payload(message); MakeArgumentEncoder(&payload).EncodeReturn(return_value); auto ret = message.WriteAll(payload.Data(), payload.Size()); auto status = message.Reply(ret); ALOGE_IF(!status, "RemoteMethodReturn: Failed to reply to message: %s", status.GetErrorMessage().c_str()); } // Overload for Status return types. template void RemoteMethodReturn(Message& message, const Status& return_value) { if (return_value) RemoteMethodReturn(message, 0); else RemoteMethodError(message, return_value.error()); } // Overload for Status return types. This overload forwards the underlying // value or error within the Status. template void RemoteMethodReturn(Message& message, const Status& return_value) { if (return_value) RemoteMethodReturn(message, return_value.get()); else RemoteMethodError(message, return_value.error()); } // Dispatches a method by deserializing arguments from the given Message, with // compile-time interface check. Overload for void return types. template > void DispatchRemoteMethod(Class& instance, void (Class::*method)(Message&, Args...), Message& message, std::size_t max_capacity = InitialBufferCapacity) { using Signature = typename RemoteMethodType::template RewriteArgs; rpc::ServicePayload payload(message); payload.Resize(max_capacity); Status read_status = message.Read(payload.Data(), payload.Size()); if (!read_status) { RemoteMethodError(message, read_status.error()); return; } payload.Resize(read_status.get()); ErrorType error; auto decoder = MakeArgumentDecoder(&payload); auto arguments = decoder.DecodeArguments(&error); if (error) { RemoteMethodError(message, EIO); return; } UnpackArguments(instance, method, message, arguments) .Invoke(); // Return to the caller unless the message was moved. if (message) RemoteMethodReturn(message, 0); } // Dispatches a method by deserializing arguments from the given Message, with // compile-time interface signature check. Overload for generic return types. template > void DispatchRemoteMethod(Class& instance, Return (Class::*method)(Message&, Args...), Message& message, std::size_t max_capacity = InitialBufferCapacity) { using Signature = typename RemoteMethodType::template RewriteSignature; rpc::ServicePayload payload(message); payload.Resize(max_capacity); Status read_status = message.Read(payload.Data(), payload.Size()); if (!read_status) { RemoteMethodError(message, read_status.error()); return; } payload.Resize(read_status.get()); ErrorType error; auto decoder = MakeArgumentDecoder(&payload); auto arguments = decoder.DecodeArguments(&error); if (error) { RemoteMethodError(message, EIO); return; } auto return_value = UnpackArguments(instance, method, message, arguments) .Invoke(); // Return the value to the caller unless the message was moved. if (message) RemoteMethodReturn(message, return_value); } // Dispatches a method by deserializing arguments from the given Message, with // compile-time interface signature check. Overload for Status return types. template > void DispatchRemoteMethod(Class& instance, Status (Class::*method)(Message&, Args...), Message& message, std::size_t max_capacity = InitialBufferCapacity) { using Signature = typename RemoteMethodType::template RewriteSignature; using InvokeSignature = typename RemoteMethodType::template RewriteSignatureWrapReturn< Status, Return, Args...>; rpc::ServicePayload payload(message); payload.Resize(max_capacity); Status read_status = message.Read(payload.Data(), payload.Size()); if (!read_status) { RemoteMethodError(message, read_status.error()); return; } payload.Resize(read_status.get()); ErrorType error; auto decoder = MakeArgumentDecoder(&payload); auto arguments = decoder.DecodeArguments(&error); if (error) { RemoteMethodError(message, EIO); return; } auto return_value = UnpackArguments( instance, method, message, arguments) .Invoke(); // Return the value to the caller unless the message was moved. if (message) RemoteMethodReturn(message, return_value); } #ifdef __clang__ // Overloads to handle Void argument type without exploding clang. // Overload for void return type. template > void DispatchRemoteMethod(Class& instance, void (Class::*method)(Message&), Message& message) { (instance.*method)(message); // Return to the caller unless the message was moved. if (message) RemoteMethodReturn(message, 0); } // Overload for generic return type. template > void DispatchRemoteMethod(Class& instance, Return (Class::*method)(Message&), Message& message) { auto return_value = (instance.*method)(message); // Return the value to the caller unless the message was moved. if (message) RemoteMethodReturn(message, return_value); } // Overload for Status return type. template > void DispatchRemoteMethod(Class& instance, Status (Class::*method)(Message&), Message& message) { auto return_value = (instance.*method)(message); // Return the value to the caller unless the message was moved. if (message) RemoteMethodReturn(message, return_value); } #endif } // namespace rpc // Definitions for template methods declared in pdx/client.h. template struct CheckArgumentTypes; template struct CheckArgumentTypes { template static typename rpc::EnableIfDirectReturn> Invoke(Client& client, Args... args) { Transaction trans{client}; rpc::ClientPayload payload{trans}; rpc::MakeArgumentEncoder(&payload).EncodeArguments( std::forward(args)...); return trans.Send(Opcode, payload.Data(), payload.Size(), nullptr, 0); } template static typename rpc::EnableIfNotDirectReturn> Invoke( Client& client, Args... args) { Transaction trans{client}; rpc::ClientPayload send_payload{trans}; rpc::MakeArgumentEncoder(&send_payload) .EncodeArguments(std::forward(args)...); rpc::ClientPayload reply_payload{trans}; reply_payload.Resize(reply_payload.Capacity()); Status result; auto status = trans.Send(Opcode, send_payload.Data(), send_payload.Size(), reply_payload.Data(), reply_payload.Size()); if (!status) { result.SetError(status.error()); } else { R return_value; rpc::ErrorType error = rpc::MakeArgumentDecoder(&reply_payload) .DecodeReturn(&return_value); switch (error.error_code()) { case rpc::ErrorCode::NO_ERROR: result.SetValue(std::move(return_value)); break; // This error is returned when ArrayWrapper/StringWrapper is too // small to receive the payload. case rpc::ErrorCode::INSUFFICIENT_DESTINATION_SIZE: result.SetError(ENOBUFS); break; default: result.SetError(EIO); break; } } return result; } template static typename rpc::EnableIfDirectReturn> InvokeInPlace( Client& client, R* return_value, Args... args) { Transaction trans{client}; rpc::ClientPayload send_payload{trans}; rpc::MakeArgumentEncoder(&send_payload) .EncodeArguments(std::forward(args)...); Status result; auto status = trans.Send(Opcode, send_payload.Data(), send_payload.Size(), nullptr, 0); if (status) { *return_value = status.take(); result.SetValue(); } else { result.SetError(status.error()); } return result; } template static typename rpc::EnableIfNotDirectReturn> InvokeInPlace( Client& client, R* return_value, Args... args) { Transaction trans{client}; rpc::ClientPayload send_payload{trans}; rpc::MakeArgumentEncoder(&send_payload) .EncodeArguments(std::forward(args)...); rpc::ClientPayload reply_payload{trans}; reply_payload.Resize(reply_payload.Capacity()); auto result = trans.Send(Opcode, send_payload.Data(), send_payload.Size(), reply_payload.Data(), reply_payload.Size()); if (result) { rpc::ErrorType error = rpc::MakeArgumentDecoder(&reply_payload) .DecodeReturn(return_value); switch (error.error_code()) { case rpc::ErrorCode::NO_ERROR: result.SetValue(); break; // This error is returned when ArrayWrapper/StringWrapper is too // small to receive the payload. case rpc::ErrorCode::INSUFFICIENT_DESTINATION_SIZE: result.SetError(ENOBUFS); break; default: result.SetError(EIO); break; } } return result; } }; // Invokes the remote method with opcode and signature described by // |RemoteMethodType|. template Status Client::InvokeRemoteMethod( Args&&... args) { return CheckArgumentTypes< RemoteMethodType::Opcode, typename RemoteMethodType::template RewriteArgs>:: template Invoke( *this, std::forward(args)...); } template Status Client::InvokeRemoteMethodInPlace(Return* return_value, Args&&... args) { return CheckArgumentTypes< RemoteMethodType::Opcode, typename RemoteMethodType::template RewriteSignature>:: template InvokeInPlace(*this, return_value, std::forward(args)...); } } // namespace pdx } // namespace android #endif // ANDROID_PDX_RPC_REMOTE_METHOD_H_