503 lines
14 KiB
C++
503 lines
14 KiB
C++
/*
|
|||
|
|
* SPDX-License-Identifier: LicenseRef-Unisoc-General-1.0
|
||
|
|
|
||
|
|
* Copyright 2016-2023 Unisoc (Shanghai) Technologies Co., Ltd
|
||
|
|
|
||
|
|
* Licensed under the Unisoc General Software License, version 1.0 (the License);
|
||
|
|
* you may not use this file except in compliance with the License. You may obtain a copy of the License at
|
||
|
|
|
||
|
|
* https://www.unisoc.com/en_us/license/UNISOC_GENERAL_LICENSE_V1.0-EN_US
|
||
|
|
|
||
|
|
* Software distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
|
* WITHOUT WARRANTIES OF ANY KIND, either express or implied.
|
||
|
|
* See the Unisoc General Software License, version 1.0 for more details.
|
||
|
|
*/
|
||
|
|
#include <Debug.h>
|
||
|
|
#include <Object.h>
|
||
|
|
#include "InternalError.h"
|
||
|
|
|
||
|
|
#include <cstring>
|
||
|
|
#include <map>
|
||
|
|
#include <dbus/dbus.h>
|
||
|
|
|
||
|
|
#include "MessageInternal.h"
|
||
|
|
#include "ConnectionInternal.h"
|
||
|
|
|
||
|
|
using namespace UDBus;
|
||
|
|
|
||
|
|
Object::Object(Connection &conn, const UDBus::Path &path, const char *service)
|
||
|
|
: mConn(conn),
|
||
|
|
mPath(path),
|
||
|
|
mService(service ? service : "")
|
||
|
|
{
|
||
|
|
}
|
||
|
|
|
||
|
|
Object::~Object()
|
||
|
|
{
|
||
|
|
}
|
||
|
|
|
||
|
|
struct UDbusObjectAdaptor::UDBusPrivate {
|
||
|
|
static void FuncStub_UnRegister(DBusConnection *, void *);
|
||
|
|
static DBusHandlerResult FuncStub_Message(DBusConnection *, DBusMessage *, void *);
|
||
|
|
};
|
||
|
|
|
||
|
|
static DBusObjectPathVTable _vtable = {
|
||
|
|
UDbusObjectAdaptor::UDBusPrivate::FuncStub_UnRegister,
|
||
|
|
UDbusObjectAdaptor::UDBusPrivate::FuncStub_Message,
|
||
|
|
nullptr, nullptr, nullptr, nullptr
|
||
|
|
};
|
||
|
|
|
||
|
|
typedef std::map<Path, UDbusObjectAdaptor *> UDbusObjectAdaptorTable;
|
||
|
|
static UDbusObjectAdaptorTable sAdaptorTable;
|
||
|
|
|
||
|
|
void UDbusObjectAdaptor::UDBusPrivate::FuncStub_UnRegister(DBusConnection *conn, void *data)
|
||
|
|
{
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
DBusHandlerResult UDbusObjectAdaptor::UDBusPrivate::FuncStub_Message(DBusConnection *, DBusMessage *dbusmsg, void *data)
|
||
|
|
{
|
||
|
|
UDbusObjectAdaptor *obj = static_cast<UDbusObjectAdaptor *>(data);
|
||
|
|
|
||
|
|
if (obj)
|
||
|
|
{
|
||
|
|
Message message(new Message::UDBusPrivate(dbusmsg));
|
||
|
|
|
||
|
|
ERROR("%s serial:%d %s -> %s", obj->GetPath().c_str(), message.Serial(),
|
||
|
|
message.Sender(), message.Destination());
|
||
|
|
|
||
|
|
return obj->HandleMessage(message) ? DBUS_HANDLER_RESULT_HANDLED : DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
ERROR("no obj registered")
|
||
|
|
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
UDbusObjectAdaptor *UDbusObjectAdaptor::FromPath(const UDBus::Path &path)
|
||
|
|
{
|
||
|
|
UDbusObjectAdaptorTable::iterator adapti = sAdaptorTable.find(path);
|
||
|
|
|
||
|
|
if (adapti != sAdaptorTable.end())
|
||
|
|
return adapti->second;
|
||
|
|
|
||
|
|
return nullptr;
|
||
|
|
}
|
||
|
|
|
||
|
|
UDbusObjectAdaptorPList UDbusObjectAdaptor::FromPathPrefix(const std::string &prefix)
|
||
|
|
{
|
||
|
|
UDbusObjectAdaptorPList ali;
|
||
|
|
|
||
|
|
UDbusObjectAdaptorTable::iterator adapti = sAdaptorTable.begin();
|
||
|
|
|
||
|
|
size_t plen = prefix.length();
|
||
|
|
|
||
|
|
while (adapti != sAdaptorTable.end()) {
|
||
|
|
if (!strncmp(adapti->second->GetPath().c_str(), prefix.c_str(), plen))
|
||
|
|
ali.push_back(adapti->second);
|
||
|
|
|
||
|
|
++adapti;
|
||
|
|
}
|
||
|
|
|
||
|
|
return ali;
|
||
|
|
}
|
||
|
|
|
||
|
|
ObjectPathList UDbusObjectAdaptor::ChildNodesFromPrefix(const std::string &prefix)
|
||
|
|
{
|
||
|
|
ObjectPathList objPathlist;
|
||
|
|
|
||
|
|
UDbusObjectAdaptorTable::iterator iter = sAdaptorTable.begin();
|
||
|
|
|
||
|
|
size_t plen = prefix.length();
|
||
|
|
|
||
|
|
while (iter != sAdaptorTable.end())
|
||
|
|
{
|
||
|
|
if (iter->second->GetPath() != "/" &&
|
||
|
|
!strncmp(iter->second->GetPath().c_str(), prefix.c_str(), plen))
|
||
|
|
{
|
||
|
|
std::string path = iter->second->GetPath().substr(plen);
|
||
|
|
path = path.substr(0, path.find('/'));
|
||
|
|
objPathlist.push_back(path);
|
||
|
|
}
|
||
|
|
++iter;
|
||
|
|
}
|
||
|
|
|
||
|
|
objPathlist.sort();
|
||
|
|
objPathlist.unique();
|
||
|
|
|
||
|
|
return objPathlist;
|
||
|
|
}
|
||
|
|
|
||
|
|
UDbusObjectAdaptor::UDbusObjectAdaptor(Connection &Connection, const UDBus::Path &path)
|
||
|
|
: Object(Connection, path, Connection.UniqueName()), mEflag(USE_EXCEPTIONS)
|
||
|
|
{
|
||
|
|
RegisterObj();
|
||
|
|
}
|
||
|
|
|
||
|
|
UDbusObjectAdaptor::UDbusObjectAdaptor(Connection &Connection, const UDBus::Path &path, RegistrationTime rtime)
|
||
|
|
: Object(Connection, path, Connection.UniqueName()), mEflag(USE_EXCEPTIONS)
|
||
|
|
{
|
||
|
|
if (rtime == REGISTER_NOW)
|
||
|
|
RegisterObj();
|
||
|
|
}
|
||
|
|
|
||
|
|
UDbusObjectAdaptor::UDbusObjectAdaptor(Connection &Connection, const UDBus::Path &path, RegistrationTime rtime, ExceptionsFlag eflag)
|
||
|
|
: Object(Connection, path, Connection.UniqueName()), mEflag(eflag)
|
||
|
|
{
|
||
|
|
if (rtime == REGISTER_NOW)
|
||
|
|
RegisterObj();
|
||
|
|
}
|
||
|
|
|
||
|
|
UDbusObjectAdaptor::~UDbusObjectAdaptor()
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
UnRegisterObj();
|
||
|
|
}
|
||
|
|
catch(UDBusError &e)
|
||
|
|
{
|
||
|
|
DEBUG("Error{%s}", e.what());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void UDbusObjectAdaptor::RegisterObj()
|
||
|
|
{
|
||
|
|
DEBUG("registering local object %s", GetPath().c_str());
|
||
|
|
|
||
|
|
if (GetConn().mPrivate->Connection == nullptr)
|
||
|
|
{
|
||
|
|
throw ErrorInvalidArgs("nullptr connection");
|
||
|
|
}
|
||
|
|
else if (GetPath().c_str() == nullptr)
|
||
|
|
{
|
||
|
|
throw ErrorInvalidArgs("nullptr path");
|
||
|
|
}
|
||
|
|
else if (GetPath().c_str()[0] != '/')
|
||
|
|
{
|
||
|
|
std::string message = "Path must start with '/': " + GetPath();
|
||
|
|
throw ErrorInvalidArgs(message.c_str());
|
||
|
|
}
|
||
|
|
|
||
|
|
DBusError error = DBUS_ERROR_INIT;
|
||
|
|
if (!dbus_connection_try_register_object_path(
|
||
|
|
GetConn().mPrivate->Connection, GetPath().c_str(), &_vtable, this,
|
||
|
|
&error))
|
||
|
|
{
|
||
|
|
if (dbus_error_has_name(&error, DBUS_ERROR_OBJECT_PATH_IN_USE))
|
||
|
|
{
|
||
|
|
dbus_error_free(&error);
|
||
|
|
throw ErrorObjectPathInUse("unable to register object path");
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
dbus_error_free(&error);
|
||
|
|
throw ErrorNoMemory("unable to register object path");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
sAdaptorTable[GetPath()] = this;
|
||
|
|
}
|
||
|
|
|
||
|
|
void UDbusObjectAdaptor::UnRegisterObj()
|
||
|
|
{
|
||
|
|
if (!IsRegistered())
|
||
|
|
return;
|
||
|
|
|
||
|
|
sAdaptorTable.erase(GetPath());
|
||
|
|
|
||
|
|
DEBUG("unregistering local object %s", GetPath().c_str());
|
||
|
|
|
||
|
|
if (GetConn().mPrivate->Connection == nullptr) {
|
||
|
|
throw ErrorInvalidArgs("nullptr connection");
|
||
|
|
} else if (GetPath().c_str() == nullptr) {
|
||
|
|
throw ErrorInvalidArgs("nullptr path");
|
||
|
|
} else if (GetPath().c_str()[0] != '/') {
|
||
|
|
std::string message = "Path must start with '/': " + GetPath();
|
||
|
|
throw ErrorInvalidArgs(message.c_str());
|
||
|
|
}
|
||
|
|
|
||
|
|
dbus_connection_unregister_object_path(GetConn().mPrivate->Connection, GetPath().c_str());
|
||
|
|
}
|
||
|
|
|
||
|
|
bool UDbusObjectAdaptor::IsRegistered()
|
||
|
|
{
|
||
|
|
return sAdaptorTable.find(GetPath()) != sAdaptorTable.end();
|
||
|
|
}
|
||
|
|
|
||
|
|
void UDbusObjectAdaptor::EmitSignalInternal(SignalMessage &sig)
|
||
|
|
{
|
||
|
|
sig.SetPath(GetPath().c_str());
|
||
|
|
GetConn().Send(sig);
|
||
|
|
}
|
||
|
|
|
||
|
|
struct UDbusReturnLaterError {
|
||
|
|
const UDBus::Tag *tag;
|
||
|
|
};
|
||
|
|
|
||
|
|
// for dbus method_call message
|
||
|
|
bool UDbusObjectAdaptor::HandleMessage(const Message &msg)
|
||
|
|
{
|
||
|
|
switch (msg.Type()) {
|
||
|
|
case DBUS_MESSAGE_TYPE_METHOD_CALL: {
|
||
|
|
const CallMessage &cmsg = reinterpret_cast<const CallMessage &>(msg);
|
||
|
|
const char* member = cmsg.GetMember();
|
||
|
|
const char* interface = cmsg.GetInterface();
|
||
|
|
|
||
|
|
DEBUG(" invoking method %s.%s", interface, member);
|
||
|
|
|
||
|
|
UDbusInterfaceAdaptor *interfaceProxy = FindInterface(interface);
|
||
|
|
if (interfaceProxy) {
|
||
|
|
if (mEflag == AVOID_EXCEPTIONS) {
|
||
|
|
Message ret = interfaceProxy->DispatchMethod(cmsg);
|
||
|
|
if (cmsg.isSync == false)
|
||
|
|
return true;
|
||
|
|
Tag *tag = ret.GetTag();
|
||
|
|
if (tag) {
|
||
|
|
mContinuations[tag] =
|
||
|
|
new UDbusContinuation(GetConn(), cmsg, tag);
|
||
|
|
} else {
|
||
|
|
GetConn().Send(ret);
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
try {
|
||
|
|
Message ret = interfaceProxy->DispatchMethod(cmsg);
|
||
|
|
if (cmsg.isSync == false)
|
||
|
|
{
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
GetConn().Send(ret);
|
||
|
|
} catch(UDBusError &e) {
|
||
|
|
UDbusErrorMessage em(cmsg, e.GetName(), e.message());
|
||
|
|
GetConn().Send(em);
|
||
|
|
} catch(UDbusReturnLaterError &UDbusRle) {
|
||
|
|
mContinuations[UDbusRle.tag] = new UDbusContinuation(GetConn(), cmsg, UDbusRle.tag);
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
} else {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
default: return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void UDbusObjectAdaptor::ReturnLater(const UDBus::Tag *tag)
|
||
|
|
{
|
||
|
|
UDbusReturnLaterError rle = { tag };
|
||
|
|
throw rle;
|
||
|
|
}
|
||
|
|
|
||
|
|
void UDbusObjectAdaptor::ReturnNow(UDbusContinuation *ret)
|
||
|
|
{
|
||
|
|
ret->mConn.Send(ret->mReturn);
|
||
|
|
|
||
|
|
ContinuationMap::iterator di = mContinuations.find(ret->mTag);
|
||
|
|
|
||
|
|
delete di->second;
|
||
|
|
|
||
|
|
mContinuations.erase(di);
|
||
|
|
}
|
||
|
|
|
||
|
|
void UDbusObjectAdaptor::ReturnError(UDbusContinuation *ret, const UDBusError error)
|
||
|
|
{
|
||
|
|
ret->mConn.Send(UDbusErrorMessage(ret->mCall, error.GetName(), error.message()));
|
||
|
|
|
||
|
|
ContinuationMap::iterator di = mContinuations.find(ret->mTag);
|
||
|
|
|
||
|
|
delete di->second;
|
||
|
|
|
||
|
|
mContinuations.erase(di);
|
||
|
|
}
|
||
|
|
|
||
|
|
bool UDbusObjectAdaptor::ReplyAsyncMessage(const CallMessage & cmsg, const Message &msg)
|
||
|
|
{
|
||
|
|
try {
|
||
|
|
GetConn().Send(msg);
|
||
|
|
} catch(UDBusError &e) {
|
||
|
|
UDbusErrorMessage em(msg, e.GetName(), e.message());
|
||
|
|
GetConn().Send(em);
|
||
|
|
} catch(UDbusReturnLaterError &UDbusRle) {
|
||
|
|
mContinuations[UDbusRle.tag] = new UDbusContinuation(GetConn(), cmsg, UDbusRle.tag);
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
UDbusObjectAdaptor::UDbusContinuation *UDbusObjectAdaptor::FindContinuation(const UDBus::Tag *tag)
|
||
|
|
{
|
||
|
|
ContinuationMap::iterator di = mContinuations.find(tag);
|
||
|
|
|
||
|
|
return di != mContinuations.end() ? di->second : nullptr;
|
||
|
|
}
|
||
|
|
|
||
|
|
UDbusObjectAdaptor::UDbusContinuation::UDbusContinuation(Connection &Connection, const CallMessage &call, const UDBus::Tag *tag)
|
||
|
|
: mConn(Connection), mCall(call), mReturn(mCall), mTag(tag)
|
||
|
|
{
|
||
|
|
mWriter = mReturn.Writer();
|
||
|
|
}
|
||
|
|
|
||
|
|
UDbusObjectProxy::UDbusObjectProxy(Connection &Connection, const UDBus::Path &path, const char *service)
|
||
|
|
: Object(Connection, path, service)
|
||
|
|
{
|
||
|
|
RegisterObj();
|
||
|
|
}
|
||
|
|
|
||
|
|
UDbusObjectProxy::~UDbusObjectProxy()
|
||
|
|
{
|
||
|
|
CancelPendingCalls();
|
||
|
|
try
|
||
|
|
{
|
||
|
|
UnRegisterObj();
|
||
|
|
}
|
||
|
|
catch(UDBusError &e)
|
||
|
|
{
|
||
|
|
DEBUG("Error{%s}", e.what());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void UDbusObjectProxy::CancelPendingCalls()
|
||
|
|
{
|
||
|
|
PendingCallList::const_iterator pi = mPendingCalls.begin();
|
||
|
|
while (pi != mPendingCalls.end()) {
|
||
|
|
(*pi)->Cancel();
|
||
|
|
delete *pi;
|
||
|
|
++pi;
|
||
|
|
}
|
||
|
|
mPendingCalls.clear();
|
||
|
|
}
|
||
|
|
|
||
|
|
void UDbusObjectProxy::_RemovePendingCall(PendingCall *pending)
|
||
|
|
{
|
||
|
|
PendingCallList::iterator iter = mPendingCalls.begin();
|
||
|
|
while (iter != mPendingCalls.end()) {
|
||
|
|
if (pending == *iter) {
|
||
|
|
mPendingCalls.erase(iter);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
++iter;
|
||
|
|
}
|
||
|
|
delete pending;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool UDbusObjectProxy::IsRegistered()
|
||
|
|
{
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
void UDbusObjectProxy::RegisterObj()
|
||
|
|
{
|
||
|
|
DEBUG("registering remote object %s", GetPath().c_str());
|
||
|
|
|
||
|
|
mFiltered = new Callback<UDbusObjectProxy, bool, const Message &>(this, &UDbusObjectProxy::HandleMessage);
|
||
|
|
|
||
|
|
GetConn().AddFilter(mFiltered);
|
||
|
|
|
||
|
|
InterfaceProxyTable::const_iterator interfaceProxy = mInterfaces.begin();
|
||
|
|
while (interfaceProxy != mInterfaces.end()) {
|
||
|
|
std::string im = "type='signal',interface='" + interfaceProxy->first + "',path='" + GetPath() + "'";
|
||
|
|
GetConn().AddMatch(im.c_str());
|
||
|
|
++interfaceProxy;
|
||
|
|
}
|
||
|
|
DEBUG("");
|
||
|
|
}
|
||
|
|
|
||
|
|
void UDbusObjectProxy::UnRegisterObj()
|
||
|
|
{
|
||
|
|
DEBUG("unregistering remote object %s", GetPath().c_str());
|
||
|
|
|
||
|
|
InterfaceProxyTable::const_iterator interfaceProxy = mInterfaces.begin();
|
||
|
|
while (interfaceProxy != mInterfaces.end()) {
|
||
|
|
std::string im = "type='signal',interface='" + interfaceProxy->first + "',path='" + GetPath() + "'";
|
||
|
|
GetConn().RmoveMatch(im.c_str());
|
||
|
|
++interfaceProxy;
|
||
|
|
}
|
||
|
|
GetConn().RemoveFilter(mFiltered);
|
||
|
|
}
|
||
|
|
|
||
|
|
Message UDbusObjectProxy::_InvokeMethod(CallMessage &call)
|
||
|
|
{
|
||
|
|
if (call.GetPath() == nullptr)
|
||
|
|
call.SetPath(GetPath().c_str());
|
||
|
|
|
||
|
|
if (call.Destination() == nullptr)
|
||
|
|
call.SetDestination(GetService().c_str());
|
||
|
|
|
||
|
|
return GetConn().SendBlocking(call);
|
||
|
|
}
|
||
|
|
|
||
|
|
Message UDbusObjectProxy::_InvokeMethodWaitTime(CallMessage &call, int timeout)
|
||
|
|
{
|
||
|
|
if (call.GetPath() == nullptr)
|
||
|
|
call.SetPath(GetPath().c_str());
|
||
|
|
|
||
|
|
if (call.Destination() == nullptr)
|
||
|
|
call.SetDestination(GetService().c_str());
|
||
|
|
|
||
|
|
return GetConn().SendBlocking(call, timeout);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
bool UDbusObjectProxy::_InvokeMethodNoReply(CallMessage &call)
|
||
|
|
{
|
||
|
|
if (call.GetPath() == nullptr)
|
||
|
|
call.SetPath(GetPath().c_str());
|
||
|
|
|
||
|
|
if (call.Destination() == nullptr)
|
||
|
|
call.SetDestination(GetService().c_str());
|
||
|
|
|
||
|
|
return GetConn().Send(call);
|
||
|
|
}
|
||
|
|
|
||
|
|
// for dbus signal message
|
||
|
|
bool UDbusObjectProxy::HandleMessage(const Message &msg)
|
||
|
|
{
|
||
|
|
switch (msg.Type()) {
|
||
|
|
case DBUS_MESSAGE_TYPE_SIGNAL: {
|
||
|
|
const SignalMessage &smsg = reinterpret_cast<const SignalMessage &>(msg);
|
||
|
|
const char* interface = smsg.GetInterface();
|
||
|
|
const char* member = smsg.GetMember();
|
||
|
|
const char* objpath = smsg.GetPath();
|
||
|
|
|
||
|
|
if (objpath != GetPath()) return false;
|
||
|
|
|
||
|
|
ERROR("filtered signal %s(in %s) from %s to object %s", member, interface, msg.Sender(), objpath);
|
||
|
|
|
||
|
|
InterfaceProxy* interfaceProxy = FindInterface(interface);
|
||
|
|
if (interfaceProxy) {
|
||
|
|
bool done = true;
|
||
|
|
try
|
||
|
|
{
|
||
|
|
done = interfaceProxy->DispatchSignal(smsg);
|
||
|
|
}
|
||
|
|
catch(UDBusError &e)
|
||
|
|
{
|
||
|
|
ERROR("Error{%s}", e.what());
|
||
|
|
done = true;
|
||
|
|
}
|
||
|
|
return done;
|
||
|
|
} else {
|
||
|
|
ERROR("interfaceProxy is null return false");
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
default:
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
PendingCall *UDbusObjectProxy::_InvokeMethodAsync(CallMessage &call, int timeout)
|
||
|
|
{
|
||
|
|
if (call.GetPath() == nullptr)
|
||
|
|
call.SetPath(GetPath().c_str());
|
||
|
|
|
||
|
|
if (call.Destination() == nullptr)
|
||
|
|
call.SetDestination(GetService().c_str());
|
||
|
|
|
||
|
|
PendingCall *pending = GetConn().SendAsync(call, timeout);
|
||
|
|
|
||
|
|
mPendingCalls.push_back(pending);
|
||
|
|
return pending;
|
||
|
|
}
|