/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * 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/.
*/
// Simplest ways to check whether we have Bluez 5+ is to check // that we can obtain adapters using the new interfaces. // The first two error checks however don't tell us anything as they should // succeed as long as dbus is working correctly.
pMsg = DBusObject( "org.bluez", "/", "org.freedesktop.DBus.ObjectManager" ).getMethodCall( "GetManagedObjects" ); if (!pMsg)
{
SAL_INFO("sdremote.bluetooth", "No GetManagedObjects call created"); returnfalse;
}
pMsg = sendUnrefAndWaitForReply( pConnection, pMsg ); if (!pMsg)
{
SAL_INFO("sdremote.bluetooth", "No reply received"); returnfalse;
}
// If dbus is working correctly and we aren't on bluez 5 this is where we // should actually get the error. if (dbus_message_get_error_name( pMsg ))
{
SAL_INFO( "sdremote.bluetooth", "GetManagedObjects call failed with \""
<< dbus_message_get_error_name( pMsg )
<< "\" -- we don't seem to have Bluez 5 available"); returnfalse;
}
SAL_INFO("sdremote.bluetooth", "GetManagedObjects call seems to have succeeded -- we must be on Bluez 5");
dbus_message_unref(pMsg); returntrue;
}
static std::unique_ptr<DBusObject>
getBluez5Adapter(DBusConnection *pConnection)
{
DBusMessage *pMsg; // This returns a list of objects where we need to find the first // org.bluez.Adapter1 .
pMsg = DBusObject( "org.bluez", "/", "org.freedesktop.DBus.ObjectManager" ).getMethodCall( "GetManagedObjects" ); if (!pMsg) return nullptr;
DBusMessageIter aObjectIterator; if (pMsg && dbus_message_iter_init(pMsg, &aObjectIterator))
{ if (DBUS_TYPE_ARRAY == dbus_message_iter_get_arg_type(&aObjectIterator))
{
DBusMessageIter aObject;
dbus_message_iter_recurse(&aObjectIterator, &aObject); do
{ if (DBUS_TYPE_DICT_ENTRY == dbus_message_iter_get_arg_type(&aObject))
{
DBusMessageIter aContainerIter;
dbus_message_iter_recurse(&aObject, &aContainerIter); char *pPath = nullptr; do
{ if (DBUS_TYPE_OBJECT_PATH == dbus_message_iter_get_arg_type(&aContainerIter))
{
dbus_message_iter_get_basic(&aContainerIter, &pPath);
SAL_INFO( "sdremote.bluetooth", "Something retrieved: '"
<< pPath << "' '");
} elseif (DBUS_TYPE_ARRAY == dbus_message_iter_get_arg_type(&aContainerIter))
{
DBusMessageIter aInnerIter;
dbus_message_iter_recurse(&aContainerIter, &aInnerIter); do
{ if (DBUS_TYPE_DICT_ENTRY == dbus_message_iter_get_arg_type(&aInnerIter))
{
DBusMessageIter aInnerInnerIter;
dbus_message_iter_recurse(&aInnerIter, &aInnerInnerIter); do
{ if (DBUS_TYPE_STRING == dbus_message_iter_get_arg_type(&aInnerInnerIter))
{ char* pMessage;
dbus_message_iter_get_basic(&aInnerInnerIter, &pMessage); if (pMessage == std::string_view("org.bluez.Adapter1"))
{
dbus_message_unref(pMsg); if (pPath)
{ return std::make_unique<DBusObject>( "org.bluez", pPath, pInterfaceType );
}
assert(false); // We should already have pPath provided for us.
}
}
} while (dbus_message_iter_next(&aInnerInnerIter));
}
} while (dbus_message_iter_next(&aInnerIter));
}
} while (dbus_message_iter_next(&aContainerIter));
}
} while (dbus_message_iter_next(&aObject));
}
dbus_message_unref(pMsg);
}
// org.bluez.manager only exists for bluez 4. // getMethodCall should return NULL if there is any issue e.g. the // if org.bluez.manager doesn't exist.
pMsg = DBusObject( "org.bluez", "/", "org.bluez.Manager" ).getMethodCall( "DefaultAdapter" );
if (!pMsg)
{
SAL_WARN("sdremote.bluetooth", "Couldn't retrieve DBusObject for DefaultAdapter"); return nullptr;
}
// We ignore the uint de-registration handle we get back: // bluez will clean us up automatically on exit
returntrue;
}
staticvoid
bluezCreateAttachListeningSocket( GMainContext *pContext, GPollFD *pSocketFD )
{ int nSocket;
pSocketFD->fd = -1;
if( ( nSocket = socket( AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM ) ) < 0 )
{
SAL_WARN( "sdremote.bluetooth", "failed to open bluetooth socket with error " << nSocket ); return;
}
sockaddr_rc aAddr; // Initialize whole structure. Mainly to appease valgrind, which // doesn't know about the padding at the end of sockaddr_rc which // it will dutifully check for definedness. But also the standard // definition of BDADDR_ANY is unusable in C++ code, so just use // memset to set aAddr.rc_bdaddr to 0.
memset( &aAddr, 0, sizeof( aAddr ) );
aAddr.rc_family = AF_BLUETOOTH;
aAddr.rc_channel = 5;
int a; if ( ( a = bind( nSocket, reinterpret_cast<sockaddr*>(&aAddr), sizeof(aAddr) ) ) < 0 ) {
SAL_WARN( "sdremote.bluetooth", "bind failed with error" << a );
close( nSocket ); return;
}
if ( ( a = listen( nSocket, 1 ) ) < 0 )
{
SAL_WARN( "sdremote.bluetooth", "listen failed with error" << a );
close( nSocket ); return;
}
/* * Bluez 4 uses custom methods for setting properties, whereas Bluez 5+ * implements properties using the generic "org.freedesktop.DBus.Properties" * interface -- hence we have a specific Bluez 4 function to deal with the * old style of reading properties.
*/ staticbool
getBluez4BooleanProperty( DBusConnection *pConnection, DBusObject *pAdapter, constchar *pPropertyName, bool *pBoolean )
{
*pBoolean = false;
if (!dbus_message_iter_next(&it))
SAL_WARN("sdremote.bluetooth", "not enough parameters passed");
// DBUS_TYPE_UNIX_FD == 'h' -- doesn't exist in older versions // of dbus (< 1.3?) hence defined manually for now if ('h' == dbus_message_iter_get_arg_type(&it))
{
int nDescriptor;
dbus_message_iter_get_basic(&it, &nDescriptor);
std::vector<Communicator*>* pCommunicators = static_cast<std::vector<Communicator*>*>(user_data);
// Bluez gives us non-blocking sockets, but our code relies // on blocking behaviour.
(void)fcntl(nDescriptor, F_SETFL, fcntl(nDescriptor, F_GETFL) & ~O_NONBLOCK);
// For some reason an (empty?) reply is expected.
DBusMessage* pRet = dbus_message_new_method_return(pMessage);
dbus_connection_send(pConnection, pRet, nullptr);
dbus_message_unref(pRet);
// We could read the remote profile version and features here // (i.e. they are provided as part of the DBusMessage), // however for us they are irrelevant (as our protocol handles // equivalent functionality independently of whether we're on // bluetooth or normal network connection). return DBUS_HANDLER_RESULT_HANDLED;
}
} elseif (dbus_message_get_member(pMessage) == std::string_view("RequestDisconnection"))
{ return DBUS_HANDLER_RESULT_HANDLED;
}
}
SAL_WARN("sdremote.bluetooth", "Couldn't handle message correctly."); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
// dbus_connection_try_register_object_path could be used but only exists for // dbus >= 1.2 -- we really shouldn't be trying this twice in any case. // (dbus_connection_try_register_object_path also returns an error with more // information which could be useful for debugging purposes.)
bErr = !dbus_connection_register_object_path(pConnection, "/org/libreoffice/bluez/profile1", &aVTable, pCommunicators);
if (bErr)
{
SAL_WARN("sdremote.bluetooth", "Failed to register Bluez 5 Profile1 callback, bluetooth won't work.");
}
// Other properties that we could set (but don't, since they appear // to be useless for us): // "Service": "0x1101" (not needed, but we used to have it in the manually defined profile). // "Role": setting this to "server" breaks things, although we think we're a server? // "Channel": seems to be dealt with automatically (but we used to use 5 in the manual profile).
dbus_error_free(&aError); if (pMsg)
dbus_message_unref(pMsg);
dbus_connection_flush(pConnection);
return bSuccess;
}
#endif// LINUX_BLUETOOTH
BluetoothServer::BluetoothServer( std::vector<Communicator*>* pCommunicators )
: meWasDiscoverable( UNKNOWN ),
mpCommunicators( pCommunicators )
{ #ifdef LINUX_BLUETOOTH // D-Bus requires the following in order to be thread-safe (and we // potentially access D-Bus from different threads in different places of // the code base): if (!dbus_threads_init_default()) { throw std::bad_alloc();
}
// We have to have all our clients shut otherwise we can't // re-bind to the same port number it appears. void BluetoothServer::cleanupCommunicators()
{
::osl::MutexGuard aGuard(RemoteServer::sDataMutex); for (auto& rpCommunicator : *mpCommunicators)
rpCommunicator->forceClose(); // the hope is that all the threads then terminate cleanly and // clean themselves up.
}
// We don't need to listen to adapter changes anymore -- profile // registration is done globally for the entirety of bluez, so we only // need adapters when setting discoverability, which can be done // dynamically without the need to listen for changes.
// TODO: exit on SD deinit // Probably best to do that in SdModule::~SdModule? while (true)
{
aDBusFD.revents = 0;
g_main_context_iteration( mpImpl->mpContext, true ); if( aDBusFD.revents )
{
dbus_connection_read_write( pConnection, 0 ); while (DBUS_DISPATCH_DATA_REMAINS == dbus_connection_get_dispatch_status( pConnection ))
dbus_connection_dispatch( pConnection );
} if ((false)) break; // silence Clang -Wunreachable-code after loop (TODO: proper // fix?)
}
unregisterBluez5Profile( pConnection );
g_main_context_unref( mpImpl->mpContext );
mpImpl->mpConnection = nullptr;
mpImpl->mpContext = nullptr; return;
}
// Otherwise we could be on Bluez 4 and continue as usual.
mpImpl->maBluezVersion = Impl::BluezVersion::BLUEZ4;
// Try to setup the default adapter, otherwise wait for add/remove signal
mpImpl->mpService = registerWithDefaultAdapter( pConnection ); // listen for connection state and power changes - we need to close // and re-create our socket code on suspend / resume, enable/disable
DBusError aError;
dbus_error_init( &aError );
dbus_bus_add_match( pConnection, "type='signal',interface='org.bluez.Manager'", &aError );
dbus_connection_flush( pConnection );
// Try to setup the default adapter, otherwise wait for add/remove signal
mpImpl->mpService = registerWithDefaultAdapter( pConnection );
// poll on our bluetooth socket - if we can.
GPollFD aSocketFD; if( mpImpl->mpService )
bluezCreateAttachListeningSocket( mpImpl->mpContext, &aSocketFD );
SOCKADDR_BTH aName; int aNameSize = sizeof(aName);
getsockname( aSocket, reinterpret_cast<SOCKADDR*>(&aName), &aNameSize ); // Retrieve the local address and port
// Service class ID list
[NSArray arrayWithObject:
[IOBluetoothSDPUUID uuid16: kBluetoothSDPUUID16ServiceClassSerialPort]],
@"0001 - ServiceClassIDList",
// Protocol descriptor list
[NSArray arrayWithObjects:
[NSArray arrayWithObject: [IOBluetoothSDPUUID uuid16: kBluetoothSDPUUID16L2CAP]],
[NSArray arrayWithObjects:
[IOBluetoothSDPUUID uuid16: kBluetoothL2CAPPSMRFCOMM],
[NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt: 1],
@"DataElementSize",
[NSNumber numberWithInt: 1],
@"DataElementType",
[NSNumber numberWithInt: 5], // RFCOMM port number, will be replaced if necessary automatically
@"DataElementValue",
nil],
nil],
nil],
@"0004 - Protocol descriptor list",
// Browse group list
[NSArray arrayWithObject:
[IOBluetoothSDPUUID uuid16: kBluetoothSDPUUID16ServiceClassPublicBrowseGroup]],
@"0005 - BrowseGroupList",
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.