fdo#74697 Add Bluez 5 support for impress remote.
[LibreOffice.git] / sd / source / ui / remotecontrol / BluetoothServer.cxx
blob0367b510ccefc93848af6dfe8d63697ea1a9c23c
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #include "BluetoothServer.hxx"
12 #include <iostream>
13 #include <iomanip>
15 #include <boost/scoped_ptr.hpp>
17 #include <sal/log.hxx>
19 #ifdef LINUX_BLUETOOTH
20 #include <glib.h>
21 #include <dbus/dbus.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <sys/unistd.h>
25 #include <sys/socket.h>
26 #include <bluetooth/bluetooth.h>
27 #include <bluetooth/rfcomm.h>
28 #include "BluetoothServiceRecord.hxx"
29 #include "BufferedStreamSocket.hxx"
30 #endif
32 #ifdef WIN32
33 // LO vs WinAPI conflict
34 #undef WB_LEFT
35 #undef WB_RIGHT
36 #include <winsock2.h>
37 #include <ws2bth.h>
38 #include "BufferedStreamSocket.hxx"
39 #endif
41 #ifdef MACOSX
42 #include <osl/conditn.hxx> // Include this early to avoid error as check() gets defined by some SDK header to empty
43 #include <premac.h>
44 #if MACOSX_SDK_VERSION >= 1070
45 #import <IOBluetooth/IOBluetooth.h>
46 #else
47 #import <CoreFoundation/CoreFoundation.h>
48 #import <IOBluetooth/IOBluetoothUtilities.h>
49 #import <IOBluetooth/objc/IOBluetoothSDPUUID.h>
50 #import <IOBluetooth/objc/IOBluetoothSDPServiceRecord.h>
51 #endif
52 #include <postmac.h>
53 #import "OSXBluetooth.h"
54 #include "OSXBluetoothWrapper.hxx"
55 #endif
57 #ifdef __MINGW32__
58 // Value taken from http://msdn.microsoft.com/en-us/library/windows/desktop/ms738518%28v=vs.85%29.aspx
59 #define NS_BTH 16
60 #endif
62 #include "Communicator.hxx"
64 using namespace sd;
66 #ifdef LINUX_BLUETOOTH
68 struct DBusObject {
69 OString maBusName;
70 OString maPath;
71 OString maInterface;
73 DBusObject() { }
74 DBusObject( const char *pBusName, const char *pPath, const char *pInterface )
75 : maBusName( pBusName ), maPath( pPath ), maInterface( pInterface ) { }
77 DBusMessage *getMethodCall( const char *pName )
79 return dbus_message_new_method_call( maBusName.getStr(), maPath.getStr(),
80 maInterface.getStr(), pName );
82 DBusObject *cloneForInterface( const char *pInterface )
84 DBusObject *pObject = new DBusObject();
86 pObject->maBusName = maBusName;
87 pObject->maPath = maPath;
88 pObject->maInterface = pInterface;
90 return pObject;
94 static DBusObject* getBluez5Adapter(DBusConnection *pConnection);
96 struct sd::BluetoothServer::Impl {
97 // the glib mainloop running in the thread
98 GMainContext *mpContext;
99 DBusConnection *mpConnection;
100 DBusObject *mpService;
101 volatile bool mbExitMainloop;
102 enum BluezVersion { BLUEZ4, BLUEZ5, UNKNOWN };
103 BluezVersion maBluezVersion;
105 Impl()
106 : mpContext( g_main_context_new() )
107 , mpConnection( NULL )
108 , mpService( NULL )
109 , mbExitMainloop( false )
110 , maBluezVersion( UNKNOWN )
113 DBusObject *getAdapter()
115 if (mpService)
117 DBusObject* pAdapter = mpService->cloneForInterface( "org.bluez.Adapter" );
118 return pAdapter;
120 else if (spServer->mpImpl->maBluezVersion == BLUEZ5)
122 return getBluez5Adapter(mpConnection);
124 else
126 return NULL;
131 static DBusConnection *
132 dbusConnectToNameOnBus()
134 DBusError aError;
135 DBusConnection *pConnection;
137 dbus_error_init( &aError );
139 pConnection = dbus_bus_get( DBUS_BUS_SYSTEM, &aError );
140 if( !pConnection || dbus_error_is_set( &aError ))
142 SAL_WARN( "sdremote.bluetooth", "failed to get dbus system bus: " << aError.message );
143 dbus_error_free( &aError );
144 return NULL;
147 return pConnection;
150 static DBusMessage *
151 sendUnrefAndWaitForReply( DBusConnection *pConnection, DBusMessage *pMsg )
153 DBusPendingCall *pPending = NULL;
155 if( !pMsg || !dbus_connection_send_with_reply( pConnection, pMsg, &pPending,
156 -1 /* default timeout */ ) )
158 SAL_WARN( "sdremote.bluetooth", "Memory allocation failed on message send" );
159 dbus_message_unref( pMsg );
160 return NULL;
162 dbus_connection_flush( pConnection );
163 dbus_message_unref( pMsg );
165 dbus_pending_call_block( pPending ); // block for reply
167 pMsg = dbus_pending_call_steal_reply( pPending );
168 if( !pMsg )
169 SAL_WARN( "sdremote.bluetooth", "no valid reply / timeout" );
171 dbus_pending_call_unref( pPending );
172 return pMsg;
175 static bool
176 isBluez5Available(DBusConnection *pConnection)
178 DBusMessage *pMsg;
180 // Simplest wasy to check whether we have Bluez 5+ is to check
181 // that we can obtain adapters using the new interfaces.
182 // The first two error checks however don't tell us anything as they should
183 // succeed as long as dbus is working correctly.
184 pMsg = DBusObject( "org.bluez", "/", "org.freedesktop.DBus.ObjectManager" ).getMethodCall( "GetManagedObjects" );
185 if (!pMsg)
187 SAL_INFO("sdremote.bluetooth", "No GetManagedObjects call created");
188 return false;
191 pMsg = sendUnrefAndWaitForReply( pConnection, pMsg );
192 if (!pMsg)
194 SAL_INFO("sdremote.bluetooth", "No reply received");
195 return false;
198 // If dbus is working correctly and we aren't on bluez 5 this is where we
199 // should actually get the error.
200 if (dbus_message_get_error_name( pMsg ))
202 SAL_INFO( "sdremote.bluetooth", "GetManagedObjects call failed with \""
203 << dbus_message_get_error_name( pMsg )
204 << "\" -- we don't seem to have Bluez 5 available");
205 return false;
207 SAL_INFO("sdremote.bluetooth", "GetManagedObjects call seems to have succeeded -- we must be on Bluez 5");
208 dbus_message_unref(pMsg);
209 return true;
213 static DBusObject*
214 getBluez5Adapter(DBusConnection *pConnection)
216 DBusMessage *pMsg;
217 // This returns a list of objects where we need to find the first
218 // org.bluez.Adapter1 .
219 pMsg = DBusObject( "org.bluez", "/", "org.freedesktop.DBus.ObjectManager" ).getMethodCall( "GetManagedObjects" );
220 if (!pMsg)
221 return NULL;
223 const gchar* pInterfaceType = "org.bluez.Adapter1";
225 pMsg = sendUnrefAndWaitForReply( pConnection, pMsg );
227 DBusMessageIter aObjectIterator;
228 if (pMsg && dbus_message_iter_init(pMsg, &aObjectIterator))
230 if (DBUS_TYPE_ARRAY == dbus_message_iter_get_arg_type(&aObjectIterator))
232 DBusMessageIter aObject;
233 dbus_message_iter_recurse(&aObjectIterator, &aObject);
236 if (DBUS_TYPE_DICT_ENTRY == dbus_message_iter_get_arg_type(&aObject))
238 DBusMessageIter aContainerIter;
239 dbus_message_iter_recurse(&aObject, &aContainerIter);
240 char *pPath = 0;
243 if (DBUS_TYPE_OBJECT_PATH == dbus_message_iter_get_arg_type(&aContainerIter))
245 dbus_message_iter_get_basic(&aContainerIter, &pPath);
246 SAL_INFO( "sdremote.bluetooth", "Something retrieved: '"
247 << pPath << "' '");
249 else if (DBUS_TYPE_ARRAY == dbus_message_iter_get_arg_type(&aContainerIter))
251 DBusMessageIter aInnerIter;
252 dbus_message_iter_recurse(&aContainerIter, &aInnerIter);
255 if (DBUS_TYPE_DICT_ENTRY == dbus_message_iter_get_arg_type(&aInnerIter))
257 DBusMessageIter aInnerInnerIter;
258 dbus_message_iter_recurse(&aInnerIter, &aInnerInnerIter);
261 if (DBUS_TYPE_STRING == dbus_message_iter_get_arg_type(&aInnerInnerIter))
263 char* pMessage;
265 dbus_message_iter_get_basic(&aInnerInnerIter, &pMessage);
266 if (OString(pMessage) == "org.bluez.Adapter1")
268 dbus_message_unref(pMsg);
269 if (pPath)
271 return new DBusObject( "org.bluez", pPath, pInterfaceType );
273 assert(false); // We should already have pPath provided for us.
277 while (dbus_message_iter_next(&aInnerInnerIter));
280 while (dbus_message_iter_next(&aInnerIter));
283 while (dbus_message_iter_next(&aContainerIter));
286 while (dbus_message_iter_next(&aObject));
288 dbus_message_unref(pMsg);
291 return NULL;
294 static DBusObject *
295 bluez4GetDefaultService( DBusConnection *pConnection )
297 DBusMessage *pMsg;
298 DBusMessageIter it;
299 const gchar* pInterfaceType = "org.bluez.Service";
301 // org.bluez.manager only exists for bluez 4.
302 // getMethodCall should return NULL if there is any issue e.g. the
303 // if org.bluez.manager doesn't exist.
304 pMsg = DBusObject( "org.bluez", "/", "org.bluez.Manager" ).getMethodCall( "DefaultAdapter" );
306 if (!pMsg)
308 SAL_WARN("sdremote.bluetooth", "Couldn't retrieve DBusObject for DefaultAdapter");
309 return NULL;
312 SAL_INFO("sdremote.bluetooth", "successfully retrieved org.bluez.Manager.DefaultAdapter, attempting to use.");
313 pMsg = sendUnrefAndWaitForReply( pConnection, pMsg );
315 if(!pMsg || !dbus_message_iter_init( pMsg, &it ) )
317 return NULL;
320 // This works for Bluez 4
321 if( DBUS_TYPE_OBJECT_PATH == dbus_message_iter_get_arg_type( &it ) )
323 const char *pObjectPath = NULL;
324 dbus_message_iter_get_basic( &it, &pObjectPath );
325 SAL_INFO( "sdremote.bluetooth", "DefaultAdapter retrieved: '"
326 << pObjectPath << "' '" << pInterfaceType << "'" );
327 dbus_message_unref( pMsg );
328 return new DBusObject( "org.bluez", pObjectPath, pInterfaceType );
330 // Some form of error, e.g. if we have bluez 5 we get a message that
331 // this method doesn't exist.
332 else if ( DBUS_TYPE_STRING == dbus_message_iter_get_arg_type( &it ) )
334 const char *pMessage = NULL;
335 dbus_message_iter_get_basic( &it, &pMessage );
336 SAL_INFO( "sdremote.bluetooth", "Error message: '"
337 << pMessage << "' '" << pInterfaceType << "'" );
339 else
341 SAL_INFO( "sdremote.bluetooth", "invalid type of reply to DefaultAdapter: '"
342 << (const char) dbus_message_iter_get_arg_type( &it ) << "'" );
344 dbus_message_unref(pMsg);
345 return NULL;
348 static bool
349 bluez4RegisterServiceRecord( DBusConnection *pConnection, DBusObject *pAdapter,
350 const char *pServiceRecord )
352 DBusMessage *pMsg;
353 DBusMessageIter it;
355 pMsg = pAdapter->getMethodCall( "AddRecord" );
356 dbus_message_iter_init_append( pMsg, &it );
357 dbus_message_iter_append_basic( &it, DBUS_TYPE_STRING, &pServiceRecord );
359 pMsg = sendUnrefAndWaitForReply( pConnection, pMsg );
361 if( !pMsg || !dbus_message_iter_init( pMsg, &it ) ||
362 dbus_message_iter_get_arg_type( &it ) != DBUS_TYPE_UINT32 )
364 SAL_WARN( "sdremote.bluetooth", "SDP registration failed" );
365 return false;
368 // We ignore the uint de-registration handle we get back:
369 // bluez will clean us up automatically on exit
371 return true;
374 static void
375 bluezCreateAttachListeningSocket( GMainContext *pContext, GPollFD *pSocketFD )
377 int nSocket;
379 pSocketFD->fd = -1;
381 if( ( nSocket = socket( AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM ) ) < 0 )
383 SAL_WARN( "sdremote.bluetooth", "failed to open bluetooth socket with error " << nSocket );
384 return;
387 sockaddr_rc aAddr;
388 aAddr.rc_family = AF_BLUETOOTH;
389 // BDADDR_ANY is broken, so use memset to set to 0.
390 memset( &aAddr.rc_bdaddr, 0, sizeof( aAddr.rc_bdaddr ) );
391 aAddr.rc_channel = 5;
393 int a;
394 if ( ( a = bind( nSocket, (sockaddr*) &aAddr, sizeof(aAddr) ) ) < 0 ) {
395 SAL_WARN( "sdremote.bluetooth", "bind failed with error" << a );
396 close( nSocket );
397 return;
400 if ( ( a = listen( nSocket, 1 ) ) < 0 )
402 SAL_WARN( "sdremote.bluetooth", "listen failed with error" << a );
403 close( nSocket );
404 return;
407 // set non-blocking behaviour ...
408 if( fcntl( nSocket, F_SETFL, O_NONBLOCK) < 0 )
410 close( nSocket );
411 return;
414 pSocketFD->fd = nSocket;
415 pSocketFD->events = G_IO_IN | G_IO_PRI;
416 pSocketFD->revents = 0;
418 g_main_context_add_poll( pContext, pSocketFD, G_PRIORITY_DEFAULT );
421 static void
422 bluezDetachCloseSocket( GMainContext *pContext, GPollFD *pSocketFD )
424 if( pSocketFD->fd >= 0 )
426 close( pSocketFD->fd );
427 g_main_context_remove_poll( pContext, pSocketFD );
428 pSocketFD->fd = -1;
432 #endif // LINUX_BLUETOOTH
434 void BluetoothServer::addCommunicator( Communicator* pCommunicator )
436 mpCommunicators->push_back( pCommunicator );
439 #if defined(MACOSX)
441 OSXBluetoothWrapper::OSXBluetoothWrapper( IOBluetoothRFCOMMChannel* channel ) :
442 mpChannel(channel),
443 mnMTU(0),
444 mHaveBytes(),
445 mMutex(),
446 mBuffer()
448 // silly enough, can't write more than mnMTU bytes at once
449 mnMTU = [channel getMTU];
451 SAL_INFO( "sdremote.bluetooth", "OSXBluetoothWrapper::OSXBluetoothWrapper(): mnMTU=" << mnMTU );
454 sal_Int32 OSXBluetoothWrapper::readLine( OString& aLine )
456 SAL_INFO( "sdremote.bluetooth", "OSXBluetoothWrapper::readLine()" );
458 while( true )
461 SAL_INFO( "sdremote.bluetooth", "OSXBluetoothWrapper::readLine: entering mutex" );
462 ::osl::MutexGuard aQueueGuard( mMutex );
463 SAL_INFO( "sdremote.bluetooth", "OSXBluetoothWrapper::readLine: entered mutex" );
465 #ifdef SAL_LOG_INFO
466 // We should have in the sal logging some standard way to
467 // output char buffers with non-printables escaped.
468 std::ostringstream s;
469 if (mBuffer.size() > 0)
471 for (unsigned char *p = (unsigned char *) &mBuffer.front(); p != (unsigned char *) &mBuffer.front() + mBuffer.size(); p++)
473 if (*p == '\n')
474 s << "\\n";
475 else if (*p < ' ' || *p >= 0x7F)
476 s << "\\0x" << std::hex << std::setw(2) << std::setfill('0') << (int) *p << std::setfill(' ') << std::setw(1) << std::dec;
477 else
478 s << *p;
481 SAL_INFO( "sdremote.bluetooth", "OSXBluetoothWrapper::readLine mBuffer: \"" << s.str() << "\"" );
482 #endif
484 // got enough bytes to return a line?
485 std::vector<char>::iterator aIt;
486 if ( (aIt = find( mBuffer.begin(), mBuffer.end(), '\n' ))
487 != mBuffer.end() )
489 sal_uInt64 aLocation = aIt - mBuffer.begin();
491 aLine = OString( &(*mBuffer.begin()), aLocation );
493 mBuffer.erase( mBuffer.begin(), aIt + 1 ); // Also delete the empty line
495 // yeps
496 SAL_INFO( "sdremote.bluetooth", " returning, got \"" << OStringToOUString( aLine, RTL_TEXTENCODING_UTF8 ) << "\"" );
497 return aLine.getLength() + 1;
500 // nope - wait some more (after releasing the mutex)
501 SAL_INFO( "sdremote.bluetooth", " resetting mHaveBytes" );
502 mHaveBytes.reset();
503 SAL_INFO( "sdremote.bluetooth", " leaving mutex" );
506 SAL_INFO( "sdremote.bluetooth", " waiting for mHaveBytes" );
507 mHaveBytes.wait();
508 SAL_INFO( "sdremote.bluetooth", "OSXBluetoothWrapper::readLine: got mHaveBytes" );
512 sal_Int32 OSXBluetoothWrapper::write( const void* pBuffer, sal_uInt32 n )
514 SAL_INFO( "sdremote.bluetooth", "OSXBluetoothWrapper::write(" << pBuffer << ", " << n << ") mpChannel=" << mpChannel );
516 char* ptr = (char*)pBuffer;
517 sal_uInt32 nBytesWritten = 0;
519 if (mpChannel == nil)
520 return 0;
522 while( nBytesWritten < n )
524 int toWrite = n - nBytesWritten;
525 toWrite = toWrite <= mnMTU ? toWrite : mnMTU;
526 if ( [mpChannel writeSync:ptr length:toWrite] != kIOReturnSuccess )
528 SAL_INFO( "sdremote.bluetooth", " [mpChannel writeSync:" << (void *) ptr << " length:" << toWrite << "] returned error, total written " << nBytesWritten );
529 return nBytesWritten;
531 ptr += toWrite;
532 nBytesWritten += toWrite;
534 SAL_INFO( "sdremote.bluetooth", " total written " << nBytesWritten );
535 return nBytesWritten;
538 void OSXBluetoothWrapper::appendData(void* pBuffer, size_t len)
540 SAL_INFO( "sdremote.bluetooth", "OSXBluetoothWrapper::appendData(" << pBuffer << ", " << len << ")" );
542 if( len )
544 SAL_INFO( "sdremote.bluetooth", "OSXBluetoothWrapper::appendData: entering mutex" );
545 ::osl::MutexGuard aQueueGuard( mMutex );
546 SAL_INFO( "sdremote.bluetooth", "OSXBluetoothWrapper::appendData: entered mutex" );
547 mBuffer.insert(mBuffer.begin()+mBuffer.size(),
548 (char*)pBuffer, (char *)pBuffer+len);
549 SAL_INFO( "sdremote.bluetooth", " setting mHaveBytes" );
550 mHaveBytes.set();
551 SAL_INFO( "sdremote.bluetooth", " leaving mutex" );
555 void OSXBluetoothWrapper::channelClosed()
557 SAL_INFO( "sdremote.bluetooth", "OSXBluetoothWrapper::channelClosed()" );
559 mpChannel = nil;
562 void incomingCallback( void *userRefCon,
563 IOBluetoothUserNotificationRef inRef,
564 IOBluetoothObjectRef objectRef )
566 (void) inRef;
568 SAL_INFO( "sdremote.bluetooth", "incomingCallback()" );
570 BluetoothServer* pServer = (BluetoothServer*)userRefCon;
572 IOBluetoothRFCOMMChannel* channel = [IOBluetoothRFCOMMChannel withRFCOMMChannelRef:(IOBluetoothRFCOMMChannelRef)objectRef];
574 OSXBluetoothWrapper* socket = new OSXBluetoothWrapper( channel);
575 Communicator* pCommunicator = new Communicator( socket );
576 pServer->addCommunicator( pCommunicator );
578 ChannelDelegate* delegate = [[ChannelDelegate alloc] initWithCommunicatorAndSocket: pCommunicator socket: socket];
579 [channel setDelegate: delegate];
580 [delegate retain];
582 pCommunicator->launch();
585 #endif // MACOSX
587 #ifdef LINUX_BLUETOOTH
589 extern "C" {
590 static gboolean ensureDiscoverable_cb(gpointer)
592 BluetoothServer::doEnsureDiscoverable();
593 return FALSE; // remove source
595 static gboolean restoreDiscoverable_cb(gpointer)
597 BluetoothServer::doRestoreDiscoverable();
598 return FALSE; // remove source
603 * Bluez 4 uses custom methods for setting properties, whereas Bluez 5+
604 * implements properties using the generic "org.freedesktop.DBus.Properties"
605 * interface -- hence we have a specific Bluez 4 function to deal with the
606 * old style of reading properties.
608 static bool
609 getBluez4BooleanProperty( DBusConnection *pConnection, DBusObject *pAdapter,
610 const char *pPropertyName, bool *pBoolean )
612 *pBoolean = false;
614 if( !pAdapter )
615 return false;
617 DBusMessage *pMsg;
618 pMsg = sendUnrefAndWaitForReply( pConnection,
619 pAdapter->getMethodCall( "GetProperties" ) );
621 DBusMessageIter it;
622 if( !pMsg || !dbus_message_iter_init( pMsg, &it ) )
624 SAL_WARN( "sdremote.bluetooth", "no valid reply / timeout" );
625 return false;
628 if( DBUS_TYPE_ARRAY != dbus_message_iter_get_arg_type( &it ) )
630 SAL_WARN( "sdremote.bluetooth", "no valid reply / timeout" );
631 return false;
634 DBusMessageIter arrayIt;
635 dbus_message_iter_recurse( &it, &arrayIt );
637 while( dbus_message_iter_get_arg_type( &arrayIt ) == DBUS_TYPE_DICT_ENTRY )
639 DBusMessageIter dictIt;
640 dbus_message_iter_recurse( &arrayIt, &dictIt );
642 const char *pName = NULL;
643 if( dbus_message_iter_get_arg_type( &dictIt ) == DBUS_TYPE_STRING )
645 dbus_message_iter_get_basic( &dictIt, &pName );
646 if( pName != NULL && !strcmp( pName, pPropertyName ) )
648 SAL_INFO( "sdremote.bluetooth", "hit " << pPropertyName << " property" );
649 dbus_message_iter_next( &dictIt );
650 dbus_bool_t bBool = false;
652 if( dbus_message_iter_get_arg_type( &dictIt ) == DBUS_TYPE_VARIANT )
654 DBusMessageIter variantIt;
655 dbus_message_iter_recurse( &dictIt, &variantIt );
657 if( dbus_message_iter_get_arg_type( &variantIt ) == DBUS_TYPE_BOOLEAN )
659 dbus_message_iter_get_basic( &variantIt, &bBool );
660 SAL_INFO( "sdremote.bluetooth", "" << pPropertyName << " is " << bBool );
661 *pBoolean = bBool;
662 return true;
664 else
665 SAL_WARN( "sdremote.bluetooth", "" << pPropertyName << " type " <<
666 dbus_message_iter_get_arg_type( &variantIt ) );
668 else
669 SAL_WARN( "sdremote.bluetooth", "variant type ? " <<
670 dbus_message_iter_get_arg_type( &dictIt ) );
672 else
674 const char *pStr = pName ? pName : "<null>";
675 SAL_INFO( "sdremote.bluetooth", "property '" << pStr << "'" );
678 else
679 SAL_WARN( "sdremote.bluetooth", "unexpected property key type "
680 << dbus_message_iter_get_arg_type( &dictIt ) );
681 dbus_message_iter_next( &arrayIt );
683 dbus_message_unref( pMsg );
685 return false;
689 * This gets an org.freedesktop.DBus.Properties boolean
690 * (as opposed to the old Bluez 4 custom properties methods as visible above).
692 static bool
693 getDBusBooleanProperty( DBusConnection *pConnection, DBusObject *pAdapter,
694 const char *pPropertyName, bool *pBoolean )
696 assert( pAdapter );
698 *pBoolean = false;
699 bool bRet = false;
701 ::boost::scoped_ptr< DBusObject > pProperties (
702 pAdapter->cloneForInterface( "org.freedesktop.DBus.Properties" ) );
704 DBusMessage *pMsg = pProperties->getMethodCall( "Get" );
706 DBusMessageIter itIn;
707 dbus_message_iter_init_append( pMsg, &itIn );
708 const char* pInterface = "org.bluez.Adapter1";
709 dbus_message_iter_append_basic( &itIn, DBUS_TYPE_STRING, &pInterface );
710 dbus_message_iter_append_basic( &itIn, DBUS_TYPE_STRING, &pPropertyName );
711 pMsg = sendUnrefAndWaitForReply( pConnection, pMsg );
713 DBusMessageIter it;
714 if( !pMsg || !dbus_message_iter_init( pMsg, &it ) )
716 SAL_WARN( "sdremote.bluetooth", "no valid reply / timeout" );
717 return false;
720 if( DBUS_TYPE_VARIANT != dbus_message_iter_get_arg_type( &it ) )
722 SAL_WARN( "sdremote.bluetooth", "invalid return type" );
724 else
726 DBusMessageIter variantIt;
727 dbus_message_iter_recurse( &it, &variantIt );
729 if( dbus_message_iter_get_arg_type( &variantIt ) == DBUS_TYPE_BOOLEAN )
731 dbus_bool_t bBool = false;
732 dbus_message_iter_get_basic( &variantIt, &bBool );
733 SAL_INFO( "sdremote.bluetooth", "" << pPropertyName << " is " << bBool );
734 *pBoolean = bBool;
735 bRet = true;
737 else
739 SAL_WARN( "sdremote.bluetooth", "" << pPropertyName << " type " <<
740 dbus_message_iter_get_arg_type( &variantIt ) );
743 const char* pError = dbus_message_get_error_name( pMsg );
744 if ( pError )
746 SAL_WARN( "sdremote.bluetooth",
747 "Get failed for " << pPropertyName << " on " <<
748 pAdapter->maPath << " with error: " << pError );
751 dbus_message_unref( pMsg );
753 return bRet;
756 static void
757 setDBusBooleanProperty( DBusConnection *pConnection, DBusObject *pAdapter,
758 const char *pPropertyName, bool bBoolean )
760 assert( pAdapter );
762 ::boost::scoped_ptr< DBusObject > pProperties(
763 pAdapter->cloneForInterface( "org.freedesktop.DBus.Properties" ) );
765 DBusMessage *pMsg = pProperties->getMethodCall( "Set" );
767 DBusMessageIter itIn;
768 dbus_message_iter_init_append( pMsg, &itIn );
769 const char* pInterface = "org.bluez.Adapter1";
770 dbus_message_iter_append_basic( &itIn, DBUS_TYPE_STRING, &pInterface );
771 dbus_message_iter_append_basic( &itIn, DBUS_TYPE_STRING, &pPropertyName );
774 DBusMessageIter varIt;
775 dbus_message_iter_open_container( &itIn, DBUS_TYPE_VARIANT,
776 DBUS_TYPE_BOOLEAN_AS_STRING, &varIt );
777 dbus_bool_t bDBusBoolean = bBoolean;
778 dbus_message_iter_append_basic( &varIt, DBUS_TYPE_BOOLEAN, &bDBusBoolean );
779 dbus_message_iter_close_container( &itIn, &varIt );
782 pMsg = sendUnrefAndWaitForReply( pConnection, pMsg );
784 if( !pMsg )
786 SAL_WARN( "sdremote.bluetooth", "no valid reply / timeout" );
788 else
790 const char* pError = dbus_message_get_error_name( pMsg );
791 if ( pError )
793 SAL_WARN( "sdremote.bluetooth",
794 "Set failed for " << pPropertyName << " on " <<
795 pAdapter->maPath << " with error: " << pError );
797 dbus_message_unref( pMsg );
801 static bool
802 getDiscoverable( DBusConnection *pConnection, DBusObject *pAdapter )
804 if (pAdapter->maInterface == "org.bluez.Adapter") // Bluez 4
806 bool bDiscoverable;
807 if( getBluez4BooleanProperty(pConnection, pAdapter, "Discoverable", &bDiscoverable ) )
808 return bDiscoverable;
810 else if (pAdapter->maInterface == "org.bluez.Adapter1") // Bluez 5
812 bool bDiscoverable;
813 if ( getDBusBooleanProperty(pConnection, pAdapter, "Discoverable", &bDiscoverable ) )
814 return bDiscoverable;
816 return false;
819 static void
820 setDiscoverable( DBusConnection *pConnection, DBusObject *pAdapter, bool bDiscoverable )
822 SAL_INFO( "sdremote.bluetooth", "setDiscoverable to " << bDiscoverable );
824 if (pAdapter->maInterface == "org.bluez.Adapter") // Bluez 4
826 bool bPowered = false;
827 if( !getBluez4BooleanProperty( pConnection, pAdapter, "Powered", &bPowered ) || !bPowered )
828 return; // nothing to do
830 DBusMessage *pMsg;
831 DBusMessageIter it, varIt;
833 // set timeout to zero
834 pMsg = pAdapter->getMethodCall( "SetProperty" );
835 dbus_message_iter_init_append( pMsg, &it );
836 const char *pTimeoutStr = "DiscoverableTimeout";
837 dbus_message_iter_append_basic( &it, DBUS_TYPE_STRING, &pTimeoutStr );
838 dbus_message_iter_open_container( &it, DBUS_TYPE_VARIANT,
839 DBUS_TYPE_UINT32_AS_STRING, &varIt );
840 dbus_uint32_t nTimeout = 0;
841 dbus_message_iter_append_basic( &varIt, DBUS_TYPE_UINT32, &nTimeout );
842 dbus_message_iter_close_container( &it, &varIt );
843 dbus_connection_send( pConnection, pMsg, NULL ); // async send - why not ?
844 dbus_message_unref( pMsg );
846 // set discoverable value
847 pMsg = pAdapter->getMethodCall( "SetProperty" );
848 dbus_message_iter_init_append( pMsg, &it );
849 const char *pDiscoverableStr = "Discoverable";
850 dbus_message_iter_append_basic( &it, DBUS_TYPE_STRING, &pDiscoverableStr );
851 dbus_message_iter_open_container( &it, DBUS_TYPE_VARIANT,
852 DBUS_TYPE_BOOLEAN_AS_STRING, &varIt );
853 dbus_bool_t bValue = bDiscoverable;
854 dbus_message_iter_append_basic( &varIt, DBUS_TYPE_BOOLEAN, &bValue );
855 dbus_message_iter_close_container( &it, &varIt ); // async send - why not ?
856 dbus_connection_send( pConnection, pMsg, NULL );
857 dbus_message_unref( pMsg );
859 else if (pAdapter->maInterface == "org.bluez.Adapter1") // Bluez 5
861 setDBusBooleanProperty(pConnection, pAdapter, "Discoverable", bDiscoverable );
865 static DBusObject *
866 registerWithDefaultAdapter( DBusConnection *pConnection )
868 DBusObject *pService;
869 pService = bluez4GetDefaultService( pConnection );
870 if( pService )
872 if( !bluez4RegisterServiceRecord( pConnection, pService,
873 bluetooth_service_record ) )
875 delete pService;
876 return NULL;
880 return pService;
883 void ProfileUnregisterFunction
884 (DBusConnection *connection, void *user_data)
886 // We specifically don't need to do anything here.
887 (void) connection;
888 (void) user_data;
891 DBusHandlerResult ProfileMessageFunction
892 (DBusConnection *pConnection, DBusMessage *pMessage, void *user_data)
894 SAL_INFO("sdremote.bluetooth", "ProfileMessageFunction||" << dbus_message_get_interface(pMessage) << "||" << dbus_message_get_member(pMessage));
895 DBusHandlerResult aRet = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
897 if (OString(dbus_message_get_interface(pMessage)).equals("org.bluez.Profile1"))
899 if (OString(dbus_message_get_member(pMessage)).equals("Release"))
901 return DBUS_HANDLER_RESULT_HANDLED;
903 else if (OString(dbus_message_get_member(pMessage)).equals("NewConnection"))
905 if (!dbus_message_has_signature(pMessage, "oha{sv}"))
907 SAL_WARN("sdremote.bluetooth", "wrong signature for NewConnection");
910 DBusMessageIter it;
911 dbus_message_iter_init(pMessage, &it);
913 char* pPath;
914 dbus_message_iter_get_basic(&it, &pPath);
915 SAL_INFO("sdremote.bluetooth", "Adapter path:" << pPath);
917 if (!dbus_message_iter_next(&it))
918 SAL_WARN("sdremote.bluetooth", "not enough parameters passed");
920 // DBUS_TYPE_UNIX_FD == 'h' -- doesn't exist in older versions
921 // of dbus (< 1.3?) hence defined manually for now
922 if ('h' == dbus_message_iter_get_arg_type(&it))
925 int nDescriptor;
926 dbus_message_iter_get_basic(&it, &nDescriptor);
927 std::vector<Communicator*>* pCommunicators = (std::vector<Communicator*>*) user_data;
929 // Bluez gives us non-blocking sockets, but our code relies
930 // on blocking behaviour.
931 fcntl(nDescriptor, F_SETFL, fcntl(nDescriptor, F_GETFL) & ~O_NONBLOCK);
933 SAL_INFO( "sdremote.bluetooth", "connection accepted " << nDescriptor);
934 Communicator* pCommunicator = new Communicator( new BufferedStreamSocket( nDescriptor ) );
935 pCommunicators->push_back( pCommunicator );
936 pCommunicator->launch();
939 // For some reason an (empty?) reply is expected.
940 DBusMessage* pRet = dbus_message_new_method_return(pMessage);
941 dbus_connection_send(pConnection, pRet, NULL);
942 dbus_message_unref(pRet);
944 // We could read the remote profile version and features here
945 // (i.e. they are provided as part of the DBusMessage),
946 // however for us they are irrelevant (as our protocol handles
947 // equivalent functionality independently of whether we're on
948 // bluetooth or normal network connection).
949 return DBUS_HANDLER_RESULT_HANDLED;
951 else if (OString(dbus_message_get_member(pMessage)).equals("RequestDisconnection"))
953 return DBUS_HANDLER_RESULT_HANDLED;
956 SAL_WARN("sdremote.bluetooth", "Couldn't handle message correctly.");
957 return aRet;
961 static void
962 setupBluez5Profile1(DBusConnection* pConnection, std::vector<Communicator*>* pCommunicators)
964 bool bErr;
966 SAL_INFO("sdremote.bluetooth", "Attempting to register our org.bluez.Profile1");
967 static DBusObjectPathVTable aVTable;
968 aVTable.unregister_function = ProfileUnregisterFunction;
969 aVTable.message_function = ProfileMessageFunction;
971 // dbus_connection_try_register_object_path could be used but only exists for
972 // dbus-glib >= 1.2 -- we really shouldn't be trying this twice in any case.
973 // (dbus_connection_try_register_object_path also returns an error with more
974 // information which could be useful for debugging purposes.)
975 bErr = !dbus_connection_register_object_path(pConnection, "/org/libreoffice/bluez/profile1", &aVTable, pCommunicators);
977 if (bErr)
979 SAL_WARN("sdremote.bluetooth", "Failed to register Bluez 5 Profile1 callback, bluetooth won't work.");
982 dbus_connection_flush( pConnection );
985 static void
986 unregisterBluez5Profile(DBusConnection* pConnection)
988 DBusMessage* pMsg = dbus_message_new_method_call("org.bluez", "/org/bluez",
989 "org.bluez.ProfileManager1", "UnregisterProfile");
990 DBusMessageIter it;
991 dbus_message_iter_init_append(pMsg, &it);
993 const char *pPath = "/org/libreoffice/bluez/profile1";
994 dbus_message_iter_append_basic(&it, DBUS_TYPE_OBJECT_PATH, &pPath);
996 pMsg = sendUnrefAndWaitForReply( pConnection, pMsg );
998 if (pMsg)
999 dbus_message_unref(pMsg);
1001 dbus_connection_unregister_object_path( pConnection, "/org/libreoffice/bluez/profile1");
1003 dbus_connection_flush(pConnection);
1006 static bool
1007 registerBluez5Profile(DBusConnection* pConnection, std::vector<Communicator*>* pCommunicators)
1009 setupBluez5Profile1(pConnection, pCommunicators);
1011 DBusMessage *pMsg;
1012 DBusMessageIter it;
1014 pMsg = dbus_message_new_method_call("org.bluez", "/org/bluez",
1015 "org.bluez.ProfileManager1", "RegisterProfile");
1016 dbus_message_iter_init_append(pMsg, &it);
1018 const char *pPath = "/org/libreoffice/bluez/profile1";
1019 dbus_message_iter_append_basic(&it, DBUS_TYPE_OBJECT_PATH, &pPath);
1020 const char *pUUID = "spp"; // Bluez translates this to 0x1101 for spp
1021 dbus_message_iter_append_basic(&it, DBUS_TYPE_STRING, &pUUID);
1023 DBusMessageIter aOptionsIter;
1024 dbus_message_iter_open_container(&it, DBUS_TYPE_ARRAY, "{sv}", &aOptionsIter);
1026 DBusMessageIter aEntry;
1029 dbus_message_iter_open_container(&aOptionsIter, DBUS_TYPE_DICT_ENTRY, NULL, &aEntry);
1031 const char *pString = "Name";
1032 dbus_message_iter_append_basic(&aEntry, DBUS_TYPE_STRING, &pString);
1034 const char *pValue = "LibreOffice Impress Remote";
1035 DBusMessageIter aValue;
1036 dbus_message_iter_open_container(&aEntry, DBUS_TYPE_VARIANT, "s", &aValue);
1037 dbus_message_iter_append_basic(&aValue, DBUS_TYPE_STRING, &pValue);
1038 dbus_message_iter_close_container(&aEntry, &aValue);
1039 dbus_message_iter_close_container(&aOptionsIter, &aEntry);
1042 dbus_message_iter_close_container(&it, &aOptionsIter);
1044 // Other properties that we could set (but don't, since they appear
1045 // to be useless for us):
1046 // "Service": "0x1101" (not needed, but we used to have it in the manually defined profile).
1047 // "Role": setting this to "server" breaks things, although we think we're a server?
1048 // "Channel": seems to be dealt with automatically (but we used to use 5 in the manual profile).
1050 bool bSuccess = true;
1052 pMsg = sendUnrefAndWaitForReply( pConnection, pMsg );
1054 DBusError aError;
1055 dbus_error_init(&aError);
1056 if (pMsg && dbus_set_error_from_message( &aError, pMsg ))
1058 bSuccess = false;
1059 SAL_WARN("sdremote.bluetooth",
1060 "Failed to register our Profile1 with bluez ProfileManager "
1061 << (const char *)(aError.message ? aError.message : "<null>"));
1064 dbus_error_free(&aError);
1065 if (pMsg)
1066 dbus_message_unref(pMsg);
1068 dbus_connection_flush(pConnection);
1070 return bSuccess;
1073 #endif // LINUX_BLUETOOTH
1075 BluetoothServer::BluetoothServer( std::vector<Communicator*>* pCommunicators )
1076 : meWasDiscoverable( UNKNOWN ),
1077 mpCommunicators( pCommunicators )
1079 #ifdef LINUX_BLUETOOTH
1080 mpImpl.reset(new BluetoothServer::Impl());
1081 #endif
1084 BluetoothServer::~BluetoothServer()
1088 void BluetoothServer::ensureDiscoverable()
1090 #ifdef LINUX_BLUETOOTH
1091 // Push it all across into our mainloop
1092 if( !spServer )
1093 return;
1094 GSource *pIdle = g_idle_source_new();
1095 g_source_set_callback( pIdle, ensureDiscoverable_cb, NULL, NULL );
1096 g_source_set_priority( pIdle, G_PRIORITY_DEFAULT );
1097 g_source_attach( pIdle, spServer->mpImpl->mpContext );
1098 g_source_unref( pIdle );
1099 #endif
1102 void BluetoothServer::restoreDiscoverable()
1104 #ifdef LINUX_BLUETOOTH
1105 // Push it all across into our mainloop
1106 if( !spServer )
1107 return;
1108 GSource *pIdle = g_idle_source_new();
1109 g_source_set_callback( pIdle, restoreDiscoverable_cb, NULL, NULL );
1110 g_source_set_priority( pIdle, G_PRIORITY_DEFAULT_IDLE );
1111 g_source_attach( pIdle, spServer->mpImpl->mpContext );
1112 g_source_unref( pIdle );
1113 #endif
1116 void BluetoothServer::doEnsureDiscoverable()
1118 #ifdef LINUX_BLUETOOTH
1119 if (!spServer->mpImpl->mpConnection ||
1120 spServer->meWasDiscoverable != UNKNOWN )
1121 return;
1123 // Find out if we are discoverable already ...
1124 DBusObject *pAdapter = spServer->mpImpl->getAdapter();
1125 if( !pAdapter )
1126 return;
1128 bool bDiscoverable = getDiscoverable(spServer->mpImpl->mpConnection, pAdapter );
1130 spServer->meWasDiscoverable = bDiscoverable ? DISCOVERABLE : NOT_DISCOVERABLE;
1131 if( !bDiscoverable )
1132 setDiscoverable( spServer->mpImpl->mpConnection, pAdapter, true );
1134 delete pAdapter;
1135 #endif
1138 void BluetoothServer::doRestoreDiscoverable()
1140 if( spServer->meWasDiscoverable == NOT_DISCOVERABLE )
1142 #ifdef LINUX_BLUETOOTH
1143 DBusObject *pAdapter = spServer->mpImpl->getAdapter();
1144 if( !pAdapter )
1145 return;
1146 setDiscoverable( spServer->mpImpl->mpConnection, pAdapter, false );
1147 delete pAdapter;
1148 #endif
1150 spServer->meWasDiscoverable = UNKNOWN;
1153 // We have to have all our clients shut otherwise we can't
1154 // re-bind to the same port number it appears.
1155 void BluetoothServer::cleanupCommunicators()
1157 for (std::vector<Communicator *>::iterator it = mpCommunicators->begin();
1158 it != mpCommunicators->end(); ++it)
1159 (*it)->forceClose();
1160 // the hope is that all the threads then terminate cleanly and
1161 // clean themselves up.
1164 void SAL_CALL BluetoothServer::run()
1166 SAL_INFO( "sdremote.bluetooth", "BluetoothServer::run called" );
1168 #ifdef LINUX_BLUETOOTH
1169 DBusConnection *pConnection = dbusConnectToNameOnBus();
1170 if( !pConnection )
1171 return;
1174 // For either implementation we need to poll the dbus fd
1175 int fd = -1;
1176 GPollFD aDBusFD;
1177 if( dbus_connection_get_unix_fd( pConnection, &fd ) && fd >= 0 )
1179 aDBusFD.fd = fd;
1180 aDBusFD.events = G_IO_IN | G_IO_PRI;
1181 g_main_context_add_poll( mpImpl->mpContext, &aDBusFD, G_PRIORITY_DEFAULT );
1183 else
1184 SAL_WARN( "sdremote.bluetooth", "failed to poll for incoming dbus signals" );
1186 if (isBluez5Available(pConnection))
1188 SAL_INFO("sdremote.bluetooth", "Using Bluez 5");
1189 registerBluez5Profile(pConnection, mpCommunicators);
1190 mpImpl->mpConnection = pConnection;
1191 mpImpl->maBluezVersion = Impl::BLUEZ5;
1193 // We don't need to listen to adapter changes anymore -- profile
1194 // registration is done globally for the entirety of bluez, so we only
1195 // need adapters when setting discovereability, which can be done
1196 // dyanmically without the need to listen for changes.
1198 // TODO: exit on SD deinit
1199 // Probably best to do that in SdModule::~SdModule?
1200 while (!mpImpl->mbExitMainloop)
1202 aDBusFD.revents = 0;
1203 g_main_context_iteration( mpImpl->mpContext, TRUE );
1204 if( aDBusFD.revents )
1206 dbus_connection_read_write( pConnection, 0 );
1207 while (DBUS_DISPATCH_DATA_REMAINS == dbus_connection_get_dispatch_status( pConnection ))
1208 dbus_connection_dispatch( pConnection );
1211 unregisterBluez5Profile( pConnection );
1212 g_main_context_unref( mpImpl->mpContext );
1213 mpImpl->mpConnection = NULL;
1214 mpImpl->mpContext = NULL;
1215 return;
1218 // Otherwise we could be on Bluez 4 and continue as usual.
1219 mpImpl->maBluezVersion = Impl::BLUEZ4;
1221 // Try to setup the default adapter, otherwise wait for add/remove signal
1222 mpImpl->mpService = registerWithDefaultAdapter( pConnection );
1223 // listen for connection state and power changes - we need to close
1224 // and re-create our socket code on suspend / resume, enable/disable
1225 DBusError aError;
1226 dbus_error_init( &aError );
1227 dbus_bus_add_match( pConnection, "type='signal',interface='org.bluez.Manager'", &aError );
1228 dbus_connection_flush( pConnection );
1230 // Try to setup the default adapter, otherwise wait for add/remove signal
1231 mpImpl->mpService = registerWithDefaultAdapter( pConnection );
1233 // poll on our bluetooth socket - if we can.
1234 GPollFD aSocketFD;
1235 if( mpImpl->mpService )
1236 bluezCreateAttachListeningSocket( mpImpl->mpContext, &aSocketFD );
1238 mpImpl->mpConnection = pConnection;
1240 while( !mpImpl->mbExitMainloop )
1242 aDBusFD.revents = 0;
1243 aSocketFD.revents = 0;
1244 g_main_context_iteration( mpImpl->mpContext, TRUE );
1246 SAL_INFO( "sdremote.bluetooth", "main-loop spin "
1247 << aDBusFD.revents << " " << aSocketFD.revents );
1248 if( aDBusFD.revents )
1250 dbus_connection_read_write( pConnection, 0 );
1251 DBusMessage *pMsg = dbus_connection_pop_message( pConnection );
1252 if( pMsg )
1254 if( dbus_message_is_signal( pMsg, "org.bluez.Manager", "AdapterRemoved" ) )
1256 SAL_WARN( "sdremote.bluetooth", "lost adapter - cleaning up sockets" );
1257 bluezDetachCloseSocket( mpImpl->mpContext, &aSocketFD );
1258 cleanupCommunicators();
1260 else if( dbus_message_is_signal( pMsg, "org.bluez.Manager", "AdapterAdded" ) ||
1261 dbus_message_is_signal( pMsg, "org.bluez.Manager", "DefaultAdapterChanged" ) )
1263 SAL_WARN( "sdremote.bluetooth", "gained adapter - re-generating sockets" );
1264 bluezDetachCloseSocket( mpImpl->mpContext, &aSocketFD );
1265 cleanupCommunicators();
1266 mpImpl->mpService = registerWithDefaultAdapter( pConnection );
1267 if( mpImpl->mpService )
1268 bluezCreateAttachListeningSocket( mpImpl->mpContext, &aSocketFD );
1270 else
1271 SAL_INFO( "sdremote.bluetooth", "unknown incoming dbus message, "
1272 << " type: " << dbus_message_get_type( pMsg )
1273 << " path: '" << dbus_message_get_path( pMsg )
1274 << "' interface: '" << dbus_message_get_interface( pMsg )
1275 << "' member: '" << dbus_message_get_member( pMsg ) );
1277 dbus_message_unref( pMsg );
1280 if( aSocketFD.revents )
1282 sockaddr_rc aRemoteAddr;
1283 socklen_t aRemoteAddrLen = sizeof(aRemoteAddr);
1285 int nClient;
1286 SAL_INFO( "sdremote.bluetooth", "performing accept" );
1287 if ( ( nClient = accept( aSocketFD.fd, (sockaddr*) &aRemoteAddr, &aRemoteAddrLen)) < 0 &&
1288 errno != EAGAIN )
1290 SAL_WARN( "sdremote.bluetooth", "accept failed with errno " << errno );
1291 } else {
1292 SAL_INFO( "sdremote.bluetooth", "connection accepted " << nClient );
1293 Communicator* pCommunicator = new Communicator( new BufferedStreamSocket( nClient ) );
1294 mpCommunicators->push_back( pCommunicator );
1295 pCommunicator->launch();
1300 unregisterBluez5Profile( pConnection );
1301 g_main_context_unref( mpImpl->mpContext );
1302 mpImpl->mpConnection = NULL;
1303 mpImpl->mpContext = NULL;
1305 #elif defined(WIN32)
1306 WORD wVersionRequested;
1307 WSADATA wsaData;
1309 wVersionRequested = MAKEWORD(2, 2);
1311 if ( WSAStartup(wVersionRequested, &wsaData) )
1313 return; // winsock dll couldn't be loaded
1316 int aSocket = socket( AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM );
1317 if ( !aSocket )
1319 WSACleanup();
1320 return;
1322 SOCKADDR_BTH aAddr;
1323 aAddr.addressFamily = AF_BTH;
1324 aAddr.btAddr = 0;
1325 aAddr.serviceClassId = GUID_NULL;
1326 aAddr.port = BT_PORT_ANY; // Select any free socket.
1327 if ( bind( aSocket, (SOCKADDR*) &aAddr, sizeof(aAddr) ) == SOCKET_ERROR )
1329 closesocket( aSocket );
1330 WSACleanup();
1331 return;
1334 SOCKADDR aName;
1335 int aNameSize = sizeof(aAddr);
1336 getsockname( aSocket, &aName, &aNameSize ); // Retrieve the local address and port
1338 CSADDR_INFO aAddrInfo;
1339 memset( &aAddrInfo, 0, sizeof(aAddrInfo) );
1340 aAddrInfo.LocalAddr.lpSockaddr = &aName;
1341 aAddrInfo.LocalAddr.iSockaddrLength = sizeof( SOCKADDR_BTH );
1342 aAddrInfo.RemoteAddr.lpSockaddr = &aName;
1343 aAddrInfo.RemoteAddr.iSockaddrLength = sizeof( SOCKADDR_BTH );
1344 aAddrInfo.iSocketType = SOCK_STREAM;
1345 aAddrInfo.iProtocol = BTHPROTO_RFCOMM;
1347 // To be used for setting a custom UUID once available.
1348 // GUID uuid;
1349 // uuid.Data1 = 0x00001101;
1350 // memset( &uuid, 0x1000 + UUID*2^96, sizeof( GUID ) );
1351 // uuid.Data2 = 0;
1352 // uuid.Data3 = 0x1000;
1353 // ULONGLONG aData4 = 0x800000805F9B34FB;
1354 // memcpy( uuid.Data4, &aData4, sizeof(uuid.Data4) );
1356 WSAQUERYSET aRecord;
1357 memset( &aRecord, 0, sizeof(aRecord));
1358 aRecord.dwSize = sizeof(aRecord);
1359 aRecord.lpszServiceInstanceName = "LibreOffice Impress Remote Control";
1360 aRecord.lpszComment = "Remote control of presentations over bluetooth.";
1361 aRecord.lpServiceClassId = (LPGUID) &SerialPortServiceClass_UUID;
1362 aRecord.dwNameSpace = NS_BTH;
1363 aRecord.dwNumberOfCsAddrs = 1;
1364 aRecord.lpcsaBuffer = &aAddrInfo;
1366 if ( WSASetService( &aRecord, RNRSERVICE_REGISTER, 0 ) == SOCKET_ERROR )
1368 closesocket( aSocket );
1369 WSACleanup();
1370 return;
1373 if ( listen( aSocket, 1 ) == SOCKET_ERROR )
1375 closesocket( aSocket );
1376 WSACleanup();
1377 return;
1380 SOCKADDR_BTH aRemoteAddr;
1381 int aRemoteAddrLen = sizeof(aRemoteAddr);
1382 while ( true )
1384 SOCKET socket;
1385 if ( (socket = accept(aSocket, (sockaddr*) &aRemoteAddr, &aRemoteAddrLen)) == INVALID_SOCKET )
1387 closesocket( aSocket );
1388 WSACleanup();
1389 return;
1390 } else {
1391 Communicator* pCommunicator = new Communicator( new BufferedStreamSocket( socket) );
1392 mpCommunicators->push_back( pCommunicator );
1393 pCommunicator->launch();
1397 #elif defined(MACOSX)
1398 // Build up dictionary at run-time instead of bothering with a
1399 // .plist file, using the Objective-C API
1401 // Compare to BluetoothServiceRecord.hxx
1403 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1405 NSDictionary *dict =
1406 [NSDictionary dictionaryWithObjectsAndKeys:
1408 // Service class ID list
1409 [NSArray arrayWithObject:
1410 [IOBluetoothSDPUUID uuid16: kBluetoothSDPUUID16ServiceClassSerialPort]],
1411 @"0001 - ServiceClassIDList",
1413 // Protocol descriptor list
1414 [NSArray arrayWithObjects:
1415 [NSArray arrayWithObject: [IOBluetoothSDPUUID uuid16: kBluetoothSDPUUID16L2CAP]],
1416 [NSArray arrayWithObjects:
1417 [IOBluetoothSDPUUID uuid16: kBluetoothL2CAPPSMRFCOMM],
1418 [NSDictionary dictionaryWithObjectsAndKeys:
1419 [NSNumber numberWithInt: 1],
1420 @"DataElementSize",
1421 [NSNumber numberWithInt: 1],
1422 @"DataElementType",
1423 [NSNumber numberWithInt: 5], // RFCOMM port number, will be replaced if necessary automatically
1424 @"DataElementValue",
1425 nil],
1426 nil],
1427 nil],
1428 @"0004 - Protocol descriptor list",
1430 // Browse group list
1431 [NSArray arrayWithObject:
1432 [IOBluetoothSDPUUID uuid16: kBluetoothSDPUUID16ServiceClassPublicBrowseGroup]],
1433 @"0005 - BrowseGroupList",
1435 // Language base attribute ID list
1436 [NSArray arrayWithObjects:
1437 [NSData dataWithBytes: "en" length: 2],
1438 [NSDictionary dictionaryWithObjectsAndKeys:
1439 [NSNumber numberWithInt: 2],
1440 @"DataElementSize",
1441 [NSNumber numberWithInt: 1],
1442 @"DataElementType",
1443 [NSNumber numberWithInt: 0x006a], // encoding
1444 @"DataElementValue",
1445 nil],
1446 [NSDictionary dictionaryWithObjectsAndKeys:
1447 [NSNumber numberWithInt: 2],
1448 @"DataElementSize",
1449 [NSNumber numberWithInt: 1],
1450 @"DataElementType",
1451 [NSNumber numberWithInt: 0x0100], // offset
1452 @"DataElementValue",
1453 nil],
1454 nil],
1455 @"0006 - LanguageBaseAttributeIDList",
1457 // Bluetooth profile descriptor list
1458 [NSArray arrayWithObject:
1459 [NSArray arrayWithObjects:
1460 [IOBluetoothSDPUUID uuid16: kBluetoothSDPUUID16ServiceClassSerialPort],
1461 [NSDictionary dictionaryWithObjectsAndKeys:
1462 [NSNumber numberWithInt: 2],
1463 @"DataElementSize",
1464 [NSNumber numberWithInt: 1],
1465 @"DataElementType",
1466 [NSNumber numberWithInt: 0x0100], // version number ?
1467 @"DataElementValue",
1468 nil],
1469 nil]],
1470 @"0009 - BluetoothProfileDescriptorList",
1472 // Attributes pointed to by the LanguageBaseAttributeIDList
1473 @"LibreOffice Impress Remote Control",
1474 @"0100 - ServiceName",
1475 @"The Document Foundation",
1476 @"0102 - ProviderName",
1477 nil];
1479 // Create service
1480 IOBluetoothSDPServiceRecordRef serviceRecordRef;
1481 IOReturn rc = IOBluetoothAddServiceDict((CFDictionaryRef) dict, &serviceRecordRef);
1483 SAL_INFO("sdremote.bluetooth", "IOBluetoothAddServiceDict returned " << rc);
1485 if (rc == kIOReturnSuccess)
1487 IOBluetoothSDPServiceRecord *serviceRecord =
1488 [IOBluetoothSDPServiceRecord withSDPServiceRecordRef: serviceRecordRef];
1490 BluetoothRFCOMMChannelID channelID;
1491 [serviceRecord getRFCOMMChannelID: &channelID];
1493 BluetoothSDPServiceRecordHandle serviceRecordHandle;
1494 [serviceRecord getServiceRecordHandle: &serviceRecordHandle];
1496 // Register callback for incoming connections
1497 IOBluetoothUserNotificationRef callbackRef =
1498 IOBluetoothRegisterForFilteredRFCOMMChannelOpenNotifications(
1499 incomingCallback,
1500 this,
1501 channelID,
1502 kIOBluetoothUserNotificationChannelDirectionIncoming);
1504 (void) callbackRef;
1506 [serviceRecord release];
1509 [pool release];
1511 (void) mpCommunicators;
1512 #else
1513 (void) mpCommunicators; // avoid warnings about unused member
1514 #endif
1517 BluetoothServer *sd::BluetoothServer::spServer = NULL;
1519 void BluetoothServer::setup( std::vector<Communicator*>* pCommunicators )
1521 if (spServer)
1522 return;
1524 spServer = new BluetoothServer( pCommunicators );
1525 spServer->create();
1528 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */