diff --git a/libretroshare/src/retroshare/rsbroadcastdiscovery.h b/libretroshare/src/retroshare/rsbroadcastdiscovery.h index a712c60af..1bb1e1cc6 100644 --- a/libretroshare/src/retroshare/rsbroadcastdiscovery.h +++ b/libretroshare/src/retroshare/rsbroadcastdiscovery.h @@ -107,5 +107,45 @@ public: */ virtual std::vector getDiscoveredPeers() = 0; + /** + * @brief Check if multicast listening is enabled + * @jsonapi{development} + * On some platforms such as Android multicast listening, which is needed + * for broadcast discovery, is not enabled by default at WiFi driver level + * @see enableMulticastListening, so this method check if it is enabled. + * On platforms that are not expected to have such a limitation this method + * always return true. + * @return true if enabled, false otherwise. + */ + virtual bool isMulticastListeningEnabled() = 0; + + /** + * @brief On platforms that need it enable low level multicast listening + * @jsonapi{development} + * On Android and potencially other mobile platforms, WiFi drivers are + * configured by default to discard any packet that is not directed to the + * unicast mac address of the interface, this way they could save some + * battery but breaks anything that is not unicast, such as broadcast + * discovery. On such platforms this method enable low level multicast + * listening so we can receive advertisement from same broadcast domain + * nodes. + * On platforms without such limitation does nothing and always return + * false. + * It is exposed as a public API so the UI can decide the right equilibrium + * between discoverability and battery saving. + * @return true if multicast listening has been enabled due to this call, + * false otherwise. + */ + virtual bool enableMulticastListening() = 0; + + /** + * @brief Disable multicast listening + * @jsonapi{development} + * The opposite of @see enableMulticastListening. + * @return true if multicast listening has been disabled due to this call, + * false otherwise. + */ + virtual bool disableMulticastListening() = 0; + virtual ~RsBroadcastDiscovery(); }; diff --git a/libretroshare/src/services/broadcastdiscoveryservice.cc b/libretroshare/src/services/broadcastdiscoveryservice.cc index 526610ced..d49e04a17 100644 --- a/libretroshare/src/services/broadcastdiscoveryservice.cc +++ b/libretroshare/src/services/broadcastdiscoveryservice.cc @@ -25,6 +25,10 @@ #include #include +#ifdef __ANDROID__ +# include +#endif // def __ANDROID__ + #include "services/broadcastdiscoveryservice.h" #include "retroshare/rspeers.h" #include "serialiser/rsserializable.h" @@ -94,6 +98,12 @@ BroadcastDiscoveryService::BroadcastDiscoveryService( { if(mRsPeers.isHiddenNode(mRsPeers.getOwnId())) return; +#ifdef __ANDROID__ + createMulticastLock(); +#endif // def __ANDROID__ + + enableMulticastListening(); + mUdcParameters.set_can_discover(true); mUdcParameters.set_can_be_discovered(true); mUdcParameters.set_port(port); @@ -104,7 +114,10 @@ BroadcastDiscoveryService::BroadcastDiscoveryService( } BroadcastDiscoveryService::~BroadcastDiscoveryService() -{ mUdcPeer.Stop(true); } +{ + mUdcPeer.Stop(true); + disableMulticastListening(); +} std::vector BroadcastDiscoveryService::getDiscoveredPeers() @@ -192,6 +205,7 @@ RsBroadcastDiscoveryResult BroadcastDiscoveryService::createResult( BroadcastDiscoveryPack::fromSerializedString(uData); RsBroadcastDiscoveryResult rbdr; + rbdr.mPgpFingerprint = bdp.mPgpFingerprint; rbdr.mSslId = bdp.mSslId; rbdr.mProfileName = bdp.mProfileName; rbdr.mLocator. @@ -202,6 +216,95 @@ RsBroadcastDiscoveryResult BroadcastDiscoveryService::createResult( return rbdr; } +bool BroadcastDiscoveryService::isMulticastListeningEnabled() +{ +#ifdef __ANDROID__ + return assertMulticastLockIsvalid() && + mWifiMulticastLock.callMethod("isHeld"); +#endif // def __ANDROID__ + + return true; +} + +bool BroadcastDiscoveryService::enableMulticastListening() +{ +#ifdef __ANDROID__ + if(assertMulticastLockIsvalid() && !isMulticastListeningEnabled()) + { + mWifiMulticastLock.callMethod("acquire"); + return true; + } +#endif // def __ANDROID__ + + return false; +} + +bool BroadcastDiscoveryService::disableMulticastListening() +{ +#ifdef __ANDROID__ + if(assertMulticastLockIsvalid() && isMulticastListeningEnabled()) + { + mWifiMulticastLock.callMethod("release"); + return true; + } +#endif // def __ANDROID__ + + return false; +} + +#ifdef __ANDROID__ +bool BroadcastDiscoveryService::createMulticastLock() +{ + Dbg2() << __PRETTY_FUNCTION__ << std::endl; + + constexpr auto fname = __PRETTY_FUNCTION__; + const auto failure = [&](const std::string& err) + { + RsErr() << fname << " " << err << std::endl; + return false; + }; + + if(mWifiMulticastLock.isValid()) + return failure("mWifiMulticastLock is already initialized"); + + QAndroidJniObject context = QtAndroid::androidContext(); + if(!context.isValid()) + return failure("Cannot retrieve Android context"); + + QAndroidJniObject WIFI_SERVICE = QAndroidJniObject::getStaticObjectField( + "android.content.Context", "WIFI_SERVICE", "Ljava/lang/String;"); + if(!WIFI_SERVICE.isValid()) + return failure("Cannot retrieve Context.WIFI_SERVICE value"); + + QAndroidJniObject wifiManager = context.callObjectMethod( + "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;", + WIFI_SERVICE.object() ); + if(!wifiManager.isValid()) + return failure("Cannot retrieve Android Wifi Manager"); + + mWifiMulticastLock = wifiManager.callObjectMethod( + "createMulticastLock", + "(Ljava/lang/String;)Landroid/net/wifi/WifiManager$MulticastLock;", + QAndroidJniObject::fromString(fname).object() ); + if(!mWifiMulticastLock.isValid()) + return failure("Cannot create WifiManager.MulticastLock"); + + return true; +} + +bool BroadcastDiscoveryService::assertMulticastLockIsvalid() +{ + if(!mWifiMulticastLock.isValid()) + { + RsErr() << __PRETTY_FUNCTION__ << " mWifiMulticastLock is invalid!" + << std::endl; + print_stacktrace(); + return false; + } + return true; +} +#endif // def __ANDROID__ + RsBroadcastDiscovery::~RsBroadcastDiscovery() = default; RsBroadcastDiscoveryResult::~RsBroadcastDiscoveryResult() = default; RsBroadcastDiscoveryPeerFoundEvent::~RsBroadcastDiscoveryPeerFoundEvent() = default; diff --git a/libretroshare/src/services/broadcastdiscoveryservice.h b/libretroshare/src/services/broadcastdiscoveryservice.h index 17129c995..f7c52466a 100644 --- a/libretroshare/src/services/broadcastdiscoveryservice.h +++ b/libretroshare/src/services/broadcastdiscoveryservice.h @@ -27,8 +27,13 @@ #include +#ifdef __ANDROID__ +# include +#endif // def __ANDROID__ + #include "retroshare/rsbroadcastdiscovery.h" #include "util/rsthreads.h" +#include "util/rsdebug.h" namespace UDC = udpdiscovery; class RsPeers; @@ -37,13 +42,21 @@ class BroadcastDiscoveryService : public RsBroadcastDiscovery, public RsTickingThread { public: - // TODO: std::shared_ptr mRsPeers; BroadcastDiscoveryService(RsPeers& pRsPeers); - virtual ~BroadcastDiscoveryService() override; + ~BroadcastDiscoveryService() override; /// @see RsBroadcastDiscovery std::vector getDiscoveredPeers() override; + /// @see RsBroadcastDiscovery + bool isMulticastListeningEnabled() override; + + /// @see RsBroadcastDiscovery + bool enableMulticastListening() override; + + /// @see RsBroadcastDiscovery + bool disableMulticastListening() override; + /// @see RsTickingThread void data_tick() override; @@ -63,4 +76,22 @@ protected: RsBroadcastDiscoveryResult createResult( const UDC::IpPort& ipp, const std::string& uData ); + +#ifdef __ANDROID__ + /** Android WifiManager.MulticastLock */ + QAndroidJniObject mWifiMulticastLock; + + /** Initialize the wifi multicast lock without acquiring it + * Needed to enable multicast listening in Android, for RetroShare broadcast + * discovery inspired by: + * https://github.com/flutter/flutter/issues/16335#issuecomment-420547860 + */ + bool createMulticastLock(); + + /** Return false if mWifiMulticastLock is invalid and print error messages */ + bool assertMulticastLockIsvalid(); + +#endif // def __ANDROID__ + + RS_SET_CONTEXT_DEBUG_LEVEL(3) }; diff --git a/retroshare-service/src/android/AndroidManifest.xml b/retroshare-service/src/android/AndroidManifest.xml index cbf2676fa..d41a72995 100644 --- a/retroshare-service/src/android/AndroidManifest.xml +++ b/retroshare-service/src/android/AndroidManifest.xml @@ -85,4 +85,8 @@ + + +