forked from jbagg/QtZeroConf
Compare commits
32 Commits
android-ap
...
fix_warnin
Author | SHA1 | Date | |
---|---|---|---|
2fbb52b708 | |||
12d2ffd1fe | |||
475aedb0bd | |||
83df89fabe | |||
9fbb8dcbc1 | |||
a2b907b95f | |||
38083c6126 | |||
d466206fb3 | |||
9e8addde36 | |||
65e0713bc3 | |||
4fada0fb2a | |||
58d9ca7e7b | |||
39e96235ce | |||
6df17b9760 | |||
da386b2ac3 | |||
f89c73695e | |||
61f5676248 | |||
2a7a8fb9d8 | |||
966877a9a0 | |||
3f5650388c | |||
24debe31b0 | |||
473d8520f9 | |||
7b066b1aed | |||
3d7b094b75 | |||
668f7358c4 | |||
2318fb1987 | |||
81253d92da | |||
d807d5ab37 | |||
0889adbc23 | |||
c75cd78cff | |||
16b50c700f | |||
29be12c558 |
@ -1,7 +1,8 @@
|
||||
project(QtZeroConf)
|
||||
cmake_minimum_required(VERSION 2.8.11)
|
||||
cmake_minimum_required(VERSION 3.4)
|
||||
project(QtZeroConf VERSION 0.1.0)
|
||||
|
||||
find_package(Qt5 COMPONENTS Core Network)
|
||||
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core Network)
|
||||
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core Network)
|
||||
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
@ -16,25 +17,30 @@ add_library(QtZeroConf
|
||||
qzeroconfservice.cpp
|
||||
)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
if(BUILD_SHARED_LIBS)
|
||||
target_compile_definitions(QtZeroConf PRIVATE QT_BUILD_ZEROCONF_LIB)
|
||||
set_target_properties(QtZeroConf PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION 0)
|
||||
else()
|
||||
target_compile_definitions(QtZeroConf PUBLIC QZEROCONF_STATIC)
|
||||
endif()
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
|
||||
find_library(avahi-client-lib avahi-client REQUIRED)
|
||||
find_library(avahi-common-lib avahi-common REQUIRED)
|
||||
find_path(avahi-client-includes avahi-client/client.h REQUIRED)
|
||||
find_path(avahi-common-includes avahi-common/defs.h REQUIRED)
|
||||
target_sources(QtZeroConf PRIVATE
|
||||
avahi-qt/qt-watch_p.h
|
||||
avahi-qt/qt-watch.cpp
|
||||
avahiclient.cpp
|
||||
)
|
||||
target_include_directories(QtZeroConf PRIVATE ${avahi-client-includes})
|
||||
target_include_directories(QtZeroConf PRIVATE ${avahi-client-includes} ${avahi-common-includes})
|
||||
list(APPEND ${PUBLIC_HEADERS}
|
||||
avahi-qt/qt-watch.h
|
||||
)
|
||||
target_link_libraries(QtZeroConf PRIVATE ${avahi-client-lib})
|
||||
target_link_libraries(QtZeroConf PRIVATE ${avahi-client-lib} ${avahi-common-lib})
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
@ -48,9 +54,9 @@ endif()
|
||||
|
||||
target_include_directories(QtZeroConf PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||
$<INSTALL_INTERFACE:include>
|
||||
$<INSTALL_INTERFACE:include/QtZeroConf>
|
||||
)
|
||||
target_link_libraries(QtZeroConf PUBLIC Qt5::Core Qt5::Network)
|
||||
target_link_libraries(QtZeroConf PUBLIC Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Network)
|
||||
|
||||
if(WIN32)
|
||||
target_sources(QtZeroConf PRIVATE
|
||||
@ -77,8 +83,7 @@ if(WIN32)
|
||||
target_include_directories(QtZeroConf PRIVATE "${CMAKE_CURRENT_LIST_DIR}/bonjour-sdk")
|
||||
endif()
|
||||
|
||||
|
||||
if(ANDROID)
|
||||
if(ANDROID_AVAHI)
|
||||
set(ACM "${CMAKE_CURRENT_LIST_DIR}/avahi-common")
|
||||
set(ACR "${CMAKE_CURRENT_LIST_DIR}/avahi-core")
|
||||
target_sources(QtZeroConf PRIVATE
|
||||
@ -137,17 +142,29 @@ if(ANDROID)
|
||||
avahi-qt/qt-watch.h
|
||||
)
|
||||
target_compile_definitions(QtZeroConf PRIVATE HAVE_STRLCPY GETTEXT_PACKAGE HAVE_NETLINK)
|
||||
elseif(ANDROID)
|
||||
target_sources(QtZeroConf PRIVATE
|
||||
qzeroconf.h
|
||||
androidnsd_p.h
|
||||
androidnsd.cpp
|
||||
)
|
||||
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Gui)
|
||||
target_link_libraries(QtZeroConf PUBLIC Qt${QT_VERSION_MAJOR}::Gui)
|
||||
if (QT_VERSION_MAJOR EQUAL 5)
|
||||
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS AndroidExtras)
|
||||
target_link_libraries(QtZeroConf PUBLIC Qt${QT_VERSION_MAJOR}::AndroidExtras)
|
||||
endif ()
|
||||
endif()
|
||||
|
||||
# install
|
||||
set(INSTALL_CMAKEDIR "lib/cmake/${PROJECT_NAME}" CACHE STRING "Installation directory for cmake config files")
|
||||
set(INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" CACHE STRING "Installation directory for cmake config files")
|
||||
set_target_properties(QtZeroConf PROPERTIES PUBLIC_HEADER
|
||||
"${PUBLIC_HEADERS}"
|
||||
)
|
||||
install(TARGETS QtZeroConf
|
||||
EXPORT QtZeroConfConfig
|
||||
LIBRARY DESTINATION lib
|
||||
PUBLIC_HEADER DESTINATION include/QtZeroConf
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}
|
||||
)
|
||||
export(TARGETS QtZeroConf
|
||||
FILE ${CMAKE_CURRENT_BINARY_DIR}/QtZeroConfConfig.cmake
|
||||
|
217
QZeroConfNsdManager.java
Normal file
217
QZeroConfNsdManager.java
Normal file
@ -0,0 +1,217 @@
|
||||
/**************************************************************************************************
|
||||
---------------------------------------------------------------------------------------------------
|
||||
Copyright (C) 2021 Jonathan Bagg
|
||||
This file is part of QtZeroConf.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this list of
|
||||
conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
conditions and the following disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
* Neither the name of Jonathan Bagg nor the names of its contributors may be used to
|
||||
endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
---------------------------------------------------------------------------------------------------
|
||||
Project name : QtZeroConf
|
||||
File name : QZeroConfNsdManager.java
|
||||
Created : 10 Spetember 2021
|
||||
Author(s) : Michael Zanetti
|
||||
---------------------------------------------------------------------------------------------------
|
||||
NsdManager wrapper for use on Android devices
|
||||
---------------------------------------------------------------------------------------------------
|
||||
**************************************************************************************************/
|
||||
|
||||
package qtzeroconf;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import android.net.nsd.NsdServiceInfo;
|
||||
import android.net.nsd.NsdManager;
|
||||
|
||||
public class QZeroConfNsdManager {
|
||||
|
||||
public static native void onServiceResolvedJNI(long id, String name, String type, String hostname, String address, int port, Map<String, byte[]> txtRecords);
|
||||
public static native void onServiceRemovedJNI(long id, String name);
|
||||
public static native void onBrowserStateChangedJNI(long id, boolean running, boolean error);
|
||||
public static native void onPublisherStateChangedJNI(long id, boolean running, boolean error);
|
||||
public static native void onServiceNameChangedJNI(long id, String newName);
|
||||
|
||||
private static String TAG = "QZeroConfNsdManager";
|
||||
private long id;
|
||||
private Context context;
|
||||
private NsdManager nsdManager;
|
||||
private NsdManager.DiscoveryListener discoveryListener;
|
||||
private NsdManager.RegistrationListener registrationListener;
|
||||
private String registrationName; // The original service name that was given for registration, it might change on collisions
|
||||
|
||||
// There can only be one resolver at a time per application, we'll need to queue the resolving
|
||||
static private ArrayList<NsdServiceInfo> resolverQueue = new ArrayList<NsdServiceInfo>();
|
||||
static private NsdServiceInfo pendingResolve = null;
|
||||
|
||||
public QZeroConfNsdManager(long id, Context context) {
|
||||
super();
|
||||
this.id = id;
|
||||
this.context = context;
|
||||
|
||||
nsdManager = (NsdManager)context.getSystemService(Context.NSD_SERVICE);
|
||||
discoveryListener = initializeDiscoveryListener();
|
||||
registrationListener = initializeRegistrationListener();
|
||||
}
|
||||
|
||||
public void registerService(String name, String type, int port, Map<String, String> txtRecords) {
|
||||
registrationName = name;
|
||||
|
||||
NsdServiceInfo serviceInfo = new NsdServiceInfo();
|
||||
serviceInfo.setServiceName(name);
|
||||
serviceInfo.setServiceType(type);
|
||||
serviceInfo.setPort(port);
|
||||
for (Map.Entry<String, String> entry: txtRecords.entrySet()) {
|
||||
serviceInfo.setAttribute(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
try {
|
||||
nsdManager.registerService(serviceInfo, NsdManager.PROTOCOL_DNS_SD, registrationListener);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log.w(TAG, "Error registering service: " + e.toString());
|
||||
onPublisherStateChangedJNI(id, false, true);
|
||||
}
|
||||
}
|
||||
|
||||
public void unregisterService() {
|
||||
nsdManager.unregisterService(registrationListener);
|
||||
}
|
||||
|
||||
public void discoverServices(String serviceType) {
|
||||
nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD, discoveryListener);
|
||||
}
|
||||
|
||||
public void stopServiceDiscovery() {
|
||||
nsdManager.stopServiceDiscovery(discoveryListener);
|
||||
}
|
||||
|
||||
private NsdManager.DiscoveryListener initializeDiscoveryListener() {
|
||||
return new NsdManager.DiscoveryListener() {
|
||||
|
||||
@Override
|
||||
public void onDiscoveryStarted(String regType) {
|
||||
QZeroConfNsdManager.onBrowserStateChangedJNI(id, true, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceFound(NsdServiceInfo service) {
|
||||
enqueueResolver(service);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceLost(NsdServiceInfo serviceInfo) {
|
||||
QZeroConfNsdManager.onServiceRemovedJNI(id, serviceInfo.getServiceName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDiscoveryStopped(String serviceType) {
|
||||
QZeroConfNsdManager.onBrowserStateChangedJNI(id, false, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartDiscoveryFailed(String serviceType, int errorCode) {
|
||||
QZeroConfNsdManager.onBrowserStateChangedJNI(id, false, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopDiscoveryFailed(String serviceType, int errorCode) {
|
||||
QZeroConfNsdManager.onBrowserStateChangedJNI(id, false, true);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private NsdManager.ResolveListener initializeResolveListener() {
|
||||
return new NsdManager.ResolveListener() {
|
||||
|
||||
@Override
|
||||
public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
|
||||
Log.w(TAG, "Resolving failed for: " + serviceInfo.getServiceName() + " " + serviceInfo.getServiceType() + ": " + errorCode);
|
||||
if (errorCode == NsdManager.FAILURE_ALREADY_ACTIVE) {
|
||||
enqueueResolver(pendingResolve);
|
||||
}
|
||||
pendingResolve = null;
|
||||
processResolverQueue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceResolved(NsdServiceInfo serviceInfo) {
|
||||
QZeroConfNsdManager.onServiceResolvedJNI(id,
|
||||
serviceInfo.getServiceName(),
|
||||
serviceInfo.getServiceType(),
|
||||
serviceInfo.getHost().getHostName(),
|
||||
serviceInfo.getHost().getHostAddress(),
|
||||
serviceInfo.getPort(),
|
||||
serviceInfo.getAttributes()
|
||||
);
|
||||
pendingResolve = null;
|
||||
processResolverQueue();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public NsdManager.RegistrationListener initializeRegistrationListener() {
|
||||
return new NsdManager.RegistrationListener() {
|
||||
|
||||
@Override
|
||||
public void onServiceRegistered(NsdServiceInfo serviceInfo) {
|
||||
QZeroConfNsdManager.onPublisherStateChangedJNI(id, true, false);
|
||||
if (!serviceInfo.getServiceName().equals(registrationName)) {
|
||||
onServiceNameChangedJNI(id, serviceInfo.getServiceName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
|
||||
QZeroConfNsdManager.onPublisherStateChangedJNI(id, false, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceUnregistered(NsdServiceInfo arg0) {
|
||||
QZeroConfNsdManager.onPublisherStateChangedJNI(id, false, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
|
||||
QZeroConfNsdManager.onPublisherStateChangedJNI(id, false, true);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void enqueueResolver(NsdServiceInfo serviceInfo) {
|
||||
resolverQueue.add(serviceInfo);
|
||||
processResolverQueue();
|
||||
}
|
||||
|
||||
private void processResolverQueue() {
|
||||
if (resolverQueue.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (pendingResolve != null) {
|
||||
return;
|
||||
}
|
||||
pendingResolve = resolverQueue.get(0);
|
||||
resolverQueue.remove(0);
|
||||
nsdManager.resolveService(pendingResolve, initializeResolveListener());
|
||||
}
|
||||
}
|
28
README.md
28
README.md
@ -7,7 +7,7 @@ QZeroConf is a Qt wrapper class for ZeroConf libraries across various platforms.
|
||||
* Android
|
||||
* iOS
|
||||
|
||||
QZeroConf wraps avahi-client on Linux, avahi-core on Android, and dnssd on Mac, iOS and Windows.
|
||||
QZeroConf wraps avahi-client on Linux, Network Discovery Service (java) on Android, and dnssd on Mac, iOS and Windows.
|
||||
|
||||
### Building
|
||||
|
||||
@ -37,6 +37,15 @@ The default is `OFF`.
|
||||
You can also build the included example project by setting `BUILD_EXAMPLE` to `ON`.
|
||||
The default for this is `OFF`
|
||||
|
||||
#### Android
|
||||
|
||||
Prior to Android api 30, QtZeroConf used AvaliCore. AvaliCore no longer works >= api 30 as bind() to netlink sockets was disabled in Android. QtZeroConf now uses the Android java Network Discovery Services. NDS is slightly buggy, but more or less gets the job done. A common issue with NDS is that if the app is in sleep mode and a service is removed on another device, the app does not get notified the service was removed when it wakes back up. ANDROID_PACKAGE_SOURCE_DIR must be added to your app's .pro file.
|
||||
|
||||
```
|
||||
ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android
|
||||
```
|
||||
QZeroConfNsdManager.java must then be copied or linked to $$PWD/android/src/QZeroConfNsdManager.java Notice the extra src/ ...gradle expects this.
|
||||
|
||||
### API
|
||||
|
||||
#### Service Publishing
|
||||
@ -116,13 +125,26 @@ QZeroConf can be used in QML applications
|
||||
|
||||
### Build Dependencies
|
||||
|
||||
Qt5
|
||||
Qt5 or Qt6
|
||||
|
||||
On Linux, libavahi-client-dev and libavahi-common-dev
|
||||
|
||||
### Distribution Requirements
|
||||
|
||||
|
||||
Distributing Software / Apps that use QtZeroConf must follow the requirements of the LGPLv3. Some of my interpretations of the LGPLv3
|
||||
|
||||
|
||||
* LGPLv3 text or a link to the LGPLv3 text must be distributed with the binary. My preferred way to do this is in the App's "About" section.
|
||||
* An offer of source code with a link to QtZeroConf must be distributed with the binary. My preferred way to do this is in the App's "About" section
|
||||
* For Android and iOS apps only, instructions on how to re-link or re-package the App or a link to instructions must be distributed with the binary. My preferred way to do this is in the App's "About" section.
|
||||
|
||||
|
||||
All of the above must be shown to the user at least once, separate from the EULA, with a method for the user to acknowledge they have seen it (an ok button). Ideally all of the above is also listed in the description of the App on the App Store.
|
||||
|
||||
### Apple App Store deployment
|
||||
|
||||
Publishing GPL software in the App Store is a [violation of the GPL](https://news.ycombinator.com/item?id=3488833). If you need to publish an app in the Apple App Store that uses QZeroConf, please contact me for a copy of QZeroConf with a BSD licence.
|
||||
Publishing closed source Apps that use a LGPLv3 library in the Apple App Store must provide a method for the end user to 1. update the library in the app and 2. run the new version of the app with the updated library. Qt on iOS further complicates this by using static linking. Closed source Apps on iOS using QtZeroConf must provide the apps object files along with clear step by step instructions on how to re-link the app with a new / different version of QtZeroConf (obligation 1). iOS end uses can run the re-linked App on their device by creating a free iOS developer account and use the time limited signing on that account for their device. (obligation 2) I consider this an poor way to meet obligation 2, but as long as Apple has this mechanism, obligation 2 is meet. I will not pursue copyright infringement as long as the individual / organization is meeting obligation 1 and 2 and the Distribution Requirements above.
|
||||
|
||||
### iOS device sleep
|
||||
|
||||
|
368
androidnsd.cpp
Normal file
368
androidnsd.cpp
Normal file
@ -0,0 +1,368 @@
|
||||
/**************************************************************************************************
|
||||
---------------------------------------------------------------------------------------------------
|
||||
Copyright (C) 2015-2021 Jonathan Bagg
|
||||
This file is part of QtZeroConf.
|
||||
|
||||
QtZeroConf is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
QtZeroConf is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with QtZeroConf. If not, see <http://www.gnu.org/licenses/>.
|
||||
---------------------------------------------------------------------------------------------------
|
||||
Project name : QtZeroConf
|
||||
File name : androidnsd.cpp
|
||||
Created : 10 Spetember 2021
|
||||
Author(s) : Michael Zanetti
|
||||
---------------------------------------------------------------------------------------------------
|
||||
NsdManager wrapper for use on Android devices
|
||||
---------------------------------------------------------------------------------------------------
|
||||
**************************************************************************************************/
|
||||
#include <QGuiApplication>
|
||||
#include <QRegularExpression>
|
||||
#include "androidnsd_p.h"
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
using QAndroidJniEnvironment = QJniEnvironment;
|
||||
#endif
|
||||
|
||||
Q_DECLARE_METATYPE(QHostAddress)
|
||||
|
||||
static QMutex s_instancesMutex;
|
||||
static QList<QZeroConfPrivate*> s_instances;
|
||||
|
||||
|
||||
QZeroConfPrivate::QZeroConfPrivate(QZeroConf *parent)
|
||||
{
|
||||
qRegisterMetaType<QHostAddress>();
|
||||
qRegisterMetaType<TxtRecordMap>("TxtRecordMap");
|
||||
|
||||
pub = parent;
|
||||
|
||||
QAndroidJniEnvironment env;
|
||||
|
||||
JNINativeMethod methods[] {
|
||||
{ "onServiceResolvedJNI", "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/util/Map;)V", (void*)QZeroConfPrivate::onServiceResolvedJNI },
|
||||
{ "onServiceRemovedJNI", "(JLjava/lang/String;)V", (void*)QZeroConfPrivate::onServiceRemovedJNI },
|
||||
{ "onBrowserStateChangedJNI", "(JZZ)V", (void*)QZeroConfPrivate::onBrowserStateChangedJNI },
|
||||
{ "onPublisherStateChangedJNI", "(JZZ)V", (void*)QZeroConfPrivate::onPublisherStateChangedJNI },
|
||||
{ "onServiceNameChangedJNI", "(JLjava/lang/String;)V", (void*)QZeroConfPrivate::onServiceNameChangedJNI }
|
||||
};
|
||||
|
||||
// There seems to be no straight forward way to match the "thiz" pointer from JNI calls to our pointer of the Java class
|
||||
// Passing "this" as ID down to Java so we can access "this" in callbacks.
|
||||
// Note: needs to be quint64 as uintptr_t might be 32 or 64 bit depending on the system, while Java expects a jlong which is always 64 bit.
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
nsdManager = QAndroidJniObject("qtzeroconf/QZeroConfNsdManager", "(JLandroid/content/Context;)V", reinterpret_cast<quint64>(this), QtAndroid::androidActivity().object());
|
||||
#else
|
||||
nsdManager = QAndroidJniObject("qtzeroconf/QZeroConfNsdManager", "(JLandroid/content/Context;)V", reinterpret_cast<quint64>(this), QNativeInterface::QAndroidApplication::context()
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 7, 0))
|
||||
.object<jobject>()
|
||||
#endif
|
||||
);
|
||||
#endif
|
||||
if (nsdManager.isValid()) {
|
||||
jclass objectClass = env->GetObjectClass(nsdManager.object<jobject>());
|
||||
env->RegisterNatives(objectClass, methods, sizeof(methods) / sizeof(methods[0]));
|
||||
env->DeleteLocalRef(objectClass);
|
||||
}
|
||||
|
||||
QMutexLocker locker(&s_instancesMutex);
|
||||
s_instances.append(this);
|
||||
}
|
||||
|
||||
QZeroConfPrivate::~QZeroConfPrivate()
|
||||
{
|
||||
QMutexLocker locker(&s_instancesMutex);
|
||||
s_instances.removeAll(this);
|
||||
}
|
||||
|
||||
// In order to not having to pay attention to only use thread safe methods on the java side, we're only running
|
||||
// Java calls on the Android thread.
|
||||
// To make sure the Java object is not going out of scope and being garbage collected when the QZeroConf object
|
||||
// is deleted before the worker thread actually starts, keep a new QAndroidJniObject to nsdManager
|
||||
// which will increase the ref counter in the JVM.
|
||||
void QZeroConfPrivate::startServicePublish(const char *name, const char *type, quint16 port)
|
||||
{
|
||||
QAndroidJniObject ref(nsdManager);
|
||||
publishName = name;
|
||||
publishType = type;
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
QtAndroid::runOnAndroidThread([=](){
|
||||
#else
|
||||
QNativeInterface::QAndroidApplication::runOnAndroidMainThread([=]() {
|
||||
#endif
|
||||
QAndroidJniObject txtMap("java/util/HashMap");
|
||||
foreach (const QByteArray &key, txtRecords.keys()) {
|
||||
txtMap.callObjectMethod("put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
|
||||
QAndroidJniObject::fromString(key).object<jstring>(),
|
||||
QAndroidJniObject::fromString(txtRecords.value(key)).object<jstring>());
|
||||
}
|
||||
|
||||
ref.callMethod<void>("registerService", "(Ljava/lang/String;Ljava/lang/String;ILjava/util/Map;)V",
|
||||
QAndroidJniObject::fromString(publishName).object<jstring>(),
|
||||
QAndroidJniObject::fromString(publishType).object<jstring>(),
|
||||
port,
|
||||
txtMap.object());
|
||||
});
|
||||
}
|
||||
|
||||
void QZeroConfPrivate::stopServicePublish()
|
||||
{
|
||||
QAndroidJniObject ref(nsdManager);
|
||||
// If Android is on it's way to suspend when stopServicePublish() is called, we need to call nsd.unregisterService() synchronously
|
||||
// to force it to run before the device goes to sleep. If instead it is scheduled to run in the Android thread, it will not run
|
||||
// until the device is woken back up.
|
||||
if (qGuiApp->applicationState() == Qt::ApplicationSuspended) {
|
||||
ref.callMethod<void>("unregisterService");
|
||||
} else {
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
QtAndroid::runOnAndroidThread([ref]() {
|
||||
#else
|
||||
QNativeInterface::QAndroidApplication::runOnAndroidMainThread([ref]() {
|
||||
#endif
|
||||
ref.callMethod<void>("unregisterService");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void QZeroConfPrivate::startBrowser(QString type, QAbstractSocket::NetworkLayerProtocol protocol)
|
||||
{
|
||||
Q_UNUSED(protocol)
|
||||
QAndroidJniObject ref(nsdManager);
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
QtAndroid::runOnAndroidThread([ref, type]() {
|
||||
#else
|
||||
QNativeInterface::QAndroidApplication::runOnAndroidMainThread([ref, type]() {
|
||||
#endif
|
||||
ref.callMethod<void>("discoverServices", "(Ljava/lang/String;)V", QAndroidJniObject::fromString(type).object<jstring>());
|
||||
});
|
||||
}
|
||||
|
||||
void QZeroConfPrivate::stopBrowser()
|
||||
{
|
||||
QAndroidJniObject ref(nsdManager);
|
||||
// If Android is on it's way to suspend when stopBrowser() is called, we need to call nsd.stopServiceDiscovery() synchronously
|
||||
// to force it to run before the device goes to sleep.
|
||||
if (qGuiApp->applicationState() == Qt::ApplicationSuspended) {
|
||||
ref.callMethod<void>("stopServiceDiscovery");
|
||||
} else {
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
QtAndroid::runOnAndroidThread([ref]() {
|
||||
#else
|
||||
QNativeInterface::QAndroidApplication::runOnAndroidMainThread([ref]() {
|
||||
#endif
|
||||
ref.callMethod<void>("stopServiceDiscovery");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Callbacks will come in from the android thread. So we're never accessing any of our members directly but instead
|
||||
// propagate callbacks through Qt::QueuedConnection invokes into the Qt thread. Be sure to check if the instance is still
|
||||
// alive by checking s_instances while holding the mutex before scheduling the invokation.
|
||||
void QZeroConfPrivate::onServiceResolvedJNI(JNIEnv */*env*/, jobject /*thiz*/, jlong id, jstring name, jstring type, jstring hostname, jstring address, jint port, jobject txtRecords)
|
||||
{
|
||||
QMap<QByteArray, QByteArray> txtMap;
|
||||
QAndroidJniObject txt(txtRecords);
|
||||
QAndroidJniObject txtKeys = txt.callObjectMethod("keySet", "()Ljava/util/Set;").callObjectMethod("toArray", "()[Ljava/lang/Object;");
|
||||
|
||||
QAndroidJniEnvironment env;
|
||||
for (int i = 0; i < txt.callMethod<jint>("size"); i++) {
|
||||
QAndroidJniObject key = QAndroidJniObject(env->GetObjectArrayElement(txtKeys.object<jobjectArray>(), i));
|
||||
QAndroidJniObject valueObj = txt.callObjectMethod("get", "(Ljava/lang/Object;)Ljava/lang/Object;", key.object<jstring>());
|
||||
if (valueObj.isValid()) {
|
||||
jboolean isCopy;
|
||||
jbyte* b = env->GetByteArrayElements(valueObj.object<jbyteArray>(), &isCopy);
|
||||
QByteArray value((char *)b, env->GetArrayLength(valueObj.object<jbyteArray>()));
|
||||
env->ReleaseByteArrayElements(valueObj.object<jbyteArray>(), b, JNI_ABORT);
|
||||
txtMap.insert(key.toString().toUtf8(), value);
|
||||
} else {
|
||||
txtMap.insert(key.toString().toUtf8(), QByteArray());
|
||||
}
|
||||
}
|
||||
|
||||
QZeroConfPrivate *ref = reinterpret_cast<QZeroConfPrivate*>(id);
|
||||
QMutexLocker locker(&s_instancesMutex);
|
||||
if (!s_instances.contains(ref)) {
|
||||
return;
|
||||
}
|
||||
QMetaObject::invokeMethod(ref, "onServiceResolved", Qt::QueuedConnection,
|
||||
Q_ARG(QString, QAndroidJniObject(name).toString()),
|
||||
Q_ARG(QString, QAndroidJniObject(type).toString()),
|
||||
Q_ARG(QString, QAndroidJniObject(hostname).toString()),
|
||||
Q_ARG(QHostAddress, QHostAddress(QAndroidJniObject(address).toString())),
|
||||
Q_ARG(int, port),
|
||||
Q_ARG(TxtRecordMap, txtMap)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
void QZeroConfPrivate::onServiceRemovedJNI(JNIEnv */*env*/, jobject /*this*/, jlong id, jstring name)
|
||||
{
|
||||
QZeroConfPrivate *ref = reinterpret_cast<QZeroConfPrivate*>(id);
|
||||
QMutexLocker locker(&s_instancesMutex);
|
||||
if (!s_instances.contains(ref)) {
|
||||
return;
|
||||
}
|
||||
QMetaObject::invokeMethod(ref, "onServiceRemoved", Qt::QueuedConnection, Q_ARG(QString, QAndroidJniObject(name).toString()));
|
||||
}
|
||||
|
||||
|
||||
void QZeroConfPrivate::onBrowserStateChangedJNI(JNIEnv */*env*/, jobject /*thiz*/, jlong id, jboolean running, jboolean error)
|
||||
{
|
||||
QZeroConfPrivate *ref = reinterpret_cast<QZeroConfPrivate*>(id);
|
||||
QMutexLocker locker(&s_instancesMutex);
|
||||
if (!s_instances.contains(ref)) {
|
||||
return;
|
||||
}
|
||||
QMetaObject::invokeMethod(ref, "onBrowserStateChanged", Qt::QueuedConnection, Q_ARG(bool, running), Q_ARG(bool, error));
|
||||
}
|
||||
|
||||
void QZeroConfPrivate::onPublisherStateChangedJNI(JNIEnv */*env*/, jobject /*this*/, jlong id, jboolean running, jboolean error)
|
||||
{
|
||||
QZeroConfPrivate *ref = reinterpret_cast<QZeroConfPrivate*>(id);
|
||||
QMutexLocker locker(&s_instancesMutex);
|
||||
if (!s_instances.contains(ref)) {
|
||||
return;
|
||||
}
|
||||
QMetaObject::invokeMethod(ref, "onPublisherStateChanged", Qt::QueuedConnection, Q_ARG(bool, running), Q_ARG(bool, error));
|
||||
}
|
||||
|
||||
void QZeroConfPrivate::onServiceNameChangedJNI(JNIEnv */*env*/, jobject /*thiz*/, jlong id, jstring newName)
|
||||
{
|
||||
QZeroConfPrivate *ref = reinterpret_cast<QZeroConfPrivate*>(id);
|
||||
QMutexLocker locker(&s_instancesMutex);
|
||||
if (!s_instances.contains(ref)) {
|
||||
return;
|
||||
}
|
||||
QMetaObject::invokeMethod(ref, "onServiceNameChanged", Qt::QueuedConnection, Q_ARG(QString, QAndroidJniObject(newName).toString()));
|
||||
}
|
||||
|
||||
void QZeroConfPrivate::onServiceResolved(const QString &name, const QString &type, const QString &hostname, const QHostAddress &address, int port, const TxtRecordMap &txtRecords)
|
||||
{
|
||||
QZeroConfService zcs;
|
||||
bool newRecord = false;
|
||||
if (pub->services.contains(name)) {
|
||||
zcs = pub->services.value(name);
|
||||
} else {
|
||||
zcs = QZeroConfService(new QZeroConfServiceData);
|
||||
newRecord = true;
|
||||
}
|
||||
|
||||
zcs->m_name = name;
|
||||
zcs->m_type = type;
|
||||
// A previous implementation (based on avahi) returned service type as "_http._tcp" but Android API return "._http._tcp"
|
||||
// Stripping leading dot for backwards compatibility. FIXME: Still not in line with bonjour, which adds a trailing dot.
|
||||
zcs->m_type.remove(QRegularExpression("^."));
|
||||
zcs->m_host = hostname;
|
||||
zcs->m_port = port;
|
||||
zcs->m_ip = address;
|
||||
zcs->m_txt = txtRecords;
|
||||
|
||||
// Those are not available on Androids NsdManager
|
||||
// zcs->m_domain = domain;
|
||||
// zcs->m_interfaceIndex = interface;
|
||||
|
||||
if (newRecord) {
|
||||
pub->services.insert(name, zcs);
|
||||
emit pub->serviceAdded(zcs);
|
||||
} else {
|
||||
emit pub->serviceUpdated(zcs);
|
||||
}
|
||||
}
|
||||
|
||||
void QZeroConfPrivate::onServiceRemoved(const QString &name)
|
||||
{
|
||||
if (pub->services.contains(name)) {
|
||||
QZeroConfService service = pub->services.take(name);
|
||||
emit pub->serviceRemoved(service);
|
||||
}
|
||||
}
|
||||
|
||||
void QZeroConfPrivate::onBrowserStateChanged(bool running, bool error)
|
||||
{
|
||||
browserExists = running;
|
||||
if (error) {
|
||||
emit pub->error(QZeroConf::browserFailed);
|
||||
}
|
||||
}
|
||||
|
||||
void QZeroConfPrivate::onPublisherStateChanged(bool running, bool error)
|
||||
{
|
||||
publisherExists = running;
|
||||
if (running) {
|
||||
emit pub->servicePublished();
|
||||
}
|
||||
if (error) {
|
||||
emit pub->error(QZeroConf::serviceRegistrationFailed);
|
||||
}
|
||||
}
|
||||
|
||||
void QZeroConfPrivate::onServiceNameChanged(const QString &newName)
|
||||
{
|
||||
emit pub->serviceNameChanged(newName);
|
||||
}
|
||||
|
||||
|
||||
QZeroConf::QZeroConf(QObject *parent) : QObject(parent)
|
||||
{
|
||||
pri = new QZeroConfPrivate(this);
|
||||
qRegisterMetaType<QZeroConfService>("QZeroConfService");
|
||||
}
|
||||
|
||||
QZeroConf::~QZeroConf()
|
||||
{
|
||||
delete pri;
|
||||
}
|
||||
|
||||
void QZeroConf::startServicePublish(const char *name, const char *type, const char *domain, quint16 port, quint32 interface)
|
||||
{
|
||||
Q_UNUSED(domain) // Not supported on Android API
|
||||
Q_UNUSED(interface) // Not supported on Android API
|
||||
pri->startServicePublish(name, type, port);
|
||||
}
|
||||
|
||||
void QZeroConf::stopServicePublish(void)
|
||||
{
|
||||
pri->stopServicePublish();
|
||||
}
|
||||
|
||||
bool QZeroConf::publishExists(void)
|
||||
{
|
||||
return pri->publisherExists;
|
||||
}
|
||||
|
||||
void QZeroConf::addServiceTxtRecord(QString nameOnly)
|
||||
{
|
||||
pri->txtRecords.insert(nameOnly.toUtf8(), QByteArray());
|
||||
}
|
||||
|
||||
void QZeroConf::addServiceTxtRecord(QString name, QString value)
|
||||
{
|
||||
pri->txtRecords.insert(name.toUtf8(), value.toUtf8());
|
||||
}
|
||||
|
||||
void QZeroConf::clearServiceTxtRecords()
|
||||
{
|
||||
pri->txtRecords.clear();
|
||||
}
|
||||
|
||||
void QZeroConf::startBrowser(QString type, QAbstractSocket::NetworkLayerProtocol protocol)
|
||||
{
|
||||
pri->startBrowser(type, protocol);
|
||||
}
|
||||
|
||||
void QZeroConf::stopBrowser(void)
|
||||
{
|
||||
pri->stopBrowser();
|
||||
}
|
||||
|
||||
bool QZeroConf::browserExists(void)
|
||||
{
|
||||
return pri->browserExists;
|
||||
}
|
73
androidnsd_p.h
Normal file
73
androidnsd_p.h
Normal file
@ -0,0 +1,73 @@
|
||||
/**************************************************************************************************
|
||||
---------------------------------------------------------------------------------------------------
|
||||
Copyright (C) 2015-2021 Jonathan Bagg
|
||||
This file is part of QtZeroConf.
|
||||
|
||||
QtZeroConf is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
QtZeroConf is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with QtZeroConf. If not, see <http://www.gnu.org/licenses/>.
|
||||
---------------------------------------------------------------------------------------------------
|
||||
Project name : QtZeroConf
|
||||
File name : androidnsd_p.h
|
||||
Created : 10 Spetember 2021
|
||||
Author(s) : Michael Zanetti
|
||||
---------------------------------------------------------------------------------------------------
|
||||
NsdManager wrapper for use on Android devices
|
||||
---------------------------------------------------------------------------------------------------
|
||||
**************************************************************************************************/
|
||||
#include "qzeroconf.h"
|
||||
#include <QMap>
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
#include <QtAndroid>
|
||||
#include <QtAndroidExtras>
|
||||
#include <QAndroidJniObject>
|
||||
#else
|
||||
#include <QJniObject>
|
||||
using QAndroidJniObject = QJniObject;
|
||||
#endif
|
||||
|
||||
class QZeroConfPrivate: QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
typedef QMap<QByteArray, QByteArray> TxtRecordMap;
|
||||
|
||||
QZeroConfPrivate(QZeroConf *parent);
|
||||
~QZeroConfPrivate();
|
||||
void startServicePublish(const char *name, const char *type, quint16 port);
|
||||
void stopServicePublish();
|
||||
void startBrowser(QString type, QAbstractSocket::NetworkLayerProtocol protocol);
|
||||
void stopBrowser();
|
||||
static void onServiceResolvedJNI(JNIEnv */*env*/, jobject /*thiz*/, jlong id, jstring name, jstring type, jstring hostname, jstring address, jint port, jobject txtRecords);
|
||||
static void onServiceRemovedJNI(JNIEnv */*env*/, jobject /*this*/, jlong id, jstring name);
|
||||
static void onBrowserStateChangedJNI(JNIEnv */*env*/, jobject /*thiz*/, jlong id, jboolean running, jboolean error);
|
||||
static void onPublisherStateChangedJNI(JNIEnv */*env*/, jobject /*thiz*/, jlong id, jboolean running, jboolean error);
|
||||
static void onServiceNameChangedJNI(JNIEnv */*env*/, jobject /*thiz*/, jlong id, jstring newName);
|
||||
|
||||
QZeroConf *pub;
|
||||
QAndroidJniObject nsdManager;
|
||||
|
||||
bool browserExists = false;
|
||||
bool publisherExists = false;
|
||||
QMap<QByteArray, QByteArray> txtRecords;
|
||||
QString publishName;
|
||||
QString publishType;
|
||||
|
||||
|
||||
private slots:
|
||||
void onServiceResolved(const QString &name, const QString &type, const QString &hostname, const QHostAddress &address, int port, const TxtRecordMap &txtRecords);
|
||||
void onServiceRemoved(const QString &name);
|
||||
void onBrowserStateChanged(bool running, bool error);
|
||||
void onPublisherStateChanged(bool running, bool error);
|
||||
void onServiceNameChanged(const QString &newName);
|
||||
};
|
@ -33,7 +33,7 @@ AVAHI_C_DECL_BEGIN
|
||||
* is calculated like this: RFC1034 mandates maximum length of FQDNs
|
||||
* is 255. The maximum label length is 63. To minimize the number of
|
||||
* (non-escaped) dots, we comprise our maximum-length domain name of
|
||||
* four labels <EFBFBD> 63 characters plus three inner dots. Escaping the
|
||||
* four labels á 63 characters plus three inner dots. Escaping the
|
||||
* four labels quadruples their length at maximum. An escaped domain
|
||||
* name has the therefore the maximum length of 63*4*4+3=1011. A
|
||||
* trailing NUL and perhaps two unnecessary dots leading and trailing
|
||||
|
@ -226,16 +226,19 @@ QZeroConf::~QZeroConf()
|
||||
delete pri;
|
||||
}
|
||||
|
||||
void QZeroConf::startServicePublish(const char *name, const char *type, const char *domain, quint16 port)
|
||||
void QZeroConf::startServicePublish(const char *name, const char *type, const char *domain, quint16 port, quint32 interface)
|
||||
{
|
||||
if (pri->group) {
|
||||
if (!pri->client || pri->group) { // check client is ok (avahi daemon is running) and group is not already configured
|
||||
emit error(QZeroConf::serviceRegistrationFailed);
|
||||
return;
|
||||
}
|
||||
if (interface <= 0) {
|
||||
interface = AVAHI_IF_UNSPEC;
|
||||
}
|
||||
|
||||
pri->group = avahi_entry_group_new(pri->client, QZeroConfPrivate::groupCallback, pri);
|
||||
|
||||
int ret = avahi_entry_group_add_service_strlst(pri->group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_UPDATE, name, type, domain, NULL, port, pri->txt);
|
||||
int ret = avahi_entry_group_add_service_strlst(pri->group, interface, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_UPDATE, name, type, domain, NULL, port, pri->txt);
|
||||
if (ret < 0) {
|
||||
avahi_entry_group_free(pri->group);
|
||||
pri->group = NULL;
|
||||
@ -292,8 +295,10 @@ void QZeroConf::clearServiceTxtRecords()
|
||||
|
||||
void QZeroConf::startBrowser(QString type, QAbstractSocket::NetworkLayerProtocol protocol)
|
||||
{
|
||||
if (pri->browser)
|
||||
if (!pri->client || pri->browser) { // check client is ok (avahi daemon is running) and browser is not already started
|
||||
emit error(QZeroConf::browserFailed);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (protocol) {
|
||||
case QAbstractSocket::IPv4Protocol: pri->aProtocol = AVAHI_PROTO_INET; break;
|
||||
|
@ -72,7 +72,7 @@ public:
|
||||
ref->ready = 1;
|
||||
if (ref->registerWaiting) {
|
||||
ref->registerWaiting = 0;
|
||||
ref->registerService(ref->name.toUtf8(), ref->type.toUtf8(), ref->domain.toUtf8(), ref->port);
|
||||
ref->registerService(ref->name.toUtf8(), ref->type.toUtf8(), ref->domain.toUtf8(), ref->port, 0);
|
||||
}
|
||||
break;
|
||||
case AVAHI_SERVER_COLLISION:
|
||||
@ -230,7 +230,7 @@ public:
|
||||
resolvers.clear();
|
||||
}
|
||||
|
||||
void registerService(const char *name, const char *type, const char *domain, quint16 port)
|
||||
void registerService(const char *name, const char *type, const char *domain, quint16 port, quint32 interface)
|
||||
{
|
||||
qint32 ret;
|
||||
group = avahi_s_entry_group_new(server, QZeroConfPrivate::groupCallback, this);
|
||||
@ -239,7 +239,11 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
ret = avahi_server_add_service_strlst(server, group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_UPDATE, name, type, domain, NULL, port, txt);
|
||||
if (interface <= 0) {
|
||||
interface = AVAHI_IF_UNSPEC;
|
||||
}
|
||||
|
||||
ret = avahi_server_add_service_strlst(server, group, interface, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_UPDATE, name, type, domain, NULL, port, txt);
|
||||
if (ret < 0) {
|
||||
avahi_s_entry_group_free(group);
|
||||
group = NULL;
|
||||
@ -290,14 +294,14 @@ QZeroConf::~QZeroConf()
|
||||
delete pri;
|
||||
}
|
||||
|
||||
void QZeroConf::startServicePublish(const char *name, const char *type, const char *domain, quint16 port)
|
||||
void QZeroConf::startServicePublish(const char *name, const char *type, const char *domain, quint16 port, quint32 interface)
|
||||
{
|
||||
if (pri->group) {
|
||||
emit error(QZeroConf::serviceRegistrationFailed);
|
||||
return;
|
||||
}
|
||||
if (pri->ready)
|
||||
pri->registerService(name, type, domain, port);
|
||||
pri->registerService(name, type, domain, port, interface);
|
||||
else {
|
||||
pri->registerWaiting = 1;
|
||||
pri->name = name;
|
||||
|
@ -190,6 +190,8 @@ void DNSSD_API QZeroConfPrivate::resolverCallback(DNSServiceRef, DNSServiceFlags
|
||||
resolver->cleanUp();
|
||||
}
|
||||
else {
|
||||
// Fix "multiple socket notifiers for same socket" warning
|
||||
resolver->addressNotifier.clear();
|
||||
resolver->addressNotifier = QSharedPointer<QSocketNotifier>::create(sockfd, QSocketNotifier::Read);
|
||||
connect(resolver->addressNotifier.data(), &QSocketNotifier::activated, resolver, &Resolver::addressReady);
|
||||
}
|
||||
@ -266,7 +268,7 @@ QZeroConf::~QZeroConf()
|
||||
delete pri;
|
||||
}
|
||||
|
||||
void QZeroConf::startServicePublish(const char *name, const char *type, const char *domain, quint16 port)
|
||||
void QZeroConf::startServicePublish(const char *name, const char *type, const char *domain, quint16 port, quint32 interface)
|
||||
{
|
||||
DNSServiceErrorType err;
|
||||
|
||||
@ -275,7 +277,7 @@ void QZeroConf::startServicePublish(const char *name, const char *type, const ch
|
||||
return;
|
||||
}
|
||||
|
||||
err = DNSServiceRegister(&pri->dnssRef, 0, 0,
|
||||
err = DNSServiceRegister(&pri->dnssRef, 0, interface,
|
||||
name,
|
||||
type,
|
||||
domain,
|
||||
|
@ -1,7 +1,8 @@
|
||||
cmake_minimum_required(VERSION 3.4)
|
||||
project(QtZeroConfExample)
|
||||
cmake_minimum_required(VERSION 2.8.11)
|
||||
|
||||
find_package(Qt5 COMPONENTS Gui Widgets REQUIRED)
|
||||
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core Network)
|
||||
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Network Gui Widgets REQUIRED)
|
||||
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
@ -21,5 +22,5 @@ add_executable(QtZeroConfExample
|
||||
|
||||
target_link_libraries(QtZeroConfExample
|
||||
QtZeroConf
|
||||
Qt5::Gui Qt5::Widgets
|
||||
Qt${QT_VERISON_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Widgets
|
||||
)
|
||||
|
1
example/android/src/QZeroConfNsdManager.java
Symbolic link
1
example/android/src/QZeroConfNsdManager.java
Symbolic link
@ -0,0 +1 @@
|
||||
../../../QZeroConfNsdManager.java
|
@ -5,3 +5,6 @@ SOURCES= main.cpp window.cpp
|
||||
DEFINES= QZEROCONF_STATIC
|
||||
|
||||
include(../qtzeroconf.pri)
|
||||
INCLUDEPATH+=../
|
||||
|
||||
android: ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android
|
||||
|
@ -57,7 +57,7 @@ ios {
|
||||
QMAKE_CXXFLAGS+= -I$$PWD
|
||||
}
|
||||
|
||||
ubports|android: {
|
||||
ubports: {
|
||||
QMAKE_CXXFLAGS+= -I$$PWD
|
||||
QMAKE_CFLAGS+= -I$$PWD
|
||||
ACM = $$PWD/avahi-common
|
||||
@ -121,6 +121,16 @@ ubports|android: {
|
||||
#avahi-core/iface-none.c avahi-core/iface-pfroute.c avahi-core/avahi-reflector.c
|
||||
}
|
||||
|
||||
android: {
|
||||
lessThan(QT_MAJOR_VERSION, 6) {
|
||||
QT += androidextras
|
||||
}
|
||||
QT += gui
|
||||
HEADERS += $$PWD/qzeroconf.h $$PWD/androidnsd_p.h
|
||||
SOURCES += $$PWD/androidnsd.cpp
|
||||
DISTFILES += $$PWD/QZeroConfNsdManager.java
|
||||
}
|
||||
|
||||
HEADERS+= $$PWD/qzeroconfservice.h $$PWD/qzeroconfglobal.h
|
||||
|
||||
SOURCES+= $$PWD/qzeroconfservice.cpp
|
||||
|
@ -50,7 +50,7 @@ public:
|
||||
};
|
||||
QZeroConf(QObject *parent = Q_NULLPTR);
|
||||
~QZeroConf();
|
||||
void startServicePublish(const char *name, const char *type, const char *domain, quint16 port);
|
||||
void startServicePublish(const char *name, const char *type, const char *domain, quint16 port, quint32 interface = 0);
|
||||
void stopServicePublish(void);
|
||||
bool publishExists(void);
|
||||
inline void startBrowser(QString type)
|
||||
@ -66,6 +66,7 @@ public:
|
||||
|
||||
Q_SIGNALS:
|
||||
void servicePublished(void);
|
||||
void serviceNameChanged(const QString &newName);
|
||||
void error(QZeroConf::error_t);
|
||||
void serviceAdded(QZeroConfService);
|
||||
void serviceUpdated(QZeroConfService);
|
||||
|
Reference in New Issue
Block a user