diff options
Diffstat (limited to 'ipc/hal')
-rw-r--r-- | ipc/hal/DaemonRunnables.h | 950 | ||||
-rw-r--r-- | ipc/hal/DaemonSocket.cpp | 263 | ||||
-rw-r--r-- | ipc/hal/DaemonSocket.h | 63 | ||||
-rw-r--r-- | ipc/hal/DaemonSocketConnector.cpp | 244 | ||||
-rw-r--r-- | ipc/hal/DaemonSocketConnector.h | 61 | ||||
-rw-r--r-- | ipc/hal/DaemonSocketConsumer.cpp | 33 | ||||
-rw-r--r-- | ipc/hal/DaemonSocketConsumer.h | 68 | ||||
-rw-r--r-- | ipc/hal/DaemonSocketMessageHandlers.h | 40 | ||||
-rw-r--r-- | ipc/hal/DaemonSocketPDU.cpp | 202 | ||||
-rw-r--r-- | ipc/hal/DaemonSocketPDU.h | 94 | ||||
-rw-r--r-- | ipc/hal/DaemonSocketPDUHelpers.cpp | 335 | ||||
-rw-r--r-- | ipc/hal/DaemonSocketPDUHelpers.h | 1283 | ||||
-rw-r--r-- | ipc/hal/moz.build | 27 |
13 files changed, 3663 insertions, 0 deletions
diff --git a/ipc/hal/DaemonRunnables.h b/ipc/hal/DaemonRunnables.h new file mode 100644 index 000000000..e4a528a9b --- /dev/null +++ b/ipc/hal/DaemonRunnables.h @@ -0,0 +1,950 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_ipc_DaemonRunnables_h +#define mozilla_ipc_DaemonRunnables_h + +#include "mozilla/Unused.h" +#include "mozilla/UniquePtr.h" +#include "nsThreadUtils.h" + +namespace mozilla { +namespace ipc { + +namespace details { + +class DaemonRunnable : public Runnable +{ +protected: + DaemonRunnable() = default; + virtual ~DaemonRunnable() = default; + + template<typename Out, typename In> + static Out& ConvertArg(In& aArg) + { + return aArg; + } + + template<typename Out, typename In> + static Out ConvertArg(UniquePtr<In>& aArg) + { + return aArg.get(); + } +}; + +} // namespace detail + +// +// Result handling +// +// The classes of type |DaemonResultRunnable[0..3]| transfer a result +// handler from the I/O thread to the main thread for execution. Call +// the methods |Create| and |Dispatch| to create or create-and-dispatch +// a result runnable. +// +// You need to specify the called method. The |Create| and |Dispatch| +// methods of |DaemonResultRunnable[1..3]| receive an extra argument +// for initializing the result's arguments. During creation, the result +// runnable calls the supplied class's call operator with the result's +// argument. This is where initialization and conversion from backend- +// specific types is performed. +// + +template <typename Obj, typename Res> +class DaemonResultRunnable0 final : public details::DaemonRunnable +{ +public: + typedef DaemonResultRunnable0<Obj, Res> SelfType; + + template <typename InitOp> + static already_AddRefed<SelfType> + Create(Obj* aObj, Res (Obj::*aMethod)(), const InitOp& aInitOp) + { + RefPtr<SelfType> runnable(new SelfType(aObj, aMethod)); + if (NS_FAILED(runnable->Init(aInitOp))) { + return nullptr; + } + return runnable.forget(); + } + + template <typename InitOp> + static void + Dispatch(Obj* aObj, Res (Obj::*aMethod)(), const InitOp& aInitOp) + { + if (!aObj) { + return; // silently return if no result runnable has been given + } + RefPtr<SelfType> runnable = Create(aObj, aMethod, aInitOp); + if (!runnable) { + return; + } + Unused << NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(runnable))); + } + + NS_IMETHOD Run() override + { + ((*mObj).*mMethod)(); + return NS_OK; + } + +private: + DaemonResultRunnable0(Obj* aObj, Res (Obj::*aMethod)()) + : mObj(aObj) + , mMethod(aMethod) + { + MOZ_ASSERT(mObj); + MOZ_ASSERT(mMethod); + } + + template<typename InitOp> + nsresult Init(const InitOp& aInitOp) + { + return aInitOp(); + } + + RefPtr<Obj> mObj; + void (Obj::*mMethod)(); +}; + +template <typename Obj, typename Res, typename Tin1, typename Arg1> +class DaemonResultRunnable1 final : public details::DaemonRunnable +{ +public: + typedef DaemonResultRunnable1<Obj, Res, Tin1, Arg1> SelfType; + + template <typename InitOp> + static already_AddRefed<SelfType> + Create(Obj* aObj, Res (Obj::*aMethod)(Arg1), const InitOp& aInitOp) + { + RefPtr<SelfType> runnable(new SelfType(aObj, aMethod)); + if (NS_FAILED(runnable->Init(aInitOp))) { + return nullptr; + } + return runnable.forget(); + } + + template <typename InitOp> + static void + Dispatch(Obj* aObj, Res (Obj::*aMethod)(Arg1), const InitOp& aInitOp) + { + if (!aObj) { + return; // silently return if no result runnable has been given + } + RefPtr<SelfType> runnable = Create(aObj, aMethod, aInitOp); + if (!runnable) { + return; + } + Unused << NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(runnable))); + } + + NS_IMETHOD Run() override + { + ((*mObj).*mMethod)(ConvertArg<Arg1>(mArg1)); + return NS_OK; + } + +private: + DaemonResultRunnable1(Obj* aObj, Res (Obj::*aMethod)(Arg1)) + : mObj(aObj) + , mMethod(aMethod) + { + MOZ_ASSERT(mObj); + MOZ_ASSERT(mMethod); + } + + template<typename InitOp> + nsresult Init(const InitOp& aInitOp) + { + return aInitOp(mArg1); + } + + RefPtr<Obj> mObj; + Res (Obj::*mMethod)(Arg1); + Tin1 mArg1; +}; + +template <typename Obj, typename Res, + typename Tin1, typename Tin2, typename Tin3, + typename Arg1, typename Arg2, typename Arg3> +class DaemonResultRunnable3 final : public details::DaemonRunnable +{ +public: + typedef DaemonResultRunnable3<Obj, Res, + Tin1, Tin2, Tin3, + Arg1, Arg2, Arg3> SelfType; + + template<typename InitOp> + static already_AddRefed<SelfType> + Create(Obj* aObj, Res (Obj::*aMethod)(Arg1, Arg2, Arg3), + const InitOp& aInitOp) + { + RefPtr<SelfType> runnable(new SelfType(aObj, aMethod)); + if (NS_FAILED(runnable->Init(aInitOp))) { + return nullptr; + } + return runnable.forget(); + } + + template<typename InitOp> + static void + Dispatch(Obj* aObj, Res (Obj::*aMethod)(Arg1, Arg2, Arg3), + const InitOp& aInitOp) + { + if (!aObj) { + return; // silently return if no result runnable has been given + } + RefPtr<SelfType> runnable = Create(aObj, aMethod, aInitOp); + if (!runnable) { + return; + } + Unused << NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(runnable))); + } + + NS_IMETHOD Run() override + { + ((*mObj).*mMethod)(ConvertArg<Arg1>(mArg1), + ConvertArg<Arg2>(mArg2), + ConvertArg<Arg3>(mArg3)); + return NS_OK; + } + +private: + DaemonResultRunnable3(Obj* aObj, Res (Obj::*aMethod)(Arg1, Arg2, Arg3)) + : mObj(aObj) + , mMethod(aMethod) + { + MOZ_ASSERT(mObj); + MOZ_ASSERT(mMethod); + } + + template<typename InitOp> + nsresult + Init(const InitOp& aInitOp) + { + return aInitOp(mArg1, mArg2, mArg3); + } + + RefPtr<Obj> mObj; + Res (Obj::*mMethod)(Arg1, Arg2, Arg3); + Tin1 mArg1; + Tin2 mArg2; + Tin3 mArg3; +}; + +// +// Notification handling +// +// The classes of type |DaemonNotificationRunnable[0..9]| transfer a +// notification from the I/O thread to a notification handler on the +// main thread. Call the methods |Create| and |Dispatch| to create or +// create-and-dispatch a notification runnable. +// +// Like with result runnables, you need to specify the called method. +// And like with result runnables, the |Create| and |Dispatch| methods +// of |DaemonNotificationRunnable[1..9]| receive an extra argument +// for initializing the notification's arguments. During creation, the +// notification runnable calls the class's call operator with the +// notification's argument. This is where initialization and conversion +// from backend-specific types is performed. +// + +template <typename ObjectWrapper, typename Res> +class DaemonNotificationRunnable0 final : public details::DaemonRunnable +{ +public: + typedef typename ObjectWrapper::ObjectType ObjectType; + typedef DaemonNotificationRunnable0<ObjectWrapper, Res> SelfType; + + template<typename InitOp> + static already_AddRefed<SelfType> + Create(Res (ObjectType::*aMethod)(), const InitOp& aInitOp) + { + RefPtr<SelfType> runnable(new SelfType(aMethod)); + if (NS_FAILED(runnable->Init(aInitOp))) { + return nullptr; + } + return runnable.forget(); + } + + template<typename InitOp> + static void + Dispatch(Res (ObjectType::*aMethod)(), const InitOp& aInitOp) + { + RefPtr<SelfType> runnable = Create(aMethod, aInitOp); + if (!runnable) { + return; + } + Unused << NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(runnable))); + } + + NS_IMETHOD Run() override + { + MOZ_ASSERT(NS_IsMainThread()); + + ObjectType* obj = ObjectWrapper::GetInstance(); + if (!obj) { + NS_WARNING("Notification handler not initialized"); + } else { + ((*obj).*mMethod)(); + } + return NS_OK; + } + +private: + DaemonNotificationRunnable0(Res (ObjectType::*aMethod)()) + : mMethod(aMethod) + { + MOZ_ASSERT(mMethod); + } + + template<typename InitOp> + nsresult Init(const InitOp& aInitOp) + { + return aInitOp(); + } + + Res (ObjectType::*mMethod)(); +}; + +template <typename ObjectWrapper, typename Res, + typename Tin1, typename Arg1=Tin1> +class DaemonNotificationRunnable1 final : public details::DaemonRunnable +{ +public: + typedef typename ObjectWrapper::ObjectType ObjectType; + typedef DaemonNotificationRunnable1<ObjectWrapper, Res, + Tin1, Arg1> SelfType; + + template <typename InitOp> + static already_AddRefed<SelfType> + Create(Res (ObjectType::*aMethod)(Arg1), const InitOp& aInitOp) + { + RefPtr<SelfType> runnable(new SelfType(aMethod)); + if (NS_FAILED(runnable->Init(aInitOp))) { + return nullptr; + } + return runnable.forget(); + } + + template <typename InitOp> + static void + Dispatch(Res (ObjectType::*aMethod)(Arg1), const InitOp& aInitOp) + { + RefPtr<SelfType> runnable = Create(aMethod, aInitOp); + if (!runnable) { + return; + } + Unused << NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(runnable))); + } + + NS_IMETHOD Run() override + { + MOZ_ASSERT(NS_IsMainThread()); + + ObjectType* obj = ObjectWrapper::GetInstance(); + if (!obj) { + NS_WARNING("Notification handler not initialized"); + } else { + ((*obj).*mMethod)(ConvertArg<Arg1>(mArg1)); + } + return NS_OK; + } + +private: + DaemonNotificationRunnable1(Res (ObjectType::*aMethod)(Arg1)) + : mMethod(aMethod) + { + MOZ_ASSERT(mMethod); + } + + template<typename InitOp> + nsresult Init(const InitOp& aInitOp) + { + nsresult rv = aInitOp(mArg1); + if (NS_FAILED(rv)) { + return rv; + } + return NS_OK; + } + + Res (ObjectType::*mMethod)(Arg1); + Tin1 mArg1; +}; + +template <typename ObjectWrapper, typename Res, + typename Tin1, typename Tin2, + typename Arg1=Tin1, typename Arg2=Tin2> +class DaemonNotificationRunnable2 final : public details::DaemonRunnable +{ +public: + typedef typename ObjectWrapper::ObjectType ObjectType; + typedef DaemonNotificationRunnable2<ObjectWrapper, Res, + Tin1, Tin2, + Arg1, Arg2> SelfType; + + template <typename InitOp> + static already_AddRefed<SelfType> + Create(Res (ObjectType::*aMethod)(Arg1, Arg2), const InitOp& aInitOp) + { + RefPtr<SelfType> runnable(new SelfType(aMethod)); + if (NS_FAILED(runnable->Init(aInitOp))) { + return nullptr; + } + return runnable.forget(); + } + + template <typename InitOp> + static void + Dispatch(Res (ObjectType::*aMethod)(Arg1, Arg2), const InitOp& aInitOp) + { + RefPtr<SelfType> runnable = Create(aMethod, aInitOp); + if (!runnable) { + return; + } + Unused << NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(runnable))); + } + + NS_IMETHOD Run() override + { + MOZ_ASSERT(NS_IsMainThread()); + + ObjectType* obj = ObjectWrapper::GetInstance(); + if (!obj) { + NS_WARNING("Notification handler not initialized"); + } else { + ((*obj).*mMethod)(ConvertArg<Arg1>(mArg1), + ConvertArg<Arg2>(mArg2)); + } + return NS_OK; + } + +private: + DaemonNotificationRunnable2( + Res (ObjectType::*aMethod)(Arg1, Arg2)) + : mMethod(aMethod) + { + MOZ_ASSERT(mMethod); + } + + template<typename InitOp> + nsresult Init(const InitOp& aInitOp) + { + nsresult rv = aInitOp(mArg1, mArg2); + if (NS_FAILED(rv)) { + return rv; + } + return NS_OK; + } + + Res (ObjectType::*mMethod)(Arg1, Arg2); + Tin1 mArg1; + Tin2 mArg2; +}; + +template <typename ObjectWrapper, typename Res, + typename Tin1, typename Tin2, typename Tin3, + typename Arg1=Tin1, typename Arg2=Tin2, typename Arg3=Tin3> +class DaemonNotificationRunnable3 final : public details::DaemonRunnable +{ +public: + typedef typename ObjectWrapper::ObjectType ObjectType; + typedef DaemonNotificationRunnable3<ObjectWrapper, Res, + Tin1, Tin2, Tin3, + Arg1, Arg2, Arg3> SelfType; + + template <typename InitOp> + static already_AddRefed<SelfType> + Create(Res (ObjectType::*aMethod)(Arg1, Arg2, Arg3), const InitOp& aInitOp) + { + RefPtr<SelfType> runnable(new SelfType(aMethod)); + if (NS_FAILED(runnable->Init(aInitOp))) { + return nullptr; + } + return runnable.forget(); + } + + template <typename InitOp> + static void + Dispatch(Res (ObjectType::*aMethod)(Arg1, Arg2, Arg3), + const InitOp& aInitOp) + { + RefPtr<SelfType> runnable = Create(aMethod, aInitOp); + if (!runnable) { + return; + } + Unused << NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(runnable))); + } + + NS_IMETHOD Run() override + { + MOZ_ASSERT(NS_IsMainThread()); + + ObjectType* obj = ObjectWrapper::GetInstance(); + if (!obj) { + NS_WARNING("Notification handler not initialized"); + } else { + ((*obj).*mMethod)(ConvertArg<Arg1>(mArg1), + ConvertArg<Arg2>(mArg2), + ConvertArg<Arg3>(mArg3)); + } + return NS_OK; + } + +private: + DaemonNotificationRunnable3( + Res (ObjectType::*aMethod)(Arg1, Arg2, Arg3)) + : mMethod(aMethod) + { + MOZ_ASSERT(mMethod); + } + + template<typename InitOp> + nsresult Init(const InitOp& aInitOp) + { + nsresult rv = aInitOp(mArg1, mArg2, mArg3); + if (NS_FAILED(rv)) { + return rv; + } + return NS_OK; + } + + Res (ObjectType::*mMethod)(Arg1, Arg2, Arg3); + Tin1 mArg1; + Tin2 mArg2; + Tin3 mArg3; +}; + +template <typename ObjectWrapper, typename Res, + typename Tin1, typename Tin2, typename Tin3, typename Tin4, + typename Arg1=Tin1, typename Arg2=Tin2, + typename Arg3=Tin3, typename Arg4=Tin4> +class DaemonNotificationRunnable4 final : public details::DaemonRunnable +{ +public: + typedef typename ObjectWrapper::ObjectType ObjectType; + typedef DaemonNotificationRunnable4<ObjectWrapper, Res, + Tin1, Tin2, Tin3, Tin4, Arg1, Arg2, Arg3, Arg4> SelfType; + + template <typename InitOp> + static already_AddRefed<SelfType> Create( + Res (ObjectType::*aMethod)(Arg1, Arg2, Arg3, Arg4), + const InitOp& aInitOp) + { + RefPtr<SelfType> runnable(new SelfType(aMethod)); + if (NS_FAILED(runnable->Init(aInitOp))) { + return nullptr; + } + return runnable.forget(); + } + + template <typename InitOp> + static void + Dispatch(Res (ObjectType::*aMethod)(Arg1, Arg2, Arg3, Arg4), + const InitOp& aInitOp) + { + RefPtr<SelfType> runnable = Create(aMethod, aInitOp); + if (!runnable) { + return; + } + Unused << NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(runnable))); + } + + NS_IMETHOD Run() override + { + MOZ_ASSERT(NS_IsMainThread()); + + ObjectType* obj = ObjectWrapper::GetInstance(); + if (!obj) { + NS_WARNING("Notification handler not initialized"); + } else { + ((*obj).*mMethod)(ConvertArg<Arg1>(mArg1), + ConvertArg<Arg2>(mArg2), + ConvertArg<Arg3>(mArg3), + ConvertArg<Arg4>(mArg4)); + } + return NS_OK; + } + +private: + DaemonNotificationRunnable4( + Res (ObjectType::*aMethod)(Arg1, Arg2, Arg3, Arg4)) + : mMethod(aMethod) + { + MOZ_ASSERT(mMethod); + } + + template<typename InitOp> + nsresult Init(const InitOp& aInitOp) + { + nsresult rv = aInitOp(mArg1, mArg2, mArg3, mArg4); + if (NS_FAILED(rv)) { + return rv; + } + return NS_OK; + } + + Res (ObjectType::*mMethod)(Arg1, Arg2, Arg3, Arg4); + Tin1 mArg1; + Tin2 mArg2; + Tin3 mArg3; + Tin4 mArg4; +}; + +template <typename ObjectWrapper, typename Res, + typename Tin1, typename Tin2, typename Tin3, + typename Tin4, typename Tin5, + typename Arg1=Tin1, typename Arg2=Tin2, typename Arg3=Tin3, + typename Arg4=Tin4, typename Arg5=Tin5> +class DaemonNotificationRunnable5 final : public details::DaemonRunnable +{ +public: + typedef typename ObjectWrapper::ObjectType ObjectType; + typedef DaemonNotificationRunnable5<ObjectWrapper, Res, + Tin1, Tin2, Tin3, Tin4, Tin5, Arg1, Arg2, Arg3, Arg4, Arg5> SelfType; + + template <typename InitOp> + static already_AddRefed<SelfType> Create( + Res (ObjectType::*aMethod)(Arg1, Arg2, Arg3, Arg4, Arg5), + const InitOp& aInitOp) + { + RefPtr<SelfType> runnable(new SelfType(aMethod)); + if (NS_FAILED(runnable->Init(aInitOp))) { + return nullptr; + } + return runnable.forget(); + } + + template <typename InitOp> + static void + Dispatch(Res (ObjectType::*aMethod)(Arg1, Arg2, Arg3, Arg4, Arg5), + const InitOp& aInitOp) + { + RefPtr<SelfType> runnable = Create(aMethod, aInitOp); + if (!runnable) { + return; + } + Unused << NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(runnable))); + } + + NS_IMETHOD Run() override + { + MOZ_ASSERT(NS_IsMainThread()); + + ObjectType* obj = ObjectWrapper::GetInstance(); + if (!obj) { + NS_WARNING("Notification handler not initialized"); + } else { + ((*obj).*mMethod)(ConvertArg<Arg1>(mArg1), + ConvertArg<Arg2>(mArg2), + ConvertArg<Arg3>(mArg3), + ConvertArg<Arg4>(mArg4), + ConvertArg<Arg5>(mArg5)); + } + return NS_OK; + } + +private: + DaemonNotificationRunnable5( + Res (ObjectType::*aMethod)(Arg1, Arg2, Arg3, Arg4, Arg5)) + : mMethod(aMethod) + { + MOZ_ASSERT(mMethod); + } + + template<typename InitOp> + nsresult Init(const InitOp& aInitOp) + { + nsresult rv = aInitOp(mArg1, mArg2, mArg3, mArg4, mArg5); + if (NS_FAILED(rv)) { + return rv; + } + return NS_OK; + } + + Res (ObjectType::*mMethod)(Arg1, Arg2, Arg3, Arg4, Arg5); + Tin1 mArg1; + Tin2 mArg2; + Tin3 mArg3; + Tin4 mArg4; + Tin5 mArg5; +}; + +template <typename ObjectWrapper, typename Res, + typename Tin1, typename Tin2, typename Tin3, + typename Tin4, typename Tin5, typename Tin6, + typename Arg1=Tin1, typename Arg2=Tin2, typename Arg3=Tin3, + typename Arg4=Tin4, typename Arg5=Tin5, typename Arg6=Tin6> +class DaemonNotificationRunnable6 final : public details::DaemonRunnable +{ +public: + typedef typename ObjectWrapper::ObjectType ObjectType; + typedef DaemonNotificationRunnable6<ObjectWrapper, Res, + Tin1, Tin2, Tin3, Tin4, Tin5, Tin6, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6> + SelfType; + + template <typename InitOp> + static already_AddRefed<SelfType> Create( + Res (ObjectType::*aMethod)(Arg1, Arg2, Arg3, Arg4, Arg5, Arg6), + const InitOp& aInitOp) + { + RefPtr<SelfType> runnable(new SelfType(aMethod)); + if (NS_FAILED(runnable->Init(aInitOp))) { + return nullptr; + } + return runnable.forget(); + } + + template <typename InitOp> + static void + Dispatch(Res (ObjectType::*aMethod)(Arg1, Arg2, Arg3, Arg4, Arg5, Arg6), + const InitOp& aInitOp) + { + RefPtr<SelfType> runnable = Create(aMethod, aInitOp); + if (!runnable) { + return; + } + Unused << NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(runnable))); + } + + NS_IMETHOD Run() override + { + MOZ_ASSERT(NS_IsMainThread()); + + ObjectType* obj = ObjectWrapper::GetInstance(); + if (!obj) { + NS_WARNING("Notification handler not initialized"); + } else { + ((*obj).*mMethod)(ConvertArg<Arg1>(mArg1), + ConvertArg<Arg2>(mArg2), + ConvertArg<Arg3>(mArg3), + ConvertArg<Arg4>(mArg4), + ConvertArg<Arg5>(mArg5), + ConvertArg<Arg6>(mArg6)); + } + return NS_OK; + } + +private: + DaemonNotificationRunnable6( + Res (ObjectType::*aMethod)(Arg1, Arg2, Arg3, Arg4, Arg5, Arg6)) + : mMethod(aMethod) + { + MOZ_ASSERT(mMethod); + } + + template<typename InitOp> + nsresult Init(const InitOp& aInitOp) + { + nsresult rv = aInitOp(mArg1, mArg2, mArg3, mArg4, mArg5, mArg6); + if (NS_FAILED(rv)) { + return rv; + } + return NS_OK; + } + + Res (ObjectType::*mMethod)(Arg1, Arg2, Arg3, Arg4, Arg5, Arg6); + Tin1 mArg1; + Tin2 mArg2; + Tin3 mArg3; + Tin4 mArg4; + Tin5 mArg5; + Tin6 mArg6; +}; + +template <typename ObjectWrapper, typename Res, + typename Tin1, typename Tin2, typename Tin3, + typename Tin4, typename Tin5, typename Tin6, + typename Tin7, typename Tin8, + typename Arg1=Tin1, typename Arg2=Tin2, typename Arg3=Tin3, + typename Arg4=Tin4, typename Arg5=Tin5, typename Arg6=Tin6, + typename Arg7=Tin7, typename Arg8=Tin8> +class DaemonNotificationRunnable8 final : public details::DaemonRunnable +{ +public: + typedef typename ObjectWrapper::ObjectType ObjectType; + typedef DaemonNotificationRunnable8<ObjectWrapper, Res, + Tin1, Tin2, Tin3, Tin4, Tin5, Tin6, Tin7, Tin8, + Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7, Arg8> SelfType; + + template <typename InitOp> + static already_AddRefed<SelfType> Create( + Res (ObjectType::*aMethod)( + Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7, Arg8), + const InitOp& aInitOp) + { + RefPtr<SelfType> runnable(new SelfType(aMethod)); + if (NS_FAILED(runnable->Init(aInitOp))) { + return nullptr; + } + return runnable.forget(); + } + + template <typename InitOp> + static void + Dispatch( + Res (ObjectType::*aMethod)( + Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7, Arg8), + const InitOp& aInitOp) + { + RefPtr<SelfType> runnable = Create(aMethod, aInitOp); + if (!runnable) { + return; + } + Unused << NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(runnable))); + } + + NS_IMETHOD Run() override + { + MOZ_ASSERT(NS_IsMainThread()); + + ObjectType* obj = ObjectWrapper::GetInstance(); + if (!obj) { + NS_WARNING("Notification handler not initialized"); + } else { + ((*obj).*mMethod)(ConvertArg<Arg1>(mArg1), + ConvertArg<Arg2>(mArg2), + ConvertArg<Arg3>(mArg3), + ConvertArg<Arg4>(mArg4), + ConvertArg<Arg5>(mArg5), + ConvertArg<Arg6>(mArg6), + ConvertArg<Arg7>(mArg7), + ConvertArg<Arg8>(mArg8)); + } + return NS_OK; + } + +private: + DaemonNotificationRunnable8( + Res (ObjectType::*aMethod)( + Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7, Arg8)) + : mMethod(aMethod) + { + MOZ_ASSERT(mMethod); + } + + template<typename InitOp> + nsresult Init(const InitOp& aInitOp) + { + nsresult rv = aInitOp(mArg1, mArg2, mArg3, mArg4, + mArg5, mArg6, mArg7, mArg8); + if (NS_FAILED(rv)) { + return rv; + } + return NS_OK; + } + + Res (ObjectType::*mMethod)( + Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7, Arg8); + Tin1 mArg1; + Tin2 mArg2; + Tin3 mArg3; + Tin4 mArg4; + Tin5 mArg5; + Tin6 mArg6; + Tin7 mArg7; + Tin8 mArg8; +}; + +template <typename ObjectWrapper, typename Res, + typename Tin1, typename Tin2, typename Tin3, + typename Tin4, typename Tin5, typename Tin6, + typename Tin7, typename Tin8, typename Tin9, + typename Arg1=Tin1, typename Arg2=Tin2, typename Arg3=Tin3, + typename Arg4=Tin4, typename Arg5=Tin5, typename Arg6=Tin6, + typename Arg7=Tin7, typename Arg8=Tin8, typename Arg9=Tin9> +class DaemonNotificationRunnable9 final : public details::DaemonRunnable +{ +public: + typedef typename ObjectWrapper::ObjectType ObjectType; + typedef DaemonNotificationRunnable9<ObjectWrapper, Res, + Tin1, Tin2, Tin3, Tin4, Tin5, Tin6, Tin7, Tin8, Tin9, + Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7, Arg8, Arg9> SelfType; + + template <typename InitOp> + static already_AddRefed<SelfType> Create( + Res (ObjectType::*aMethod)( + Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7, Arg8, Arg9), + const InitOp& aInitOp) + { + RefPtr<SelfType> runnable(new SelfType(aMethod)); + if (NS_FAILED(runnable->Init(aInitOp))) { + return nullptr; + } + return runnable.forget(); + } + + template <typename InitOp> + static void + Dispatch( + Res (ObjectType::*aMethod)( + Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7, Arg8, Arg9), + const InitOp& aInitOp) + { + RefPtr<SelfType> runnable = Create(aMethod, aInitOp); + if (!runnable) { + return; + } + Unused << NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(runnable))); + } + + NS_IMETHOD Run() override + { + MOZ_ASSERT(NS_IsMainThread()); + + ObjectType* obj = ObjectWrapper::GetInstance(); + if (!obj) { + NS_WARNING("Notification handler not initialized"); + } else { + ((*obj).*mMethod)(ConvertArg<Arg1>(mArg1), + ConvertArg<Arg2>(mArg2), + ConvertArg<Arg3>(mArg3), + ConvertArg<Arg4>(mArg4), + ConvertArg<Arg5>(mArg5), + ConvertArg<Arg6>(mArg6), + ConvertArg<Arg7>(mArg7), + ConvertArg<Arg8>(mArg8), + ConvertArg<Arg9>(mArg9)); + } + return NS_OK; + } + +private: + DaemonNotificationRunnable9( + Res (ObjectType::*aMethod)( + Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7, Arg8, Arg9)) + : mMethod(aMethod) + { + MOZ_ASSERT(mMethod); + } + + template<typename InitOp> + nsresult Init(const InitOp& aInitOp) + { + nsresult rv = aInitOp(mArg1, mArg2, mArg3, mArg4, + mArg5, mArg6, mArg7, mArg8, mArg9); + if (NS_FAILED(rv)) { + return rv; + } + return NS_OK; + } + + Res (ObjectType::*mMethod)( + Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7, Arg8, Arg9); + Tin1 mArg1; + Tin2 mArg2; + Tin3 mArg3; + Tin4 mArg4; + Tin5 mArg5; + Tin6 mArg6; + Tin7 mArg7; + Tin8 mArg8; + Tin9 mArg9; +}; + +} +} + +#endif // mozilla_ipc_DaemonRunnables_h diff --git a/ipc/hal/DaemonSocket.cpp b/ipc/hal/DaemonSocket.cpp new file mode 100644 index 000000000..94c36b955 --- /dev/null +++ b/ipc/hal/DaemonSocket.cpp @@ -0,0 +1,263 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "DaemonSocket.h" +#include "mozilla/ipc/DaemonSocketConsumer.h" +#include "mozilla/ipc/DaemonSocketPDU.h" +#include "mozilla/UniquePtr.h" +#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, MOZ_COUNT_DTOR + +#ifdef CHROMIUM_LOG +#undef CHROMIUM_LOG +#endif + +#if defined(MOZ_WIDGET_GONK) +#include <android/log.h> +#define CHROMIUM_LOG(args...) __android_log_print(ANDROID_LOG_INFO, "I/O", args); +#else +#include <stdio.h> +#define IODEBUG true +#define CHROMIUM_LOG(args...) if (IODEBUG) printf(args); +#endif + +namespace mozilla { +namespace ipc { + +// +// DaemonSocketIO +// + +class DaemonSocketIO final : public ConnectionOrientedSocketIO +{ +public: + DaemonSocketIO(MessageLoop* aConsumerLoop, + MessageLoop* aIOLoop, + int aFd, ConnectionStatus aConnectionStatus, + UnixSocketConnector* aConnector, + DaemonSocket* aConnection, + DaemonSocketIOConsumer* aConsumer); + + ~DaemonSocketIO(); + + // Methods for |DataSocketIO| + // + + nsresult QueryReceiveBuffer(UnixSocketIOBuffer** aBuffer) override; + void ConsumeBuffer() override; + void DiscardBuffer() override; + + // Methods for |SocketIOBase| + // + + SocketBase* GetSocketBase() override; + + bool IsShutdownOnConsumerThread() const override; + bool IsShutdownOnIOThread() const override; + + void ShutdownOnConsumerThread() override; + void ShutdownOnIOThread() override; + +private: + DaemonSocket* mConnection; + DaemonSocketIOConsumer* mConsumer; + UniquePtr<DaemonSocketPDU> mPDU; + bool mShuttingDownOnIOThread; +}; + +DaemonSocketIO::DaemonSocketIO( + MessageLoop* aConsumerLoop, + MessageLoop* aIOLoop, + int aFd, + ConnectionStatus aConnectionStatus, + UnixSocketConnector* aConnector, + DaemonSocket* aConnection, + DaemonSocketIOConsumer* aConsumer) + : ConnectionOrientedSocketIO(aConsumerLoop, + aIOLoop, + aFd, + aConnectionStatus, + aConnector) + , mConnection(aConnection) + , mConsumer(aConsumer) + , mShuttingDownOnIOThread(false) +{ + MOZ_ASSERT(mConnection); + MOZ_ASSERT(mConsumer); + + MOZ_COUNT_CTOR_INHERITED(DaemonSocketIO, ConnectionOrientedSocketIO); +} + +DaemonSocketIO::~DaemonSocketIO() +{ + MOZ_COUNT_DTOR_INHERITED(DaemonSocketIO, ConnectionOrientedSocketIO); +} + +// |DataSocketIO| + +nsresult +DaemonSocketIO::QueryReceiveBuffer(UnixSocketIOBuffer** aBuffer) +{ + MOZ_ASSERT(aBuffer); + + if (!mPDU) { + /* There's only one PDU for receiving. We reuse it every time. */ + mPDU = MakeUnique<DaemonSocketPDU>(DaemonSocketPDU::PDU_MAX_PAYLOAD_LENGTH); + } + *aBuffer = mPDU.get(); + + return NS_OK; +} + +void +DaemonSocketIO::ConsumeBuffer() +{ + MOZ_ASSERT(mConsumer); + + mConsumer->Handle(*mPDU); +} + +void +DaemonSocketIO::DiscardBuffer() +{ + // Nothing to do. +} + +// |SocketIOBase| + +SocketBase* +DaemonSocketIO::GetSocketBase() +{ + return mConnection; +} + +bool +DaemonSocketIO::IsShutdownOnConsumerThread() const +{ + MOZ_ASSERT(IsConsumerThread()); + + return mConnection == nullptr; +} + +bool +DaemonSocketIO::IsShutdownOnIOThread() const +{ + return mShuttingDownOnIOThread; +} + +void +DaemonSocketIO::ShutdownOnConsumerThread() +{ + MOZ_ASSERT(IsConsumerThread()); + MOZ_ASSERT(!IsShutdownOnConsumerThread()); + + mConnection = nullptr; +} + +void +DaemonSocketIO::ShutdownOnIOThread() +{ + MOZ_ASSERT(!IsConsumerThread()); + MOZ_ASSERT(!mShuttingDownOnIOThread); + + Close(); // will also remove fd from I/O loop + mShuttingDownOnIOThread = true; +} + +// +// DaemonSocket +// + +DaemonSocket::DaemonSocket( + DaemonSocketIOConsumer* aIOConsumer, + DaemonSocketConsumer* aConsumer, + int aIndex) + : mIO(nullptr) + , mIOConsumer(aIOConsumer) + , mConsumer(aConsumer) + , mIndex(aIndex) +{ + MOZ_ASSERT(mConsumer); + + MOZ_COUNT_CTOR_INHERITED(DaemonSocket, ConnectionOrientedSocket); +} + +DaemonSocket::~DaemonSocket() +{ + MOZ_COUNT_DTOR_INHERITED(DaemonSocket, ConnectionOrientedSocket); +} + +// |ConnectionOrientedSocket| + +nsresult +DaemonSocket::PrepareAccept(UnixSocketConnector* aConnector, + MessageLoop* aConsumerLoop, + MessageLoop* aIOLoop, + ConnectionOrientedSocketIO*& aIO) +{ + MOZ_ASSERT(!mIO); + + SetConnectionStatus(SOCKET_CONNECTING); + + mIO = new DaemonSocketIO( + aConsumerLoop, aIOLoop, -1, UnixSocketWatcher::SOCKET_IS_CONNECTING, + aConnector, this, mIOConsumer); + aIO = mIO; + + return NS_OK; +} + +// |DataSocket| + +void +DaemonSocket::SendSocketData(UnixSocketIOBuffer* aBuffer) +{ + MOZ_ASSERT(mIO); + MOZ_ASSERT(mIO->IsConsumerThread()); + + mIO->GetIOLoop()->PostTask( + MakeAndAddRef<SocketIOSendTask<DaemonSocketIO, UnixSocketIOBuffer>>( + mIO, aBuffer)); +} + +// |SocketBase| + +void +DaemonSocket::Close() +{ + if (!mIO) { + CHROMIUM_LOG("HAL daemon already disconnected!"); + return; + } + + MOZ_ASSERT(mIO->IsConsumerThread()); + + mIO->ShutdownOnConsumerThread(); + mIO->GetIOLoop()->PostTask(MakeAndAddRef<SocketIOShutdownTask>(mIO)); + mIO = nullptr; + + NotifyDisconnect(); +} + +void +DaemonSocket::OnConnectSuccess() +{ + mConsumer->OnConnectSuccess(mIndex); +} + +void +DaemonSocket::OnConnectError() +{ + mConsumer->OnConnectError(mIndex); +} + +void +DaemonSocket::OnDisconnect() +{ + mConsumer->OnDisconnect(mIndex); +} + +} +} diff --git a/ipc/hal/DaemonSocket.h b/ipc/hal/DaemonSocket.h new file mode 100644 index 000000000..63d3a2e5c --- /dev/null +++ b/ipc/hal/DaemonSocket.h @@ -0,0 +1,63 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_ipc_DaemonSocket_h +#define mozilla_ipc_DaemonSocket_h + +#include "mozilla/ipc/ConnectionOrientedSocket.h" + +namespace mozilla { +namespace ipc { + +class DaemonSocketConsumer; +class DaemonSocketIO; +class DaemonSocketIOConsumer; + +/** + * |DaemonSocket| represents the socket to connect to the HAL daemon. It + * offers connection establishment and sending PDUs. PDU receiving is + * performed by |DaemonSocketIOConsumer|. + */ +class DaemonSocket : public ConnectionOrientedSocket +{ +public: + DaemonSocket(DaemonSocketIOConsumer* aIOConsumer, + DaemonSocketConsumer* aConsumer, + int aIndex); + virtual ~DaemonSocket(); + + // Methods for |ConnectionOrientedSocket| + // + + nsresult PrepareAccept(UnixSocketConnector* aConnector, + MessageLoop* aConsumerLoop, + MessageLoop* aIOLoop, + ConnectionOrientedSocketIO*& aIO) override; + + // Methods for |DataSocket| + // + + void SendSocketData(UnixSocketIOBuffer* aBuffer) override; + + // Methods for |SocketBase| + // + + void Close() override; + void OnConnectSuccess() override; + void OnConnectError() override; + void OnDisconnect() override; + +private: + DaemonSocketIO* mIO; + DaemonSocketIOConsumer* mIOConsumer; + DaemonSocketConsumer* mConsumer; + int mIndex; +}; + +} +} + +#endif diff --git a/ipc/hal/DaemonSocketConnector.cpp b/ipc/hal/DaemonSocketConnector.cpp new file mode 100644 index 000000000..97106bdd0 --- /dev/null +++ b/ipc/hal/DaemonSocketConnector.cpp @@ -0,0 +1,244 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "DaemonSocketConnector.h" +#include <fcntl.h> +#include <limits.h> +#include <stddef.h> +#include <string.h> +#include <sys/un.h> +#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, MOZ_COUNT_DTOR +#include "prrng.h" + +#ifdef CHROMIUM_LOG +#undef CHROMIUM_LOG +#endif + +#if defined(MOZ_WIDGET_GONK) +#include <android/log.h> +#define CHROMIUM_LOG(args...) __android_log_print(ANDROID_LOG_INFO, "I/O", args); +#else +#include <stdio.h> +#define IODEBUG true +#define CHROMIUM_LOG(args...) if (IODEBUG) printf(args); +#endif + +namespace mozilla { +namespace ipc { + +nsresult +DaemonSocketConnector::CreateRandomAddressString( + const nsACString& aPrefix, unsigned long aPostfixLength, + nsACString& aAddress) +{ + static const char sHexChar[16] = { + [0x0] = '0', [0x1] = '1', [0x2] = '2', [0x3] = '3', + [0x4] = '4', [0x5] = '5', [0x6] = '6', [0x7] = '7', + [0x8] = '8', [0x9] = '9', [0xa] = 'a', [0xb] = 'b', + [0xc] = 'c', [0xd] = 'd', [0xe] = 'e', [0xf] = 'f' + }; + + unsigned short seed[3]; + + if (NS_WARN_IF(!PR_GetRandomNoise(seed, sizeof(seed)))) { + return NS_ERROR_NOT_IMPLEMENTED; + } + + aAddress = aPrefix; + aAddress.Append('-'); + + while (aPostfixLength) { + // Android doesn't provide rand_r, so we use nrand48 here, + // even though it's deprecated. + long value = nrand48(seed); + + size_t bits = sizeof(value) * CHAR_BIT; + + while ((bits > 4) && aPostfixLength) { + aAddress.Append(sHexChar[value&0xf]); + bits -= 4; + value >>= 4; + --aPostfixLength; + } + } + + return NS_OK; +} + +DaemonSocketConnector::DaemonSocketConnector(const nsACString& aSocketName) + : mSocketName(aSocketName) +{ + MOZ_COUNT_CTOR_INHERITED(DaemonSocketConnector, UnixSocketConnector); +} + +DaemonSocketConnector::~DaemonSocketConnector() +{ + MOZ_COUNT_CTOR_INHERITED(DaemonSocketConnector, UnixSocketConnector); +} + +nsresult +DaemonSocketConnector::CreateSocket(int& aFd) const +{ + aFd = socket(AF_UNIX, SOCK_SEQPACKET, 0); + if (aFd < 0) { + CHROMIUM_LOG("Could not open daemon socket!"); + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +nsresult +DaemonSocketConnector::SetSocketFlags(int aFd) const +{ + static const int sReuseAddress = 1; + + // Set close-on-exec bit. + int flags = TEMP_FAILURE_RETRY(fcntl(aFd, F_GETFD)); + if (flags < 0) { + return NS_ERROR_FAILURE; + } + flags |= FD_CLOEXEC; + int res = TEMP_FAILURE_RETRY(fcntl(aFd, F_SETFD, flags)); + if (res < 0) { + return NS_ERROR_FAILURE; + } + + // Set non-blocking status flag. + flags = TEMP_FAILURE_RETRY(fcntl(aFd, F_GETFL)); + if (flags < 0) { + return NS_ERROR_FAILURE; + } + flags |= O_NONBLOCK; + res = TEMP_FAILURE_RETRY(fcntl(aFd, F_SETFL, flags)); + if (res < 0) { + return NS_ERROR_FAILURE; + } + + // Set socket addr to be reused even if kernel is still waiting to close. + res = setsockopt(aFd, SOL_SOCKET, SO_REUSEADDR, &sReuseAddress, + sizeof(sReuseAddress)); + if (res < 0) { + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +nsresult +DaemonSocketConnector::CreateAddress(struct sockaddr& aAddress, + socklen_t& aAddressLength) const +{ + static const size_t sNameOffset = 1; + + struct sockaddr_un* address = + reinterpret_cast<struct sockaddr_un*>(&aAddress); + + size_t namesiz = mSocketName.Length() + 1; // include trailing '\0' + + if (NS_WARN_IF((sNameOffset + namesiz) > sizeof(address->sun_path))) { + return NS_ERROR_FAILURE; + } + + address->sun_family = AF_UNIX; + memset(address->sun_path, '\0', sNameOffset); // abstract socket + memcpy(address->sun_path + sNameOffset, mSocketName.get(), namesiz); + + aAddressLength = + offsetof(struct sockaddr_un, sun_path) + sNameOffset + namesiz; + + return NS_OK; +} + +// |UnixSocketConnector| + +nsresult +DaemonSocketConnector::ConvertAddressToString( + const struct sockaddr& aAddress, socklen_t aAddressLength, + nsACString& aAddressString) +{ + MOZ_ASSERT(aAddress.sa_family == AF_UNIX); + + const struct sockaddr_un* un = + reinterpret_cast<const struct sockaddr_un*>(&aAddress); + + size_t len = aAddressLength - offsetof(struct sockaddr_un, sun_path); + + aAddressString.Assign(un->sun_path, len); + + return NS_OK; +} + +nsresult +DaemonSocketConnector::CreateListenSocket(struct sockaddr* aAddress, + socklen_t* aAddressLength, + int& aListenFd) +{ + ScopedClose fd; + + nsresult rv = CreateSocket(fd.rwget()); + if (NS_FAILED(rv)) { + return rv; + } + rv = SetSocketFlags(fd); + if (NS_FAILED(rv)) { + return rv; + } + if (aAddress && aAddressLength) { + rv = CreateAddress(*aAddress, *aAddressLength); + if (NS_FAILED(rv)) { + return rv; + } + } + + aListenFd = fd.forget(); + + return NS_OK; +} + +nsresult +DaemonSocketConnector::AcceptStreamSocket(int aListenFd, + struct sockaddr* aAddress, + socklen_t* aAddressLength, + int& aStreamFd) +{ + ScopedClose fd( + TEMP_FAILURE_RETRY(accept(aListenFd, aAddress, aAddressLength))); + if (fd < 0) { + CHROMIUM_LOG("Cannot accept file descriptor!"); + return NS_ERROR_FAILURE; + } + nsresult rv = SetSocketFlags(fd); + if (NS_FAILED(rv)) { + return rv; + } + + aStreamFd = fd.forget(); + + return NS_OK; +} + +nsresult +DaemonSocketConnector::CreateStreamSocket(struct sockaddr* aAddress, + socklen_t* aAddressLength, + int& aStreamFd) +{ + MOZ_CRASH("|DaemonSocketConnector| does not support " + "creating stream sockets."); + return NS_ERROR_ABORT; +} + +nsresult +DaemonSocketConnector::Duplicate(UnixSocketConnector*& aConnector) +{ + aConnector = new DaemonSocketConnector(*this); + + return NS_OK; +} + +} // namespace ipc +} // namespace mozilla diff --git a/ipc/hal/DaemonSocketConnector.h b/ipc/hal/DaemonSocketConnector.h new file mode 100644 index 000000000..b7f2b8045 --- /dev/null +++ b/ipc/hal/DaemonSocketConnector.h @@ -0,0 +1,61 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_ipc_DaemonSocketConnector_h +#define mozilla_ipc_DaemonSocketConnector_h + +#include "mozilla/ipc/UnixSocketConnector.h" +#include "nsString.h" + +namespace mozilla { +namespace ipc { + +class DaemonSocketConnector final : public UnixSocketConnector +{ +public: + static nsresult CreateRandomAddressString(const nsACString& aPrefix, + unsigned long aPostfixLength, + nsACString& aAddress); + + DaemonSocketConnector(const nsACString& aSocketName); + ~DaemonSocketConnector(); + + // Methods for |UnixSocketConnector| + // + + nsresult ConvertAddressToString(const struct sockaddr& aAddress, + socklen_t aAddressLength, + nsACString& aAddressString) override; + + nsresult CreateListenSocket(struct sockaddr* aAddress, + socklen_t* aAddressLength, + int& aListenFd) override; + + nsresult AcceptStreamSocket(int aListenFd, + struct sockaddr* aAddress, + socklen_t* aAddressLen, + int& aStreamFd) override; + + nsresult CreateStreamSocket(struct sockaddr* aAddress, + socklen_t* aAddressLength, + int& aStreamFd) override; + + nsresult Duplicate(UnixSocketConnector*& aConnector) override; + +private: + nsresult CreateSocket(int& aFd) const; + nsresult SetSocketFlags(int aFd) const; + nsresult CreateAddress(struct sockaddr& aAddress, + socklen_t& aAddressLength) const; + + nsCString mSocketName; +}; + +} // namespace ipc +} // namespace mozilla + +#endif // mozilla_ipc_DaemonSocketConnector_h diff --git a/ipc/hal/DaemonSocketConsumer.cpp b/ipc/hal/DaemonSocketConsumer.cpp new file mode 100644 index 000000000..ad57d5f57 --- /dev/null +++ b/ipc/hal/DaemonSocketConsumer.cpp @@ -0,0 +1,33 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "DaemonSocketConsumer.h" + +namespace mozilla { +namespace ipc { + +// +// DaemonSocketIOConsumer +// + +DaemonSocketIOConsumer::DaemonSocketIOConsumer() +{ } + +DaemonSocketIOConsumer::~DaemonSocketIOConsumer() +{ } + +// +// DaemonSocketConsumer +// + +DaemonSocketConsumer::DaemonSocketConsumer() +{ } + +DaemonSocketConsumer::~DaemonSocketConsumer() +{ } + +} +} diff --git a/ipc/hal/DaemonSocketConsumer.h b/ipc/hal/DaemonSocketConsumer.h new file mode 100644 index 000000000..ababab893 --- /dev/null +++ b/ipc/hal/DaemonSocketConsumer.h @@ -0,0 +1,68 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_ipc_DaemonSocketConsumer_h +#define mozilla_ipc_DaemonSocketConsumer_h + +namespace mozilla { +namespace ipc { + +class DaemonSocketPDU; + +/** + * |DaemonSocketIOConsumer| processes incoming PDUs from the + * HAL daemon. Please note that its method |Handle| runs on a + * different than the consumer thread. + */ +class DaemonSocketIOConsumer +{ +public: + virtual ~DaemonSocketIOConsumer(); + + virtual void Handle(DaemonSocketPDU& aPDU) = 0; + virtual void StoreResultHandler(const DaemonSocketPDU& aPDU) = 0; + +protected: + DaemonSocketIOConsumer(); +}; + +/** + * |DaemonSocketConsumer| handles socket events. + */ +class DaemonSocketConsumer +{ +public: + /** + * Callback for socket success. Consumer-thread only. + * + * @param aIndex The index that has been given to the stream socket. + */ + virtual void OnConnectSuccess(int aIndex) = 0; + + /** + * Callback for socket errors. Consumer-thread only. + * + * @param aIndex The index that has been given to the stream socket. + */ + virtual void OnConnectError(int aIndex) = 0; + + /** + * Callback for socket disconnect. Consumer-thread only. + * + * @param aIndex The index that has been given to the stream socket. + */ + virtual void OnDisconnect(int aIndex) = 0; + +protected: + DaemonSocketConsumer(); + virtual ~DaemonSocketConsumer(); +}; + +} +} + +#endif + diff --git a/ipc/hal/DaemonSocketMessageHandlers.h b/ipc/hal/DaemonSocketMessageHandlers.h new file mode 100644 index 000000000..184e687f1 --- /dev/null +++ b/ipc/hal/DaemonSocketMessageHandlers.h @@ -0,0 +1,40 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * Message handlers + * + * This file contains base classes for message handling. + */ + +#ifndef mozilla_ipc_DaemonSocketMessageHandlers_h +#define mozilla_ipc_DaemonSocketMessageHandlers_h + +#include "nsISupportsImpl.h" // for ref-counting + +namespace mozilla { +namespace ipc { + +/** + * |DaemonSocketResultHandler| is the base class for all protocol-specific + * result handlers. It currently only manages the reference counting. + */ +class DaemonSocketResultHandler +{ +public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DaemonSocketResultHandler); + +protected: + DaemonSocketResultHandler() + { } + virtual ~DaemonSocketResultHandler() + { } +}; + +} // namespace ipc +} // namespace mozilla + +#endif // mozilla_ipc_DaemonSocketMessageHandlers_h diff --git a/ipc/hal/DaemonSocketPDU.cpp b/ipc/hal/DaemonSocketPDU.cpp new file mode 100644 index 000000000..de6aa0da7 --- /dev/null +++ b/ipc/hal/DaemonSocketPDU.cpp @@ -0,0 +1,202 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "DaemonSocketPDU.h" +#include "mozilla/ipc/DaemonSocketConsumer.h" +#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, MOZ_COUNT_DTOR + +#ifdef CHROMIUM_LOG +#undef CHROMIUM_LOG +#endif + +#if defined(MOZ_WIDGET_GONK) +#include <android/log.h> +#define CHROMIUM_LOG(args...) __android_log_print(ANDROID_LOG_INFO, "I/O", args); +#else +#include <stdio.h> +#define IODEBUG true +#define CHROMIUM_LOG(args...) if (IODEBUG) printf(args); +#endif + +namespace mozilla { +namespace ipc { + +// +// DaemonSocketPDU +// + +DaemonSocketPDU::DaemonSocketPDU(uint8_t aService, uint8_t aOpcode, + uint16_t aPayloadSize) + : mConsumer(nullptr) +{ + MOZ_COUNT_CTOR_INHERITED(DaemonSocketPDU, UnixSocketIOBuffer); + + // Allocate memory + size_t availableSpace = PDU_HEADER_SIZE + aPayloadSize; + ResetBuffer(new uint8_t[availableSpace], 0, 0, availableSpace); + + // Reserve PDU header + uint8_t* data = Append(PDU_HEADER_SIZE); + MOZ_ASSERT(data); + + // Setup PDU header + data[PDU_OFF_SERVICE] = aService; + data[PDU_OFF_OPCODE] = aOpcode; + memcpy(data + PDU_OFF_LENGTH, &aPayloadSize, sizeof(aPayloadSize)); +} + +DaemonSocketPDU::DaemonSocketPDU(size_t aPayloadSize) + : mConsumer(nullptr) +{ + MOZ_COUNT_CTOR_INHERITED(DaemonSocketPDU, UnixSocketIOBuffer); + + size_t availableSpace = PDU_HEADER_SIZE + aPayloadSize; + ResetBuffer(new uint8_t[availableSpace], 0, 0, availableSpace); +} + +DaemonSocketPDU::~DaemonSocketPDU() +{ + MOZ_COUNT_DTOR_INHERITED(DaemonSocketPDU, UnixSocketIOBuffer); + + UniquePtr<uint8_t[]> data(GetBuffer()); + ResetBuffer(nullptr, 0, 0, 0); +} + +void +DaemonSocketPDU::GetHeader(uint8_t& aService, uint8_t& aOpcode, + uint16_t& aPayloadSize) +{ + memcpy(&aService, GetData(PDU_OFF_SERVICE), sizeof(aService)); + memcpy(&aOpcode, GetData(PDU_OFF_OPCODE), sizeof(aOpcode)); + memcpy(&aPayloadSize, GetData(PDU_OFF_LENGTH), sizeof(aPayloadSize)); +} + +ssize_t +DaemonSocketPDU::Send(int aFd) +{ + struct iovec iv; + memset(&iv, 0, sizeof(iv)); + iv.iov_base = GetData(GetLeadingSpace()); + iv.iov_len = GetSize(); + + struct msghdr msg; + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = &iv; + msg.msg_iovlen = 1; + msg.msg_control = nullptr; + msg.msg_controllen = 0; + + ssize_t res = TEMP_FAILURE_RETRY(sendmsg(aFd, &msg, 0)); + if (res < 0) { + MOZ_ASSERT(errno != EBADF); /* internal error */ + OnError("sendmsg", errno); + return -1; + } + + Consume(res); + + if (mConsumer) { + // We successfully sent a PDU, now store the + // result handler in the consumer. + mConsumer->StoreResultHandler(*this); + } + + return res; +} + +#define CMSGHDR_CONTAINS_FD(_cmsghdr) \ + ( ((_cmsghdr)->cmsg_level == SOL_SOCKET) && \ + ((_cmsghdr)->cmsg_type == SCM_RIGHTS) ) + +ssize_t +DaemonSocketPDU::Receive(int aFd) +{ + struct iovec iv; + memset(&iv, 0, sizeof(iv)); + iv.iov_base = GetData(0); + iv.iov_len = GetAvailableSpace(); + + uint8_t cmsgbuf[CMSG_SPACE(sizeof(int)* MAX_NFDS)]; + + struct msghdr msg; + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = &iv; + msg.msg_iovlen = 1; + msg.msg_control = cmsgbuf; + msg.msg_controllen = sizeof(cmsgbuf); + + ssize_t res = TEMP_FAILURE_RETRY(recvmsg(aFd, &msg, MSG_NOSIGNAL)); + if (res < 0) { + MOZ_ASSERT(errno != EBADF); /* internal error */ + OnError("recvmsg", errno); + return -1; + } + if (msg.msg_flags & (MSG_CTRUNC | MSG_OOB | MSG_ERRQUEUE)) { + return -1; + } + + SetRange(0, res); + + struct cmsghdr* chdr = CMSG_FIRSTHDR(&msg); + + for (; chdr; chdr = CMSG_NXTHDR(&msg, chdr)) { + if (NS_WARN_IF(!CMSGHDR_CONTAINS_FD(chdr))) { + continue; + } + // Retrieve sent file descriptors. + size_t fdCount = (chdr->cmsg_len - CMSG_ALIGN(sizeof(struct cmsghdr))) / sizeof(int); + for (size_t i = 0; i < fdCount; i++) { + int* receivedFd = static_cast<int*>(CMSG_DATA(chdr)) + i; + mReceivedFds.AppendElement(*receivedFd); + } + } + + return res; +} + +nsTArray<int> +DaemonSocketPDU::AcquireFds() +{ + // Forget all RAII object to avoid closing the fds. + nsTArray<int> fds; + for (auto& fd : mReceivedFds) { + fds.AppendElement(fd.forget()); + } + mReceivedFds.Clear(); + return fds; +} + +nsresult +DaemonSocketPDU::UpdateHeader() +{ + size_t len = GetPayloadSize(); + if (len >= PDU_MAX_PAYLOAD_LENGTH) { + return NS_ERROR_ILLEGAL_VALUE; + } + uint16_t len16 = static_cast<uint16_t>(len); + + memcpy(GetData(PDU_OFF_LENGTH), &len16, sizeof(len16)); + + return NS_OK; +} + +size_t +DaemonSocketPDU::GetPayloadSize() const +{ + MOZ_ASSERT(GetSize() >= PDU_HEADER_SIZE); + + return GetSize() - PDU_HEADER_SIZE; +} + +void +DaemonSocketPDU::OnError(const char* aFunction, int aErrno) +{ + CHROMIUM_LOG("%s failed with error %d (%s)", + aFunction, aErrno, strerror(aErrno)); +} + +} +} diff --git a/ipc/hal/DaemonSocketPDU.h b/ipc/hal/DaemonSocketPDU.h new file mode 100644 index 000000000..ebc4e4419 --- /dev/null +++ b/ipc/hal/DaemonSocketPDU.h @@ -0,0 +1,94 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_ipc_DaemonSocketPDU_h +#define mozilla_ipc_DaemonSocketPDU_h + +#include "mozilla/FileUtils.h" +#include "mozilla/ipc/SocketBase.h" +#include "mozilla/ipc/DaemonSocketMessageHandlers.h" +#include "nsTArray.h" + +namespace mozilla { +namespace ipc { + +static const size_t MAX_NFDS = 16; +class DaemonSocketIOConsumer; + +/** + * |DaemonSocketPDU| represents a single PDU that is transfered from or to + * the HAL daemon. Each PDU contains exactly one command. + * + * A PDU as the following format + * + * | 1 | 1 | 2 | n | + * | service | opcode | payload length | payload | + * + * Service and Opcode each require 1 byte, the payload length requires 2 + * bytes, and the payload requires the number of bytes as stored in the + * payload-length field. + * + * Each service and opcode can have a different payload with individual + * length. For the exact details of the HAL protocol, please refer to + * + * https://git.kernel.org/cgit/bluetooth/bluez.git/tree/android/hal-ipc-api.txt?id=5.24 + * + */ +class DaemonSocketPDU final : public UnixSocketIOBuffer +{ +public: + enum { + PDU_OFF_SERVICE = 0, + PDU_OFF_OPCODE = 1, + PDU_OFF_LENGTH = 2, + PDU_OFF_PAYLOAD = 4, + PDU_HEADER_SIZE = PDU_OFF_PAYLOAD, + PDU_MAX_PAYLOAD_LENGTH = 1 << 16 + }; + + DaemonSocketPDU(uint8_t aService, uint8_t aOpcode, uint16_t aPayloadSize); + DaemonSocketPDU(size_t aPayloadSize); + ~DaemonSocketPDU(); + + void SetConsumer(DaemonSocketIOConsumer* aConsumer) + { + mConsumer = aConsumer; + } + + void SetResultHandler(DaemonSocketResultHandler* aRes) + { + mRes = aRes; + } + + DaemonSocketResultHandler* GetResultHandler() const + { + return mRes; + } + + void GetHeader(uint8_t& aService, uint8_t& aOpcode, + uint16_t& aPayloadSize); + + ssize_t Send(int aFd) override; + ssize_t Receive(int aFd) override; + + nsTArray<int> AcquireFds(); + + nsresult UpdateHeader(); + +private: + size_t GetPayloadSize() const; + void OnError(const char* aFunction, int aErrno); + + DaemonSocketIOConsumer* mConsumer; + RefPtr<DaemonSocketResultHandler> mRes; + nsTArray<ScopedClose> mReceivedFds; +}; + +} +} + +#endif + diff --git a/ipc/hal/DaemonSocketPDUHelpers.cpp b/ipc/hal/DaemonSocketPDUHelpers.cpp new file mode 100644 index 000000000..6aaf21c33 --- /dev/null +++ b/ipc/hal/DaemonSocketPDUHelpers.cpp @@ -0,0 +1,335 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "DaemonSocketPDUHelpers.h" +#include <limits> + +// Enable this constant to abort Gecko on IPC errors. This is helpful +// for debugging, but should *never* be enabled by default. +#define MOZ_HAL_ABORT_ON_IPC_ERRORS (0) + +#ifdef CHROMIUM_LOG +#undef CHROMIUM_LOG +#endif + +#if defined(MOZ_WIDGET_GONK) + +#include <android/log.h> + +#define CHROMIUM_LOG(args...) \ + __android_log_print(ANDROID_LOG_INFO, "HAL-IPC", args); + +#define CHROMIUM_LOG_VA(fmt, ap) \ + __android_log_vprint(ANDROID_LOG_INFO, "HAL-IPC", fmt, ap); + +#else + +#include <stdio.h> + +#define IODEBUG true +#define CHROMIUM_LOG(args...) if (IODEBUG) { printf(args); } +#define CHROMIUM_LOG_VA(fmt, ap) if (IODEBUG) { vprintf(fmt, ap); } + +#endif + +namespace mozilla { +namespace ipc { +namespace DaemonSocketPDUHelpers { + +// +// Logging +// + +namespace detail { + +void +LogProtocolError(const char* aFmt, ...) +{ + va_list ap; + + va_start(ap, aFmt); + CHROMIUM_LOG_VA(aFmt, ap); + va_end(ap); + + if (MOZ_HAL_ABORT_ON_IPC_ERRORS) { + MOZ_CRASH("HAL IPC protocol error"); + } +} + +} // namespace detail + +// +// Conversion +// + +nsresult +Convert(bool aIn, uint8_t& aOut) +{ + static const uint8_t sValue[] = { + [false] = 0x00, + [true] = 0x01 + }; + if (MOZ_HAL_IPC_CONVERT_WARN_IF( + aIn >= MOZ_ARRAY_LENGTH(sValue), bool, uint8_t)) { + aOut = 0; + return NS_ERROR_ILLEGAL_VALUE; + } + aOut = sValue[aIn]; + return NS_OK; +} + +nsresult +Convert(bool aIn, int32_t& aOut) +{ + uint8_t out; + nsresult rv = Convert(aIn, out); + if (NS_FAILED(rv)) { + out = 0; // silence compiler warning + return rv; + } + aOut = static_cast<int32_t>(out); + return NS_OK; +} + +nsresult +Convert(int aIn, uint8_t& aOut) +{ + if (MOZ_HAL_IPC_CONVERT_WARN_IF( + aIn < std::numeric_limits<uint8_t>::min(), int, uint8_t) || + MOZ_HAL_IPC_CONVERT_WARN_IF( + aIn > std::numeric_limits<uint8_t>::max(), int, uint8_t)) { + aOut = 0; // silences compiler warning + return NS_ERROR_ILLEGAL_VALUE; + } + aOut = static_cast<uint8_t>(aIn); + return NS_OK; +} + +nsresult +Convert(int aIn, int16_t& aOut) +{ + if (MOZ_HAL_IPC_CONVERT_WARN_IF( + aIn < std::numeric_limits<int16_t>::min(), int, int16_t) || + MOZ_HAL_IPC_CONVERT_WARN_IF( + aIn > std::numeric_limits<int16_t>::max(), int, int16_t)) { + aOut = 0; // silences compiler warning + return NS_ERROR_ILLEGAL_VALUE; + } + aOut = static_cast<int16_t>(aIn); + return NS_OK; +} + +nsresult +Convert(int aIn, int32_t& aOut) +{ + if (MOZ_HAL_IPC_CONVERT_WARN_IF( + aIn < std::numeric_limits<int32_t>::min(), int, int32_t) || + MOZ_HAL_IPC_CONVERT_WARN_IF( + aIn > std::numeric_limits<int32_t>::max(), int, int32_t)) { + aOut = 0; // silences compiler warning + return NS_ERROR_ILLEGAL_VALUE; + } + aOut = static_cast<int32_t>(aIn); + return NS_OK; +} + +nsresult +Convert(uint8_t aIn, bool& aOut) +{ + static const bool sBool[] = { + [0x00] = false, + [0x01] = true + }; + if (MOZ_HAL_IPC_CONVERT_WARN_IF( + aIn >= MOZ_ARRAY_LENGTH(sBool), uint8_t, bool)) { + return NS_ERROR_ILLEGAL_VALUE; + } + aOut = sBool[aIn]; + return NS_OK; +} + +nsresult +Convert(uint8_t aIn, char& aOut) +{ + aOut = static_cast<char>(aIn); + return NS_OK; +} + +nsresult +Convert(uint8_t aIn, int& aOut) +{ + aOut = static_cast<int>(aIn); + return NS_OK; +} + +nsresult +Convert(uint8_t aIn, unsigned long& aOut) +{ + aOut = static_cast<unsigned long>(aIn); + return NS_OK; +} + +nsresult +Convert(uint32_t aIn, int& aOut) +{ + aOut = static_cast<int>(aIn); + return NS_OK; +} + +nsresult +Convert(uint32_t aIn, uint8_t& aOut) +{ + if (MOZ_HAL_IPC_CONVERT_WARN_IF( + aIn < std::numeric_limits<uint8_t>::min(), uint32_t, uint8_t) || + MOZ_HAL_IPC_CONVERT_WARN_IF( + aIn > std::numeric_limits<uint8_t>::max(), uint32_t, uint8_t)) { + aOut = 0; // silences compiler warning + return NS_ERROR_ILLEGAL_VALUE; + } + aOut = static_cast<uint8_t>(aIn); + return NS_OK; +} + +nsresult +Convert(size_t aIn, uint16_t& aOut) +{ + if (MOZ_HAL_IPC_CONVERT_WARN_IF(aIn >= (1ul << 16), size_t, uint16_t)) { + aOut = 0; // silences compiler warning + return NS_ERROR_ILLEGAL_VALUE; + } + aOut = static_cast<uint16_t>(aIn); + return NS_OK; +} + +// +// Packing +// + +nsresult +PackPDU(bool aIn, DaemonSocketPDU& aPDU) +{ + return PackPDU(PackConversion<bool, uint8_t>(aIn), aPDU); +} + +nsresult +PackPDU(const DaemonSocketPDUHeader& aIn, DaemonSocketPDU& aPDU) +{ + nsresult rv = PackPDU(aIn.mService, aPDU); + if (NS_FAILED(rv)) { + return rv; + } + rv = PackPDU(aIn.mOpcode, aPDU); + if (NS_FAILED(rv)) { + return rv; + } + rv = PackPDU(aIn.mLength, aPDU); + if (NS_FAILED(rv)) { + return rv; + } + return NS_OK; +} + +// +// Unpacking +// + +nsresult +UnpackPDU(DaemonSocketPDU& aPDU, bool& aOut) +{ + return UnpackPDU(aPDU, UnpackConversion<uint8_t, bool>(aOut)); +} + +nsresult +UnpackPDU(DaemonSocketPDU& aPDU, char& aOut) +{ + return UnpackPDU(aPDU, UnpackConversion<uint8_t, char>(aOut)); +} + +nsresult +UnpackPDU(DaemonSocketPDU& aPDU, nsDependentCString& aOut) +{ + // We get a pointer to the first character in the PDU, a length + // of 1 ensures we consume the \0 byte. With 'str' pointing to + // the string in the PDU, we can copy the actual bytes. + + const char* str = reinterpret_cast<const char*>(aPDU.Consume(1)); + if (MOZ_HAL_IPC_UNPACK_WARN_IF(!str, nsDependentCString)) { + return NS_ERROR_ILLEGAL_VALUE; // end of PDU + } + + const char* end = static_cast<char*>(memchr(str, '\0', aPDU.GetSize() + 1)); + if (MOZ_HAL_IPC_UNPACK_WARN_IF(!end, nsDependentCString)) { + return NS_ERROR_ILLEGAL_VALUE; // no string terminator + } + + ptrdiff_t len = end - str; + + const uint8_t* rest = aPDU.Consume(len); + if (MOZ_HAL_IPC_UNPACK_WARN_IF(!rest, nsDependentCString)) { + // We couldn't consume bytes that should have been there. + return NS_ERROR_ILLEGAL_VALUE; + } + + aOut.Rebind(str, len); + + return NS_OK; +} + +nsresult +UnpackPDU(DaemonSocketPDU& aPDU, const UnpackCString0& aOut) +{ + nsDependentCString cstring; + + nsresult rv = UnpackPDU(aPDU, cstring); + if (NS_FAILED(rv)) { + return NS_ERROR_ILLEGAL_VALUE; + } + + aOut.mString->AssignASCII(cstring.get(), cstring.Length()); + + return NS_OK; +} + +nsresult +UnpackPDU(DaemonSocketPDU& aPDU, const UnpackString0& aOut) +{ + nsDependentCString cstring; + + nsresult rv = UnpackPDU(aPDU, cstring); + if (NS_FAILED(rv)) { + return NS_ERROR_ILLEGAL_VALUE; + } + + *aOut.mString = NS_ConvertUTF8toUTF16(cstring); + + return NS_OK; +} + +// +// Init operators +// + +void +PDUInitOp::WarnAboutTrailingData() const +{ + size_t size = mPDU->GetSize(); + + if (MOZ_LIKELY(!size)) { + return; + } + + uint8_t service, opcode; + uint16_t payloadSize; + mPDU->GetHeader(service, opcode, payloadSize); + + detail::LogProtocolError( + "Unpacked PDU of type (%x,%x) still contains %zu Bytes of data.", + service, opcode, size); +} + +} // namespace DaemonSocketPDUHelpers +} // namespace ipc +} // namespace mozilla diff --git a/ipc/hal/DaemonSocketPDUHelpers.h b/ipc/hal/DaemonSocketPDUHelpers.h new file mode 100644 index 000000000..38a848e46 --- /dev/null +++ b/ipc/hal/DaemonSocketPDUHelpers.h @@ -0,0 +1,1283 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_ipc_DaemonSocketPDUHelpers_h +#define mozilla_ipc_DaemonSocketPDUHelpers_h + +#include <stdint.h> +#include "mozilla/ipc/DaemonSocketPDU.h" +#include "mozilla/UniquePtr.h" +#include "nsString.h" + +namespace mozilla { +namespace ipc { + +struct DaemonSocketPDUHeader { + DaemonSocketPDUHeader() + : mService(0x00) + , mOpcode(0x00) + , mLength(0x00) + { } + + DaemonSocketPDUHeader(uint8_t aService, uint8_t aOpcode, uint16_t aLength) + : mService(aService) + , mOpcode(aOpcode) + , mLength(aLength) + { } + + uint8_t mService; + uint8_t mOpcode; + uint16_t mLength; +}; + +namespace DaemonSocketPDUHelpers { + +// +// Logging +// +// The HAL IPC logging macros below print clear error messages for +// failed IPC operations. Use |MOZ_HAL_IPC_CONVERT_WARN_IF|, +// |MOZ_HAL_IPC_PACK_WARN_IF| and |MOZ_HAL_IPC_UNPACK_WARN_IF| to +// test for failures when processing PDUs. +// +// All macros accept the test condition as their first argument, and +// additional type information: the convert macro takes the input and +// output types, the pack macro takes the input type, and the unpack +// macro takes output type. All macros return the result of the test +// condition. If the test fails (i.e., the condition is true), they +// output a warning to the log. +// +// Don't call the functions in the detail namespace. They are helpers +// and not for general use. +// + +namespace detail { + +void +LogProtocolError(const char*, ...); + +inline bool +ConvertWarnIfImpl(const char* aFile, unsigned long aLine, + bool aCondition, const char* aExpr, const char* aIn, + const char* aOut) +{ + if (MOZ_UNLIKELY(aCondition)) { + LogProtocolError("%s:%d: Convert('%s' to '%s') failed: %s", + aFile, aLine, aIn, aOut, aExpr); + } + return aCondition; +} + +inline bool +PackWarnIfImpl(const char* aFile, unsigned long aLine, + bool aCondition, const char* aExpr, const char* aIn) +{ + if (MOZ_UNLIKELY(aCondition)) { + LogProtocolError("%s:%d: Pack('%s') failed: %s", + aFile, aLine, aIn, aExpr); + } + return aCondition; +} + +inline bool +UnpackWarnIfImpl(const char* aFile, unsigned long aLine, + bool aCondition, const char* aExpr, const char* aOut) +{ + if (MOZ_UNLIKELY(aCondition)) { + LogProtocolError("%s:%d: Unpack('%s') failed: %s", + aFile, aLine, aOut, aExpr); + } + return aCondition; +} + +} // namespace detail + +#define MOZ_HAL_IPC_CONVERT_WARN_IF(condition, in, out) \ + ::mozilla::ipc::DaemonSocketPDUHelpers::detail:: \ + ConvertWarnIfImpl(__FILE__, __LINE__, condition, #condition, #in, #out) + +#define MOZ_HAL_IPC_PACK_WARN_IF(condition, in) \ + ::mozilla::ipc::DaemonSocketPDUHelpers::detail:: \ + PackWarnIfImpl(__FILE__, __LINE__, condition, #condition, #in) + +#define MOZ_HAL_IPC_UNPACK_WARN_IF(condition, out) \ + ::mozilla::ipc::DaemonSocketPDUHelpers::detail:: \ + UnpackWarnIfImpl(__FILE__, __LINE__, condition, #condition, #out) + +// +// Conversion +// +// PDUs can only store primitive data types, such as integers or +// byte arrays. Gecko often uses more complex data types, such as +// enumators or stuctures. Conversion functions convert between +// primitive data and internal Gecko's data types during a PDU's +// packing and unpacking. +// + +nsresult +Convert(bool aIn, uint8_t& aOut); + +nsresult +Convert(bool aIn, int32_t& aOut); + +nsresult +Convert(int aIn, uint8_t& aOut); + +nsresult +Convert(int aIn, int16_t& aOut); + +nsresult +Convert(int aIn, int32_t& aOut); + +nsresult +Convert(uint8_t aIn, bool& aOut); + +nsresult +Convert(uint8_t aIn, char& aOut); + +nsresult +Convert(uint8_t aIn, int& aOut); + +nsresult +Convert(uint8_t aIn, unsigned long& aOut); + +nsresult +Convert(uint32_t aIn, int& aOut); + +nsresult +Convert(uint32_t aIn, uint8_t& aOut); + +nsresult +Convert(size_t aIn, uint16_t& aOut); + +// +// Packing +// + +// introduce link errors on non-handled data types +template <typename T> +nsresult +PackPDU(T aIn, DaemonSocketPDU& aPDU); + +nsresult +PackPDU(bool aIn, DaemonSocketPDU& aPDU); + +inline nsresult +PackPDU(uint8_t aIn, DaemonSocketPDU& aPDU) +{ + return aPDU.Write(aIn); +} + +inline nsresult +PackPDU(uint16_t aIn, DaemonSocketPDU& aPDU) +{ + return aPDU.Write(aIn); +} + +inline nsresult +PackPDU(int32_t aIn, DaemonSocketPDU& aPDU) +{ + return aPDU.Write(aIn); +} + +inline nsresult +PackPDU(uint32_t aIn, DaemonSocketPDU& aPDU) +{ + return aPDU.Write(aIn); +} + +inline nsresult +PackPDU(int64_t aIn, DaemonSocketPDU& aPDU) +{ + return aPDU.Write(aIn); +} + +inline nsresult +PackPDU(uint64_t aIn, DaemonSocketPDU& aPDU) +{ + return aPDU.Write(aIn); +} + +inline nsresult +PackPDU(float aIn, DaemonSocketPDU& aPDU) +{ + return aPDU.Write(aIn); +} + +inline nsresult +PackPDU(double aIn, DaemonSocketPDU& aPDU) +{ + return aPDU.Write(aIn); +} + +nsresult +PackPDU(const DaemonSocketPDUHeader& aIn, DaemonSocketPDU& aPDU); + +/* |PackConversion| is a helper for packing converted values. Pass + * an instance of this structure to |PackPDU| to convert a value from + * the input type to the output type and and write it to the PDU. + */ +template<typename Tin, typename Tout> +struct PackConversion { + PackConversion(const Tin& aIn) + : mIn(aIn) + { } + + const Tin& mIn; +}; + +template<typename Tin, typename Tout> +inline nsresult +PackPDU(const PackConversion<Tin, Tout>& aIn, DaemonSocketPDU& aPDU) +{ + Tout out; + + nsresult rv = Convert(aIn.mIn, out); + if (NS_FAILED(rv)) { + return rv; + } + return PackPDU(out, aPDU); +} + +/* |PackArray| is a helper for packing arrays. Pass an instance + * of this structure as the first argument to |PackPDU| to pack + * an array. The array's maximum default length is 255 elements. + */ +template <typename T> +struct PackArray +{ + PackArray(const T* aData, size_t aLength) + : mData(aData) + , mLength(aLength) + { } + + const T* mData; + size_t mLength; +}; + +/* This implementation of |PackPDU| packs the length of an array + * and the elements of the array one-by-one. + */ +template<typename T> +inline nsresult +PackPDU(const PackArray<T>& aIn, DaemonSocketPDU& aPDU) +{ + for (size_t i = 0; i < aIn.mLength; ++i) { + nsresult rv = PackPDU(aIn.mData[i], aPDU); + if (NS_FAILED(rv)) { + return rv; + } + } + return NS_OK; +} + +template<> +inline nsresult +PackPDU<uint8_t>(const PackArray<uint8_t>& aIn, DaemonSocketPDU& aPDU) +{ + /* Write raw bytes in one pass */ + return aPDU.Write(aIn.mData, aIn.mLength); +} + +template<> +inline nsresult +PackPDU<char>(const PackArray<char>& aIn, DaemonSocketPDU& aPDU) +{ + /* Write raw bytes in one pass */ + return aPDU.Write(aIn.mData, aIn.mLength); +} + +/* |PackCString0| is a helper for packing 0-terminated C string, + * including the \0 character. Pass an instance of this structure + * as the first argument to |PackPDU| to pack a string. + */ +struct PackCString0 +{ + PackCString0(const nsCString& aString) + : mString(aString) + { } + + const nsCString& mString; +}; + +/* This implementation of |PackPDU| packs a 0-terminated C string. + */ +inline nsresult +PackPDU(const PackCString0& aIn, DaemonSocketPDU& aPDU) +{ + return PackPDU( + PackArray<uint8_t>(reinterpret_cast<const uint8_t*>(aIn.mString.get()), + aIn.mString.Length() + 1), aPDU); +} + +/* |PackReversed| is a helper for packing data in reversed order. Pass an + * instance of this structure as the first argument to |PackPDU| to pack data + * in reversed order. + */ +template<typename T> +struct PackReversed +{ + PackReversed(const T& aValue) + : mValue(aValue) + { } + + const T& mValue; +}; + +/* No general rules to pack data in reversed order. Signal a link error if the + * type |T| of |PackReversed| is not defined explicitly. + */ +template<typename T> +nsresult +PackPDU(const PackReversed<T>& aIn, DaemonSocketPDU& aPDU); + +/* This implementation of |PackPDU| packs elements in |PackArray| in reversed + * order. (ex. reversed GATT UUID, see bug 1171866) + */ +template<typename U> +inline nsresult +PackPDU(const PackReversed<PackArray<U>>& aIn, DaemonSocketPDU& aPDU) +{ + for (size_t i = 0; i < aIn.mValue.mLength; ++i) { + nsresult rv = PackPDU(aIn.mValue.mData[aIn.mValue.mLength - i - 1], aPDU); + if (NS_FAILED(rv)) { + return rv; + } + } + return NS_OK; +} + +/* |PackArray<PackReversed<U>>| is a helper for packing data of each element in + * the reversed order. Pass an instance of this structure as the first argument + * to |PackPDU| to pack data of each array element in the reversed order. + * + * Unlike |PackReversed<PackArray<U>>| which packed array elements in the + * reversed order, here we use |PackReversed<U>| to pack data of each element + * of |PackArray| in the reversed order. + */ +template<typename U> +struct PackArray<PackReversed<U>> +{ + PackArray(const U* aData, size_t aLength) + : mData(aData) + , mLength(aLength) + { } + + const U* mData; + size_t mLength; +}; + +/* This implementation of |PackPDU| packs data of each element in |PackArray| + * in the reversed order. (ex. reversed GATT UUID, see bug 1171866) + */ +template<typename U> +inline nsresult +PackPDU(const PackArray<PackReversed<U>>& aIn, DaemonSocketPDU& aPDU) +{ + for (size_t i = 0; i < aIn.mLength; ++i) { + nsresult rv = PackPDU(PackReversed<U>(aIn.mData[i]), aPDU); + if (NS_FAILED(rv)) { + return rv; + } + } + return NS_OK; +} + +template <typename T1, typename T2> +inline nsresult +PackPDU(const T1& aIn1, const T2& aIn2, DaemonSocketPDU& aPDU) +{ + nsresult rv = PackPDU(aIn1, aPDU); + if (NS_FAILED(rv)) { + return rv; + } + return PackPDU(aIn2, aPDU); +} + +template <typename T1, typename T2, typename T3> +inline nsresult +PackPDU(const T1& aIn1, const T2& aIn2, const T3& aIn3, + DaemonSocketPDU& aPDU) +{ + nsresult rv = PackPDU(aIn1, aPDU); + if (NS_FAILED(rv)) { + return rv; + } + rv = PackPDU(aIn2, aPDU); + if (NS_FAILED(rv)) { + return rv; + } + return PackPDU(aIn3, aPDU); +} + +template <typename T1, typename T2, typename T3, typename T4> +inline nsresult +PackPDU(const T1& aIn1, const T2& aIn2, const T3& aIn3, const T4& aIn4, + DaemonSocketPDU& aPDU) +{ + nsresult rv = PackPDU(aIn1, aPDU); + if (NS_FAILED(rv)) { + return rv; + } + rv = PackPDU(aIn2, aPDU); + if (NS_FAILED(rv)) { + return rv; + } + rv = PackPDU(aIn3, aPDU); + if (NS_FAILED(rv)) { + return rv; + } + return PackPDU(aIn4, aPDU); +} + +template <typename T1, typename T2, typename T3, + typename T4, typename T5> +inline nsresult +PackPDU(const T1& aIn1, const T2& aIn2, const T3& aIn3, + const T4& aIn4, const T5& aIn5, + DaemonSocketPDU& aPDU) +{ + nsresult rv = PackPDU(aIn1, aPDU); + if (NS_FAILED(rv)) { + return rv; + } + rv = PackPDU(aIn2, aPDU); + if (NS_FAILED(rv)) { + return rv; + } + rv = PackPDU(aIn3, aPDU); + if (NS_FAILED(rv)) { + return rv; + } + rv = PackPDU(aIn4, aPDU); + if (NS_FAILED(rv)) { + return rv; + } + return PackPDU(aIn5, aPDU); +} + +template <typename T1, typename T2, typename T3, + typename T4, typename T5, typename T6> +inline nsresult +PackPDU(const T1& aIn1, const T2& aIn2, const T3& aIn3, + const T4& aIn4, const T5& aIn5, const T6& aIn6, + DaemonSocketPDU& aPDU) +{ + nsresult rv = PackPDU(aIn1, aPDU); + if (NS_FAILED(rv)) { + return rv; + } + rv = PackPDU(aIn2, aPDU); + if (NS_FAILED(rv)) { + return rv; + } + rv = PackPDU(aIn3, aPDU); + if (NS_FAILED(rv)) { + return rv; + } + rv = PackPDU(aIn4, aPDU); + if (NS_FAILED(rv)) { + return rv; + } + rv = PackPDU(aIn5, aPDU); + if (NS_FAILED(rv)) { + return rv; + } + return PackPDU(aIn6, aPDU); +} + +template <typename T1, typename T2, typename T3, + typename T4, typename T5, typename T6, + typename T7> +inline nsresult +PackPDU(const T1& aIn1, const T2& aIn2, const T3& aIn3, + const T4& aIn4, const T5& aIn5, const T6& aIn6, + const T7& aIn7, DaemonSocketPDU& aPDU) +{ + nsresult rv = PackPDU(aIn1, aPDU); + if (NS_FAILED(rv)) { + return rv; + } + rv = PackPDU(aIn2, aPDU); + if (NS_FAILED(rv)) { + return rv; + } + rv = PackPDU(aIn3, aPDU); + if (NS_FAILED(rv)) { + return rv; + } + rv = PackPDU(aIn4, aPDU); + if (NS_FAILED(rv)) { + return rv; + } + rv = PackPDU(aIn5, aPDU); + if (NS_FAILED(rv)) { + return rv; + } + rv = PackPDU(aIn6, aPDU); + if (NS_FAILED(rv)) { + return rv; + } + return PackPDU(aIn7, aPDU); +} + +template <typename T1, typename T2, typename T3, + typename T4, typename T5, typename T6, + typename T7, typename T8> +inline nsresult +PackPDU(const T1& aIn1, const T2& aIn2, const T3& aIn3, + const T4& aIn4, const T5& aIn5, const T6& aIn6, + const T7& aIn7, const T8& aIn8, DaemonSocketPDU& aPDU) +{ + nsresult rv = PackPDU(aIn1, aPDU); + if (NS_FAILED(rv)) { + return rv; + } + rv = PackPDU(aIn2, aPDU); + if (NS_FAILED(rv)) { + return rv; + } + rv = PackPDU(aIn3, aPDU); + if (NS_FAILED(rv)) { + return rv; + } + rv = PackPDU(aIn4, aPDU); + if (NS_FAILED(rv)) { + return rv; + } + rv = PackPDU(aIn5, aPDU); + if (NS_FAILED(rv)) { + return rv; + } + rv = PackPDU(aIn6, aPDU); + if (NS_FAILED(rv)) { + return rv; + } + rv = PackPDU(aIn7, aPDU); + if (NS_FAILED(rv)) { + return rv; + } + return PackPDU(aIn8, aPDU); +} + +template <typename T1, typename T2, typename T3, + typename T4, typename T5, typename T6, + typename T7, typename T8, typename T9, + typename T10, typename T11, typename T12, + typename T13> +inline nsresult +PackPDU(const T1& aIn1, const T2& aIn2, const T3& aIn3, + const T4& aIn4, const T5& aIn5, const T6& aIn6, + const T7& aIn7, const T8& aIn8, const T9& aIn9, + const T10& aIn10, const T11& aIn11, const T12& aIn12, + const T13& aIn13, DaemonSocketPDU& aPDU) +{ + nsresult rv = PackPDU(aIn1, aPDU); + if (NS_FAILED(rv)) { + return rv; + } + rv = PackPDU(aIn2, aPDU); + if (NS_FAILED(rv)) { + return rv; + } + rv = PackPDU(aIn3, aPDU); + if (NS_FAILED(rv)) { + return rv; + } + rv = PackPDU(aIn4, aPDU); + if (NS_FAILED(rv)) { + return rv; + } + rv = PackPDU(aIn5, aPDU); + if (NS_FAILED(rv)) { + return rv; + } + rv = PackPDU(aIn6, aPDU); + if (NS_FAILED(rv)) { + return rv; + } + rv = PackPDU(aIn7, aPDU); + if (NS_FAILED(rv)) { + return rv; + } + rv = PackPDU(aIn8, aPDU); + if (NS_FAILED(rv)) { + return rv; + } + rv = PackPDU(aIn9, aPDU); + if (NS_FAILED(rv)) { + return rv; + } + rv = PackPDU(aIn10, aPDU); + if (NS_FAILED(rv)) { + return rv; + } + rv = PackPDU(aIn11, aPDU); + if (NS_FAILED(rv)) { + return rv; + } + rv = PackPDU(aIn12, aPDU); + if (NS_FAILED(rv)) { + return rv; + } + return PackPDU(aIn13, aPDU); +} + +// +// Unpacking +// + +// introduce link errors on non-handled data types +template <typename T> +nsresult +UnpackPDU(DaemonSocketPDU& aPDU, T& aOut); + +nsresult +UnpackPDU(DaemonSocketPDU& aPDU, bool& aOut); + +nsresult +UnpackPDU(DaemonSocketPDU& aPDU, char& aOut); + +inline nsresult +UnpackPDU(DaemonSocketPDU& aPDU, int8_t& aOut) +{ + return aPDU.Read(aOut); +} + +inline nsresult +UnpackPDU(DaemonSocketPDU& aPDU, uint8_t& aOut) +{ + return aPDU.Read(aOut); +} + +inline nsresult +UnpackPDU(DaemonSocketPDU& aPDU, uint16_t& aOut) +{ + return aPDU.Read(aOut); +} + +inline nsresult +UnpackPDU(DaemonSocketPDU& aPDU, int32_t& aOut) +{ + return aPDU.Read(aOut); +} + +inline nsresult +UnpackPDU(DaemonSocketPDU& aPDU, uint32_t& aOut) +{ + return aPDU.Read(aOut); +} + +inline nsresult +UnpackPDU(DaemonSocketPDU& aPDU, int64_t& aOut) +{ + return aPDU.Read(aOut); +} + +inline nsresult +UnpackPDU(DaemonSocketPDU& aPDU, uint64_t& aOut) +{ + return aPDU.Read(aOut); +} + +inline nsresult +UnpackPDU(DaemonSocketPDU& aPDU, float& aOut) +{ + return aPDU.Read(aOut); +} + +inline nsresult +UnpackPDU(DaemonSocketPDU& aPDU, double& aOut) +{ + return aPDU.Read(aOut); +} + +inline nsresult +UnpackPDU(DaemonSocketPDU& aPDU, DaemonSocketPDUHeader& aOut) +{ + nsresult rv = UnpackPDU(aPDU, aOut.mService); + if (NS_FAILED(rv)) { + return rv; + } + rv = UnpackPDU(aPDU, aOut.mOpcode); + if (NS_FAILED(rv)) { + return rv; + } + return UnpackPDU(aPDU, aOut.mLength); +} + +nsresult +UnpackPDU(DaemonSocketPDU& aPDU, nsDependentCString& aOut); + +/* |UnpackCString0| is a helper for unpacking 0-terminated C string, + * including the \0 character. Pass an instance of this structure + * as the first argument to |UnpackPDU| to unpack a string. + */ +struct UnpackCString0 +{ + UnpackCString0(nsCString& aString) + : mString(&aString) + { } + + nsCString* mString; // non-null by construction +}; + +/* This implementation of |UnpackPDU| unpacks a 0-terminated C string. + */ +nsresult +UnpackPDU(DaemonSocketPDU& aPDU, const UnpackCString0& aOut); + +/* |UnpackString0| is a helper for unpacking 0-terminated C string, + * including the \0 character. Pass an instance of this structure + * as the first argument to |UnpackPDU| to unpack a C string and convert + * it to wide-character encoding. + */ +struct UnpackString0 +{ + UnpackString0(nsString& aString) + : mString(&aString) + { } + + nsString* mString; // non-null by construction +}; + +/* This implementation of |UnpackPDU| unpacks a 0-terminated C string + * and converts it to wide-character encoding. + */ +nsresult +UnpackPDU(DaemonSocketPDU& aPDU, const UnpackString0& aOut); + +/* |UnpackConversion| is a helper for convering unpacked values. Pass + * an instance of this structure to |UnpackPDU| to read a value from + * the PDU in the input type and convert it to the output type. + */ +template<typename Tin, typename Tout> +struct UnpackConversion { + UnpackConversion(Tout& aOut) + : mOut(aOut) + { } + + Tout& mOut; +}; + +template<typename Tin, typename Tout> +inline nsresult +UnpackPDU(DaemonSocketPDU& aPDU, const UnpackConversion<Tin, Tout>& aOut) +{ + Tin in; + nsresult rv = UnpackPDU(aPDU, in); + if (NS_FAILED(rv)) { + return rv; + } + return Convert(in, aOut.mOut); +} + +/* |UnpackArray| is a helper for unpacking arrays. Pass an instance + * of this structure as the second argument to |UnpackPDU| to unpack + * an array. + */ +template <typename T> +struct UnpackArray +{ + UnpackArray(T* aData, size_t aLength) + : mData(aData) + , mLength(aLength) + { } + + UnpackArray(UniquePtr<T[]>& aData, size_t aLength) + : mData(nullptr) + , mLength(aLength) + { + aData.reset(new T[mLength]); + mData = aData.get(); + } + + UnpackArray(UniquePtr<T>& aData, size_t aSize, size_t aElemSize) + : mData(nullptr) + , mLength(aSize / aElemSize) + { + aData.reset(new T[mLength]); + mData = aData.get(); + } + + T* mData; + size_t mLength; +}; + +template<typename T> +inline nsresult +UnpackPDU(DaemonSocketPDU& aPDU, const UnpackArray<T>& aOut) +{ + for (size_t i = 0; i < aOut.mLength; ++i) { + nsresult rv = UnpackPDU(aPDU, aOut.mData[i]); + if (NS_FAILED(rv)) { + return rv; + } + } + return NS_OK; +} + +template<typename T> +inline nsresult +UnpackPDU(DaemonSocketPDU& aPDU, UnpackArray<T>& aOut) +{ + for (size_t i = 0; i < aOut.mLength; ++i) { + nsresult rv = UnpackPDU(aPDU, aOut.mData[i]); + if (NS_FAILED(rv)) { + return rv; + } + } + return NS_OK; +} + +template<> +inline nsresult +UnpackPDU<uint8_t>(DaemonSocketPDU& aPDU, const UnpackArray<uint8_t>& aOut) +{ + /* Read raw bytes in one pass */ + return aPDU.Read(aOut.mData, aOut.mLength); +} + +template<typename T> +inline nsresult +UnpackPDU(DaemonSocketPDU& aPDU, nsTArray<T>& aOut) +{ + for (typename nsTArray<T>::size_type i = 0; i < aOut.Length(); ++i) { + nsresult rv = UnpackPDU(aPDU, aOut[i]); + if (NS_FAILED(rv)) { + return rv; + } + } + return NS_OK; +} + +/* |UnpackReversed| is a helper for unpacking data in reversed order. Pass an + * instance of this structure as the second argument to |UnpackPDU| to unpack + * data in reversed order. + */ +template<typename T> +struct UnpackReversed +{ + UnpackReversed(T& aValue) + : mValue(&aValue) + { } + + UnpackReversed(T&& aValue) + : mValue(&aValue) + { } + + T* mValue; +}; + +/* No general rules to unpack data in reversed order. Signal a link error if + * the type |T| of |UnpackReversed| is not defined explicitly. + */ +template<typename T> +nsresult +UnpackPDU(DaemonSocketPDU& aPDU, const UnpackReversed<T>& aOut); + +template<typename U> +inline nsresult +UnpackPDU(DaemonSocketPDU& aPDU, const UnpackReversed<UnpackArray<U>>& aOut) +{ + for (size_t i = 0; i < aOut.mValue->mLength; ++i) { + nsresult rv = UnpackPDU(aPDU, + aOut.mValue->mData[aOut.mValue->mLength - i - 1]); + if (NS_FAILED(rv)) { + return rv; + } + } + return NS_OK; +} + +// +// Init operators +// + +// +// Below are general-purpose init operators for Bluetooth. The classes +// of type |ConstantInitOp[1..3]| initialize results or notifications +// with constant values. +// + +template <typename T1> +class ConstantInitOp1 final +{ +public: + ConstantInitOp1(const T1& aArg1) + : mArg1(aArg1) + { } + + nsresult operator () (T1& aArg1) const + { + aArg1 = mArg1; + + return NS_OK; + } + +private: + const T1& mArg1; +}; + +template <typename T1, typename T2> +class ConstantInitOp2 final +{ +public: + ConstantInitOp2(const T1& aArg1, const T2& aArg2) + : mArg1(aArg1) + , mArg2(aArg2) + { } + + nsresult operator () (T1& aArg1, T2& aArg2) const + { + aArg1 = mArg1; + aArg2 = mArg2; + + return NS_OK; + } + +private: + const T1& mArg1; + const T2& mArg2; +}; + +template <typename T1, typename T2, typename T3> +class ConstantInitOp3 final +{ +public: + ConstantInitOp3(const T1& aArg1, const T2& aArg2, const T3& aArg3) + : mArg1(aArg1) + , mArg2(aArg2) + , mArg3(aArg3) + { } + + nsresult operator () (T1& aArg1, T2& aArg2, T3& aArg3) const + { + aArg1 = mArg1; + aArg2 = mArg2; + aArg3 = mArg3; + + return NS_OK; + } + +private: + const T1& mArg1; + const T2& mArg2; + const T3& mArg3; +}; + +// |PDUInitOP| provides functionality for init operators that unpack PDUs. +class PDUInitOp +{ +protected: + PDUInitOp(DaemonSocketPDU& aPDU) + : mPDU(&aPDU) + { } + + DaemonSocketPDU& GetPDU() const + { + return *mPDU; // cannot be nullptr + } + + void WarnAboutTrailingData() const; + +private: + DaemonSocketPDU* mPDU; // Hold pointer to allow for constant instances +}; + +// |UnpackPDUInitOp| is a general-purpose init operator for all variants +// of |DaemonResultRunnable| and |DaemonNotificationRunnable|. The call +// operators of |UnpackPDUInitOp| unpack a PDU into the supplied +// arguments. +class UnpackPDUInitOp final : private PDUInitOp +{ +public: + UnpackPDUInitOp(DaemonSocketPDU& aPDU) + : PDUInitOp(aPDU) + { } + + nsresult operator () () const + { + WarnAboutTrailingData(); + return NS_OK; + } + + template<typename T1> + nsresult operator () (T1& aArg1) const + { + nsresult rv = UnpackPDU(GetPDU(), aArg1); + if (NS_FAILED(rv)) { + return rv; + } + WarnAboutTrailingData(); + return NS_OK; + } + + template<typename T1, typename T2> + nsresult operator () (T1& aArg1, T2& aArg2) const + { + DaemonSocketPDU& pdu = GetPDU(); + + nsresult rv = UnpackPDU(pdu, aArg1); + if (NS_FAILED(rv)) { + return rv; + } + rv = UnpackPDU(pdu, aArg2); + if (NS_FAILED(rv)) { + return rv; + } + WarnAboutTrailingData(); + return NS_OK; + } + + template<typename T1, typename T2, typename T3> + nsresult operator () (T1& aArg1, T2& aArg2, T3& aArg3) const + { + DaemonSocketPDU& pdu = GetPDU(); + + nsresult rv = UnpackPDU(pdu, aArg1); + if (NS_FAILED(rv)) { + return rv; + } + rv = UnpackPDU(pdu, aArg2); + if (NS_FAILED(rv)) { + return rv; + } + rv = UnpackPDU(pdu, aArg3); + if (NS_FAILED(rv)) { + return rv; + } + WarnAboutTrailingData(); + return NS_OK; + } + + template<typename T1, typename T2, typename T3, typename T4> + nsresult operator () (T1& aArg1, T2& aArg2, T3& aArg3, T4& aArg4) const + { + DaemonSocketPDU& pdu = GetPDU(); + + nsresult rv = UnpackPDU(pdu, aArg1); + if (NS_FAILED(rv)) { + return rv; + } + rv = UnpackPDU(pdu, aArg2); + if (NS_FAILED(rv)) { + return rv; + } + rv = UnpackPDU(pdu, aArg3); + if (NS_FAILED(rv)) { + return rv; + } + rv = UnpackPDU(pdu, aArg4); + if (NS_FAILED(rv)) { + return rv; + } + WarnAboutTrailingData(); + return NS_OK; + } + + template<typename T1, typename T2, typename T3, typename T4, typename T5> + nsresult operator () (T1& aArg1, T2& aArg2, T3& aArg3, T4& aArg4, + T5& aArg5) const + { + DaemonSocketPDU& pdu = GetPDU(); + + nsresult rv = UnpackPDU(pdu, aArg1); + if (NS_FAILED(rv)) { + return rv; + } + rv = UnpackPDU(pdu, aArg2); + if (NS_FAILED(rv)) { + return rv; + } + rv = UnpackPDU(pdu, aArg3); + if (NS_FAILED(rv)) { + return rv; + } + rv = UnpackPDU(pdu, aArg4); + if (NS_FAILED(rv)) { + return rv; + } + rv = UnpackPDU(pdu, aArg5); + if (NS_FAILED(rv)) { + return rv; + } + WarnAboutTrailingData(); + return NS_OK; + } + + template<typename T1, typename T2, typename T3, typename T4, + typename T5, typename T6> + nsresult operator () (T1& aArg1, T2& aArg2, T3& aArg3, T4& aArg4, + T5& aArg5, T6& aArg6) const + { + DaemonSocketPDU& pdu = GetPDU(); + + nsresult rv = UnpackPDU(pdu, aArg1); + if (NS_FAILED(rv)) { + return rv; + } + rv = UnpackPDU(pdu, aArg2); + if (NS_FAILED(rv)) { + return rv; + } + rv = UnpackPDU(pdu, aArg3); + if (NS_FAILED(rv)) { + return rv; + } + rv = UnpackPDU(pdu, aArg4); + if (NS_FAILED(rv)) { + return rv; + } + rv = UnpackPDU(pdu, aArg5); + if (NS_FAILED(rv)) { + return rv; + } + rv = UnpackPDU(pdu, aArg6); + if (NS_FAILED(rv)) { + return rv; + } + WarnAboutTrailingData(); + return NS_OK; + } + + template<typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7> + nsresult operator () (T1& aArg1, T2& aArg2, T3& aArg3, T4& aArg4, + T5& aArg5, T6& aArg6, T7& aArg7) const + { + DaemonSocketPDU& pdu = GetPDU(); + + nsresult rv = UnpackPDU(pdu, aArg1); + if (NS_FAILED(rv)) { + return rv; + } + rv = UnpackPDU(pdu, aArg2); + if (NS_FAILED(rv)) { + return rv; + } + rv = UnpackPDU(pdu, aArg3); + if (NS_FAILED(rv)) { + return rv; + } + rv = UnpackPDU(pdu, aArg4); + if (NS_FAILED(rv)) { + return rv; + } + rv = UnpackPDU(pdu, aArg5); + if (NS_FAILED(rv)) { + return rv; + } + rv = UnpackPDU(pdu, aArg6); + if (NS_FAILED(rv)) { + return rv; + } + rv = UnpackPDU(pdu, aArg7); + if (NS_FAILED(rv)) { + return rv; + } + WarnAboutTrailingData(); + return NS_OK; + } + + template<typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8> + nsresult operator () (T1& aArg1, T2& aArg2, T3& aArg3, T4& aArg4, + T5& aArg5, T6& aArg6, T7& aArg7, T8& aArg8) const + { + DaemonSocketPDU& pdu = GetPDU(); + + nsresult rv = UnpackPDU(pdu, aArg1); + if (NS_FAILED(rv)) { + return rv; + } + rv = UnpackPDU(pdu, aArg2); + if (NS_FAILED(rv)) { + return rv; + } + rv = UnpackPDU(pdu, aArg3); + if (NS_FAILED(rv)) { + return rv; + } + rv = UnpackPDU(pdu, aArg4); + if (NS_FAILED(rv)) { + return rv; + } + rv = UnpackPDU(pdu, aArg5); + if (NS_FAILED(rv)) { + return rv; + } + rv = UnpackPDU(pdu, aArg6); + if (NS_FAILED(rv)) { + return rv; + } + rv = UnpackPDU(pdu, aArg7); + if (NS_FAILED(rv)) { + return rv; + } + rv = UnpackPDU(pdu, aArg8); + if (NS_FAILED(rv)) { + return rv; + } + WarnAboutTrailingData(); + return NS_OK; + } + + template<typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9> + nsresult operator () (T1& aArg1, T2& aArg2, T3& aArg3, T4& aArg4, + T5& aArg5, T6& aArg6, T7& aArg7, T8& aArg8, + T9& aArg9) const + { + DaemonSocketPDU& pdu = GetPDU(); + + nsresult rv = UnpackPDU(pdu, aArg1); + if (NS_FAILED(rv)) { + return rv; + } + rv = UnpackPDU(pdu, aArg2); + if (NS_FAILED(rv)) { + return rv; + } + rv = UnpackPDU(pdu, aArg3); + if (NS_FAILED(rv)) { + return rv; + } + rv = UnpackPDU(pdu, aArg4); + if (NS_FAILED(rv)) { + return rv; + } + rv = UnpackPDU(pdu, aArg5); + if (NS_FAILED(rv)) { + return rv; + } + rv = UnpackPDU(pdu, aArg6); + if (NS_FAILED(rv)) { + return rv; + } + rv = UnpackPDU(pdu, aArg7); + if (NS_FAILED(rv)) { + return rv; + } + rv = UnpackPDU(pdu, aArg8); + if (NS_FAILED(rv)) { + return rv; + } + rv = UnpackPDU(pdu, aArg9); + if (NS_FAILED(rv)) { + return rv; + } + WarnAboutTrailingData(); + return NS_OK; + } +}; + +} // namespace DaemonSocketPDUHelpers + +} // namespace ipc +} // namespace mozilla + +#endif // mozilla_ipc_DaemonSocketPDUHelpers_h diff --git a/ipc/hal/moz.build b/ipc/hal/moz.build new file mode 100644 index 000000000..e97963b1e --- /dev/null +++ b/ipc/hal/moz.build @@ -0,0 +1,27 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +EXPORTS.mozilla.ipc += [ + 'DaemonRunnables.h', + 'DaemonSocket.h', + 'DaemonSocketConnector.h', + 'DaemonSocketConsumer.h', + 'DaemonSocketMessageHandlers.h', + 'DaemonSocketPDU.h', + 'DaemonSocketPDUHelpers.h' +] + +UNIFIED_SOURCES += [ + 'DaemonSocket.cpp', + 'DaemonSocketConnector.cpp', + 'DaemonSocketConsumer.cpp', + 'DaemonSocketPDU.cpp', + 'DaemonSocketPDUHelpers.cpp' +] + +include('/ipc/chromium/chromium-config.mozbuild') + +FINAL_LIBRARY = 'xul' |