2 * Copyright (C) 2017 Christian Browet
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
9 #include "ZeroconfBrowserAndroid.h"
11 #include "ServiceBroker.h"
12 #include "interfaces/AnnouncementManager.h"
13 #include "network/DNSNameCache.h"
14 #include "utils/log.h"
18 #include <androidjni/Context.h>
19 #include <androidjni/jutils-details.hpp>
21 CZeroconfBrowserAndroid::CZeroconfBrowserAndroid()
22 : m_manager(CJNIContext::getSystemService(CJNIContext::NSD_SERVICE
))
26 CZeroconfBrowserAndroid::~CZeroconfBrowserAndroid()
28 std::unique_lock
<CCriticalSection
> lock(m_data_guard
);
29 //make sure there are no browsers anymore
30 for (const auto& it
: m_service_browsers
)
31 doRemoveServiceType(it
.first
);
34 bool CZeroconfBrowserAndroid::doAddServiceType(const std::string
& fcr_service_type
)
36 CZeroconfBrowserAndroidDiscover
* discover
= new CZeroconfBrowserAndroidDiscover(this);
38 // Remove trailing dot
39 // std::string nsdType = fcr_service_type;
40 // if (nsdType.compare(nsdType.size() - 1, 1, ".") == 0)
41 // nsdType = nsdType.substr(0, nsdType.size() - 1);
43 CLog::Log(LOGDEBUG
, "CZeroconfBrowserAndroid::doAddServiceType: {}", fcr_service_type
);
44 m_manager
.discoverServices(fcr_service_type
, 1 /* PROTOCOL_DNS_SD */, *discover
);
48 std::unique_lock
<CCriticalSection
> lock(m_data_guard
);
49 m_service_browsers
.insert(std::make_pair(fcr_service_type
, discover
));
54 bool CZeroconfBrowserAndroid::doRemoveServiceType(const std::string
& fcr_service_type
)
56 CLog::Log(LOGDEBUG
, "CZeroconfBrowserAndroid::doRemoveServiceType: {}", fcr_service_type
);
58 CZeroconfBrowserAndroidDiscover
* discover
;
59 //search for this browser and remove it from the map
61 std::unique_lock
<CCriticalSection
> lock(m_data_guard
);
62 tBrowserMap::iterator it
= m_service_browsers
.find(fcr_service_type
);
63 if(it
== m_service_browsers
.end())
67 discover
= it
->second
;
68 if (discover
->IsActive())
69 m_manager
.stopServiceDiscovery(*discover
);
70 // Be extra careful: Discover listener is gone as of now
71 m_service_browsers
.erase(it
);
74 //remove the services of this browser
76 std::unique_lock
<CCriticalSection
> lock(m_data_guard
);
77 tDiscoveredServicesMap::iterator it
= m_discovered_services
.find(discover
);
78 if(it
!= m_discovered_services
.end())
79 m_discovered_services
.erase(it
);
86 std::vector
<CZeroconfBrowser::ZeroconfService
> CZeroconfBrowserAndroid::doGetFoundServices()
88 std::vector
<CZeroconfBrowser::ZeroconfService
> ret
;
89 std::unique_lock
<CCriticalSection
> lock(m_data_guard
);
90 for (const auto& it
: m_discovered_services
)
92 const auto& services
= it
.second
;
93 for (const auto& service
: services
)
95 ret
.push_back(service
.first
);
101 bool CZeroconfBrowserAndroid::doResolveService(CZeroconfBrowser::ZeroconfService
& fr_service
, double f_timeout
)
103 jni::CJNINsdServiceInfo service
;
104 service
.setServiceName(fr_service
.GetName());
105 service
.setServiceType(fr_service
.GetType());
107 CZeroconfBrowserAndroidResolve resolver
;
108 m_manager
.resolveService(service
, resolver
);
110 if (!resolver
.m_resolutionDone
.Wait(std::chrono::duration
<double, std::milli
>(f_timeout
* 1000)))
112 CLog::Log(LOGERROR
, "ZeroconfBrowserAndroid: DNSServiceResolve Timeout error");
116 if (resolver
.m_errorCode
!= -1)
118 CLog::Log(LOGERROR
, "ZeroconfBrowserAndroid: DNSServiceResolve returned (error = {})",
119 resolver
.m_errorCode
);
123 fr_service
.SetHostname(resolver
.m_retServiceInfo
.getHost().getHostName());
124 fr_service
.SetIP(resolver
.m_retServiceInfo
.getHost().getHostAddress());
125 fr_service
.SetPort(resolver
.m_retServiceInfo
.getPort());
127 CZeroconfBrowser::ZeroconfService::tTxtRecordMap recordMap
;
128 jni::CJNISet
<jni::jhstring
> txtKey
= resolver
.m_retServiceInfo
.getAttributes().keySet();
129 jni::CJNIIterator
<jni::jhstring
> it
= txtKey
.iterator();
132 jni::jhstring k
= it
.next();
133 jni::jhbyteArray v
= resolver
.m_retServiceInfo
.getAttributes().get(k
);
135 std::string key
= jni::jcast
<std::string
>(k
);
136 std::vector
<char> vv
= jni::jcast
<std::vector
<char>>(v
);
137 std::string value
= std::string(vv
.begin(), vv
.end());
139 CLog::Log(LOGDEBUG
, "ZeroconfBrowserAndroid: TXT record {} = {} ({})", key
, value
, vv
.size());
141 recordMap
.insert(std::make_pair(key
, value
));
143 fr_service
.SetTxtRecords(recordMap
);
144 return (!fr_service
.GetIP().empty());
147 /// adds the service to list of found services
148 void CZeroconfBrowserAndroid::addDiscoveredService(CZeroconfBrowserAndroidDiscover
* browser
, CZeroconfBrowser::ZeroconfService
const& fcr_service
)
150 std::unique_lock
<CCriticalSection
> lock(m_data_guard
);
151 tDiscoveredServicesMap::iterator browserIt
= m_discovered_services
.find(browser
);
152 if(browserIt
== m_discovered_services
.end())
154 //first service by this browser
155 browserIt
= m_discovered_services
.insert(make_pair(browser
, std::vector
<std::pair
<ZeroconfService
, unsigned int> >())).first
;
157 //search this service
158 std::vector
<std::pair
<ZeroconfService
, unsigned int> >& services
= browserIt
->second
;
159 std::vector
<std::pair
<ZeroconfService
, unsigned int> >::iterator serviceIt
= services
.begin();
160 for( ; serviceIt
!= services
.end(); ++serviceIt
)
162 if(serviceIt
->first
== fcr_service
)
165 if(serviceIt
== services
.end())
166 services
.emplace_back(fcr_service
, 1);
171 void CZeroconfBrowserAndroid::removeDiscoveredService(CZeroconfBrowserAndroidDiscover
* browser
, CZeroconfBrowser::ZeroconfService
const& fcr_service
)
173 std::unique_lock
<CCriticalSection
> lock(m_data_guard
);
174 tDiscoveredServicesMap::iterator browserIt
= m_discovered_services
.find(browser
);
175 //search this service
176 std::vector
<std::pair
<ZeroconfService
, unsigned int> >& services
= browserIt
->second
;
177 std::vector
<std::pair
<ZeroconfService
, unsigned int> >::iterator serviceIt
= services
.begin();
178 for( ; serviceIt
!= services
.end(); ++serviceIt
)
179 if(serviceIt
->first
== fcr_service
)
181 if(serviceIt
!= services
.end())
185 if(!serviceIt
->second
)
187 //eventually remove the service
188 services
.erase(serviceIt
);
192 //looks like we missed the announce, no problem though..
197 /******************************************************************/
199 CZeroconfBrowserAndroidDiscover::CZeroconfBrowserAndroidDiscover(CZeroconfBrowserAndroid
* browser
)
200 : CJNIXBMCNsdManagerDiscoveryListener(), m_browser(browser
)
204 void CZeroconfBrowserAndroidDiscover::onDiscoveryStarted(const std::string
& serviceType
)
206 CLog::Log(LOGDEBUG
, "CZeroconfBrowserAndroidDiscover::onDiscoveryStarted type: {}", serviceType
);
210 void CZeroconfBrowserAndroidDiscover::onDiscoveryStopped(const std::string
& serviceType
)
212 CLog::Log(LOGDEBUG
, "CZeroconfBrowserAndroidDiscover::onDiscoveryStopped type: {}", serviceType
);
216 void CZeroconfBrowserAndroidDiscover::onServiceFound(const jni::CJNINsdServiceInfo
& serviceInfo
)
219 CZeroconfBrowser::ZeroconfService
s(serviceInfo
.getServiceName(), serviceInfo
.getServiceType(), "local");
220 jni::CJNISet
<jni::jhstring
> txtKey
= serviceInfo
.getAttributes().keySet();
221 jni::CJNIIterator
<jni::jhstring
> it
= txtKey
.iterator();
224 jni::jhstring k
= it
.next();
225 jni::jhbyteArray v
= serviceInfo
.getAttributes().get(k
);
227 std::string key
= jni::jcast
<std::string
>(k
);
228 std::vector
<char> vv
= jni::jcast
<std::vector
<char>>(v
);
229 std::string value
= std::string(vv
.begin(), vv
.end());
231 CLog::Log(LOGDEBUG
, "ZeroconfBrowserAndroid::onServiceFound: TXT record {} = {} ({})", key
,
236 "CZeroconfBrowserAndroidDiscover::onServiceFound found service named: {}, type: {}, "
238 s
.GetName(), s
.GetType(), s
.GetDomain());
239 m_browser
->addDiscoveredService(this, s
);
241 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Sources
, "OnUpdated",
242 CVariant
{"zeroconf://"});
243 CLog::Log(LOGDEBUG
, "CZeroconfBrowserAndroidDiscover::onServiceFound sent source update announce "
244 "for path zeroconf://");
247 void CZeroconfBrowserAndroidDiscover::onServiceLost(const jni::CJNINsdServiceInfo
& serviceInfo
)
249 CLog::Log(LOGDEBUG
, "CZeroconfBrowserAndroidDiscover::onServiceLost name: {}, type: {}",
250 serviceInfo
.getServiceName(), serviceInfo
.getServiceType());
253 void CZeroconfBrowserAndroidDiscover::onStartDiscoveryFailed(const std::string
& serviceType
, int errorCode
)
255 CLog::Log(LOGDEBUG
, "CZeroconfBrowserAndroidDiscover::onStartDiscoveryFailed type: {}, error: {}",
256 serviceType
, errorCode
);
260 void CZeroconfBrowserAndroidDiscover::onStopDiscoveryFailed(const std::string
& serviceType
, int errorCode
)
262 CLog::Log(LOGDEBUG
, "CZeroconfBrowserAndroidDiscover::onStopDiscoveryFailed type: {}, error: {}",
263 serviceType
, errorCode
);
267 /***************************************/
269 CZeroconfBrowserAndroidResolve::CZeroconfBrowserAndroidResolve()
270 : CJNIXBMCNsdManagerResolveListener()
274 void CZeroconfBrowserAndroidResolve::onResolveFailed(const jni::CJNINsdServiceInfo
& serviceInfo
, int errorCode
)
277 "CZeroconfBrowserAndroidResolve::onResolveFailed name: {}, type: {}, error: {}",
278 serviceInfo
.getServiceName(), serviceInfo
.getServiceType(), errorCode
);
279 m_errorCode
= errorCode
;
280 m_resolutionDone
.Set();
283 void CZeroconfBrowserAndroidResolve::onServiceResolved(const jni::CJNINsdServiceInfo
& serviceInfo
)
285 CLog::Log(LOGDEBUG
, "CZeroconfBrowserAndroidResolve::onServiceResolved name: {}, type: {}",
286 serviceInfo
.getServiceName(), serviceInfo
.getServiceType());
288 m_retServiceInfo
= serviceInfo
;
289 m_resolutionDone
.Set();