import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

950
ipc/hal/DaemonRunnables.h Normal file
View file

@ -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

263
ipc/hal/DaemonSocket.cpp Normal file
View file

@ -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);
}
}
}

63
ipc/hal/DaemonSocket.h Normal file
View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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()
{ }
}
}

View file

@ -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

View file

@ -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

202
ipc/hal/DaemonSocketPDU.cpp Normal file
View file

@ -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));
}
}
}

94
ipc/hal/DaemonSocketPDU.h Normal file
View file

@ -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

View file

@ -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

File diff suppressed because it is too large Load diff

27
ipc/hal/moz.build Normal file
View file

@ -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'