diff --git a/src/android/CHIPTest/app/src/main/java/com/tcl/chip/chiptest/MainActivity.kt b/src/android/CHIPTest/app/src/main/java/com/tcl/chip/chiptest/MainActivity.kt
index d1f4fa14ec8ff7..67e78748dc1cfd 100644
--- a/src/android/CHIPTest/app/src/main/java/com/tcl/chip/chiptest/MainActivity.kt
+++ b/src/android/CHIPTest/app/src/main/java/com/tcl/chip/chiptest/MainActivity.kt
@@ -53,6 +53,6 @@ class MainActivity : AppCompatActivity() {
}.start()
}
- AndroidChipPlatform(AndroidBleManager(), PreferencesKeyValueStoreManager(this), PreferencesConfigurationManager(this), NsdManagerServiceResolver(this), ChipMdnsCallbackImpl())
+ AndroidChipPlatform.defaultInstance(this)
}
}
diff --git a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/ChipClient.kt b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/ChipClient.kt
index 7fcdfeb46f706f..322495a3b29041 100644
--- a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/ChipClient.kt
+++ b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/ChipClient.kt
@@ -50,7 +50,7 @@ object ChipClient {
if (!this::androidPlatform.isInitialized && context != null) {
//force ChipDeviceController load jni
ChipDeviceController.loadJni()
- androidPlatform = AndroidChipPlatform(AndroidBleManager(), PreferencesKeyValueStoreManager(context), PreferencesConfigurationManager(context), NsdManagerServiceResolver(context), ChipMdnsCallbackImpl())
+ androidPlatform = AndroidChipPlatform.defaultInstance(context)
}
return androidPlatform
}
diff --git a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/provisioning/AddressCommissioningFragment.kt b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/provisioning/AddressCommissioningFragment.kt
index dd7015fac13dd6..27aa3be27a912c 100644
--- a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/provisioning/AddressCommissioningFragment.kt
+++ b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/provisioning/AddressCommissioningFragment.kt
@@ -5,6 +5,8 @@ import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import android.widget.AdapterView
+import android.widget.ArrayAdapter
import androidx.fragment.app.Fragment
import com.google.chip.chiptool.ChipClient
import com.google.chip.chiptool.R
@@ -13,10 +15,21 @@ import com.google.chip.chiptool.setuppayloadscanner.CHIPDeviceInfo
import com.google.chip.chiptool.util.FragmentUtil
import kotlinx.android.synthetic.main.address_commissioning_fragment.addressEditText
import kotlinx.android.synthetic.main.address_commissioning_fragment.commissionBtn
+import kotlinx.android.synthetic.main.address_commissioning_fragment.discoverBtn
+import kotlinx.android.synthetic.main.address_commissioning_fragment.discoverListSpinner
import kotlinx.android.synthetic.main.address_commissioning_fragment.discriminatorEditText
import kotlinx.android.synthetic.main.address_commissioning_fragment.pincodeEditText
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+
class AddressCommissioningFragment : Fragment() {
+ private val ipAddressList = ArrayList()
+ private val scope = CoroutineScope(Dispatchers.Main + Job())
+
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
@@ -46,6 +59,39 @@ class AddressCommissioningFragment : Fragment() {
)
)
}
+
+ discoverBtn.setOnClickListener { _ ->
+ discoverBtn.isEnabled = false
+ val deviceController = ChipClient.getDeviceController(requireContext())
+ deviceController.discoverCommissionableNodes()
+ scope.launch {
+ delay(7000)
+ updateSpinner()
+ discoverBtn.isEnabled = true
+ }
+ }
+ }
+
+ private fun updateSpinner() {
+ val deviceController = ChipClient.getDeviceController(requireContext())
+ for(i in 0..10) {
+ val device = deviceController.getDiscoveredDevice(i) ?: break
+ ipAddressList.add("${device.ipAddress}, ${device.discriminator}")
+ }
+ requireActivity().runOnUiThread {
+ discoverListSpinner.adapter =
+ ArrayAdapter(requireContext(), android.R.layout.simple_spinner_dropdown_item, ipAddressList)
+ discoverListSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
+ override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) {
+ val address = ipAddressList[position].split(",")[0].trim()
+ val discriminator = ipAddressList[position].split(",")[1].trim()
+ addressEditText.setText(address)
+ discriminatorEditText.setText(discriminator)
+ }
+
+ override fun onNothingSelected(parent: AdapterView<*>) {}
+ }
+ }
}
companion object {
diff --git a/src/android/CHIPTool/app/src/main/res/layout/address_commissioning_fragment.xml b/src/android/CHIPTool/app/src/main/res/layout/address_commissioning_fragment.xml
index 21fafbd3ff9edf..4ff57d1767d7ed 100644
--- a/src/android/CHIPTool/app/src/main/res/layout/address_commissioning_fragment.xml
+++ b/src/android/CHIPTool/app/src/main/res/layout/address_commissioning_fragment.xml
@@ -16,6 +16,7 @@
app:layout_constraintTop_toTopOf="parent" />
+
+
+
+
+
+
+
Pincode
Commission
+ Discover
On
Off
diff --git a/src/controller/java/BUILD.gn b/src/controller/java/BUILD.gn
index f627e30b3f70f9..2d2effb862decc 100644
--- a/src/controller/java/BUILD.gn
+++ b/src/controller/java/BUILD.gn
@@ -69,6 +69,7 @@ android_library("java") {
"src/chip/devicecontroller/ChipCommandType.java",
"src/chip/devicecontroller/ChipDeviceController.java",
"src/chip/devicecontroller/ChipDeviceControllerException.java",
+ "src/chip/devicecontroller/DiscoveredDevice.java",
"src/chip/devicecontroller/GetConnectedDeviceCallbackJni.java",
"src/chip/devicecontroller/PaseVerifierParams.java",
"zap-generated/chip/devicecontroller/ChipClusters.java",
diff --git a/src/controller/java/CHIPDeviceController-JNI.cpp b/src/controller/java/CHIPDeviceController-JNI.cpp
index 4d087beff445f0..e186c7be29bade 100644
--- a/src/controller/java/CHIPDeviceController-JNI.cpp
+++ b/src/controller/java/CHIPDeviceController-JNI.cpp
@@ -352,6 +352,55 @@ JNI_METHOD(void, updateDevice)(JNIEnv * env, jobject self, jlong handle, jlong f
}
}
+JNI_METHOD(void, discoverCommissionableNodes)(JNIEnv * env, jobject self, jlong handle)
+{
+ chip::DeviceLayer::StackLock lock;
+
+ AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle);
+ chip::Dnssd::DiscoveryFilter filter = chip::Dnssd::DiscoveryFilter();
+
+ CHIP_ERROR err = wrapper->Controller()->DiscoverCommissionableNodes(filter);
+ if (err != CHIP_NO_ERROR)
+ {
+ ChipLogError(Controller, "Failed to discoverCommissionableNodes");
+ ThrowError(env, err);
+ }
+}
+
+JNI_METHOD(jobject, getDiscoveredDevice)(JNIEnv * env, jobject self, jlong handle, jint idx)
+{
+ chip::DeviceLayer::StackLock lock;
+
+ AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle);
+ const Dnssd::DiscoveredNodeData * data = wrapper->Controller()->GetDiscoveredDevice(idx);
+
+ if (data == nullptr)
+ {
+ return nullptr;
+ }
+
+ jclass discoveredDeviceCls = env->FindClass("chip/devicecontroller/DiscoveredDevice");
+ jmethodID constructor = env->GetMethodID(discoveredDeviceCls, "", "()V");
+
+ jfieldID discrminatorID = env->GetFieldID(discoveredDeviceCls, "discriminator", "J");
+ jfieldID ipAddressID = env->GetFieldID(discoveredDeviceCls, "ipAddress", "Ljava/lang/String;");
+
+ jobject discoveredObj = env->NewObject(discoveredDeviceCls, constructor);
+
+ env->SetLongField(discoveredObj, discrminatorID, data->longDiscriminator);
+
+ char ipAddress[100];
+ data->ipAddress[0].ToString(ipAddress, 100);
+ jstring jniipAdress = env->NewStringUTF(ipAddress);
+ env->SetObjectField(discoveredObj, ipAddressID, jniipAdress);
+
+ if (data == nullptr)
+ {
+ ChipLogError(Controller, "GetDiscoveredDevice - not found");
+ }
+ return discoveredObj;
+}
+
JNI_METHOD(jboolean, openPairingWindow)(JNIEnv * env, jobject self, jlong handle, jlong devicePtr, jint duration)
{
chip::DeviceLayer::StackLock lock;
diff --git a/src/controller/java/src/chip/devicecontroller/ChipDeviceController.java b/src/controller/java/src/chip/devicecontroller/ChipDeviceController.java
index 4956908084cdcd..95a8c01b6d4aec 100644
--- a/src/controller/java/src/chip/devicecontroller/ChipDeviceController.java
+++ b/src/controller/java/src/chip/devicecontroller/ChipDeviceController.java
@@ -196,6 +196,18 @@ public void updateDevice(long fabricId, long deviceId) {
updateDevice(deviceControllerPtr, fabricId, deviceId);
}
+ /**
+ * Get commmissionible Node. Commmissionible Node results are able to get using {@link
+ * ChipDeviceController.getDiscoveredDevice}.
+ */
+ public void discoverCommissionableNodes() {
+ discoverCommissionableNodes(deviceControllerPtr);
+ }
+
+ public DiscoveredDevice getDiscoveredDevice(int idx) {
+ return getDiscoveredDevice(deviceControllerPtr, idx);
+ }
+
public boolean openPairingWindow(long devicePtr, int duration) {
return openPairingWindow(deviceControllerPtr, devicePtr, duration);
}
@@ -259,6 +271,10 @@ private native void getConnectedDevicePointer(
private native void updateDevice(long deviceControllerPtr, long fabricId, long deviceId);
+ private native void discoverCommissionableNodes(long deviceControllerPtr);
+
+ private native DiscoveredDevice getDiscoveredDevice(long deviceControllerPtr, int idx);
+
private native boolean openPairingWindow(long deviceControllerPtr, long devicePtr, int duration);
private native boolean openPairingWindowWithPIN(
diff --git a/src/controller/java/src/chip/devicecontroller/DiscoveredDevice.java b/src/controller/java/src/chip/devicecontroller/DiscoveredDevice.java
new file mode 100644
index 00000000000000..3be9631512af42
--- /dev/null
+++ b/src/controller/java/src/chip/devicecontroller/DiscoveredDevice.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2020-2021 Project CHIP Authors
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package chip.devicecontroller;
+
+public class DiscoveredDevice {
+ public long discriminator;
+ public String ipAddress;
+}
diff --git a/src/platform/android/AndroidChipPlatform-JNI.cpp b/src/platform/android/AndroidChipPlatform-JNI.cpp
index 785ed89cb42638..5344018d047083 100644
--- a/src/platform/android/AndroidChipPlatform-JNI.cpp
+++ b/src/platform/android/AndroidChipPlatform-JNI.cpp
@@ -210,19 +210,26 @@ JNI_METHOD(void, setConfigurationManager)(JNIEnv * env, jclass self, jobject man
chip::DeviceLayer::ConfigurationMgrImpl().InitializeWithObject(manager);
}
-// for ServiceResolver
-JNI_METHOD(void, nativeSetServiceResolver)(JNIEnv * env, jclass self, jobject resolver, jobject chipMdnsCallback)
+// for ServiceResolver and ServiceBrowser
+JNI_METHOD(void, nativeSetDnssdDelegates)(JNIEnv * env, jclass self, jobject resolver, jobject browser, jobject chipMdnsCallback)
{
chip::DeviceLayer::StackLock lock;
- chip::Dnssd::InitializeWithObjects(resolver, chipMdnsCallback);
+ chip::Dnssd::InitializeWithObjects(resolver, browser, chipMdnsCallback);
}
JNI_MDNSCALLBACK_METHOD(void, handleServiceResolve)
-(JNIEnv * env, jclass self, jstring instanceName, jstring serviceType, jstring address, jint port, jlong callbackHandle,
- jlong contextHandle)
+(JNIEnv * env, jclass self, jstring instanceName, jstring serviceType, jstring hostName, jstring address, jint port,
+ jobject attributes, jlong callbackHandle, jlong contextHandle)
{
using ::chip::Dnssd::HandleResolve;
- HandleResolve(instanceName, serviceType, address, port, callbackHandle, contextHandle);
+ HandleResolve(instanceName, serviceType, hostName, address, port, attributes, callbackHandle, contextHandle);
+}
+
+JNI_MDNSCALLBACK_METHOD(void, handleServiceBrowse)
+(JNIEnv * env, jclass self, jobjectArray instanceName, jstring serviceType, jlong callbackHandle, jlong contextHandle)
+{
+ using ::chip::Dnssd::HandleBrowse;
+ HandleBrowse(instanceName, serviceType, callbackHandle, contextHandle);
}
#if CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE
diff --git a/src/platform/android/BUILD.gn b/src/platform/android/BUILD.gn
index a12d6aa55e9108..45d72780872534 100644
--- a/src/platform/android/BUILD.gn
+++ b/src/platform/android/BUILD.gn
@@ -75,9 +75,11 @@ android_library("java") {
"java/chip/platform/ChipMdnsCallbackImpl.java",
"java/chip/platform/ConfigurationManager.java",
"java/chip/platform/KeyValueStoreManager.java",
+ "java/chip/platform/NsdManagerServiceBrowser.java",
"java/chip/platform/NsdManagerServiceResolver.java",
"java/chip/platform/PreferencesConfigurationManager.java",
"java/chip/platform/PreferencesKeyValueStoreManager.java",
+ "java/chip/platform/ServiceBrowser.java",
"java/chip/platform/ServiceResolver.java",
]
diff --git a/src/platform/android/DnssdImpl.cpp b/src/platform/android/DnssdImpl.cpp
index f0a0bace59ebb7..2ebe43a4098bd7 100644
--- a/src/platform/android/DnssdImpl.cpp
+++ b/src/platform/android/DnssdImpl.cpp
@@ -29,6 +29,7 @@
#include
#include
+#include
#include
namespace chip {
@@ -37,11 +38,18 @@ namespace Dnssd {
using namespace chip::Platform;
namespace {
-jobject sResolverObject = nullptr;
-jobject sMdnsCallbackObject = nullptr;
-jmethodID sResolveMethod = nullptr;
-jmethodID sPublishMethod = nullptr;
-jmethodID sRemoveServicesMethod = nullptr;
+
+jobject sResolverObject = nullptr;
+jobject sDiscoverObject = nullptr;
+jobject sMdnsCallbackObject = nullptr;
+jmethodID sResolveMethod = nullptr;
+jmethodID sDiscoveryMethod = nullptr;
+jmethodID sGetAttributeKeysMethod = nullptr;
+jmethodID sGetAttributeDataMethod = nullptr;
+jclass sMdnsCallbackClass = nullptr;
+jmethodID sPublishMethod = nullptr;
+jmethodID sRemoveServicesMethod = nullptr;
+
} // namespace
// Implemention of functions declared in lib/dnssd/platform/Dnssd.h
@@ -142,8 +150,29 @@ CHIP_ERROR ChipDnssdFinalizeServiceUpdate()
CHIP_ERROR ChipDnssdBrowse(const char * type, DnssdServiceProtocol protocol, Inet::IPAddressType addressType,
Inet::InterfaceId interface, DnssdBrowseCallback callback, void * context)
{
- // TODO: Implement DNS-SD browse for Android
- return CHIP_ERROR_NOT_IMPLEMENTED;
+ VerifyOrReturnError(type != nullptr && callback != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
+ VerifyOrReturnError(sDiscoverObject != nullptr && sDiscoveryMethod != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
+ VerifyOrReturnError(sMdnsCallbackObject != nullptr, CHIP_ERROR_INCORRECT_STATE);
+
+ std::string serviceType = type;
+ serviceType += '.';
+ serviceType += (protocol == DnssdServiceProtocol::kDnssdProtocolUdp ? "_udp" : "_tcp");
+
+ JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
+ UtfString jniServiceType(env, serviceType.c_str());
+
+ env->CallVoidMethod(sDiscoverObject, sDiscoveryMethod, jniServiceType.jniValue(), reinterpret_cast(callback),
+ reinterpret_cast(context), sMdnsCallbackObject);
+
+ if (env->ExceptionCheck())
+ {
+ ChipLogError(Discovery, "Java exception in ChipDnssdBrowse");
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ return CHIP_JNI_ERROR_EXCEPTION_THROWN;
+ }
+
+ return CHIP_NO_ERROR;
}
CHIP_ERROR ChipDnssdResolve(DnssdService * service, Inet::InterfaceId interface, DnssdResolveCallback callback, void * context)
@@ -179,23 +208,52 @@ CHIP_ERROR ChipDnssdResolve(DnssdService * service, Inet::InterfaceId interface,
// Implemention of Java-specific functions
-void InitializeWithObjects(jobject resolverObject, jobject mdnsCallbackObject)
+void InitializeWithObjects(jobject resolverObject, jobject browseObject, jobject mdnsCallbackObject)
{
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
sResolverObject = env->NewGlobalRef(resolverObject);
+ sDiscoverObject = env->NewGlobalRef(browseObject);
sMdnsCallbackObject = env->NewGlobalRef(mdnsCallbackObject);
jclass resolverClass = env->GetObjectClass(sResolverObject);
+ jclass browseClass = env->GetObjectClass(browseObject);
+ sMdnsCallbackClass = env->GetObjectClass(sMdnsCallbackObject);
+ VerifyOrReturn(browseClass != nullptr, ChipLogError(Discovery, "Failed to get Browse Java class"));
VerifyOrReturn(resolverClass != nullptr, ChipLogError(Discovery, "Failed to get Resolver Java class"));
sResolveMethod =
env->GetMethodID(resolverClass, "resolve", "(Ljava/lang/String;Ljava/lang/String;JJLchip/platform/ChipMdnsCallback;)V");
+
+ sDiscoveryMethod = env->GetMethodID(browseClass, "browse", "(Ljava/lang/String;JJLchip/platform/ChipMdnsCallback;)V");
+
+ sGetAttributeKeysMethod = env->GetMethodID(sMdnsCallbackClass, "getAttributeKeys", "(Ljava/util/Map;)[Ljava/lang/String;");
+
+ sGetAttributeDataMethod = env->GetMethodID(sMdnsCallbackClass, "getAttributeData", "(Ljava/util/Map;Ljava/lang/String;)[B");
+
if (sResolveMethod == nullptr)
{
ChipLogError(Discovery, "Failed to access Resolver 'resolve' method");
env->ExceptionClear();
}
+ if (sDiscoveryMethod == nullptr)
+ {
+ ChipLogError(Discovery, "Failed to access Discover 'browse' method");
+ env->ExceptionClear();
+ }
+
+ if (sGetAttributeKeysMethod == nullptr)
+ {
+ ChipLogError(Discovery, "Failed to access MdnsCallback 'getAttributeKeys' method");
+ env->ExceptionClear();
+ }
+
+ if (sGetAttributeDataMethod == nullptr)
+ {
+ ChipLogError(Discovery, "Failed to access MdnsCallback 'getAttributeData' method");
+ env->ExceptionClear();
+ }
+
sPublishMethod =
env->GetMethodID(resolverClass, "publish",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I[Ljava/lang/String;[[B[Ljava/lang/String;)V");
@@ -213,7 +271,8 @@ void InitializeWithObjects(jobject resolverObject, jobject mdnsCallbackObject)
}
}
-void HandleResolve(jstring instanceName, jstring serviceType, jstring address, jint port, jlong callbackHandle, jlong contextHandle)
+void HandleResolve(jstring instanceName, jstring serviceType, jstring hostName, jstring address, jint port, jobject attributes,
+ jlong callbackHandle, jlong contextHandle)
{
VerifyOrReturn(callbackHandle != 0, ChipLogError(Discovery, "HandleResolve called with callback equal to nullptr"));
@@ -228,6 +287,7 @@ void HandleResolve(jstring instanceName, jstring serviceType, jstring address, j
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
JniUtfString jniInstanceName(env, instanceName);
JniUtfString jniServiceType(env, serviceType);
+ JniUtfString jnihostName(env, hostName);
JniUtfString jniAddress(env, address);
Inet::IPAddress ipAddress;
@@ -238,11 +298,108 @@ void HandleResolve(jstring instanceName, jstring serviceType, jstring address, j
DnssdService service = {};
CopyString(service.mName, jniInstanceName.c_str());
+ CopyString(service.mHostName, jnihostName.c_str());
CopyString(service.mType, jniServiceType.c_str());
service.mAddress.SetValue(ipAddress);
service.mPort = static_cast(port);
+ if (attributes != nullptr)
+ {
+ jobjectArray keys = (jobjectArray) env->CallObjectMethod(sMdnsCallbackObject, sGetAttributeKeysMethod, attributes);
+ size_t size = env->GetArrayLength(keys);
+ TextEntry * entries = new TextEntry[size];
+ for (size_t i = 0; i < size; i++)
+ {
+ jstring jniKeyObject = (jstring) env->GetObjectArrayElement(keys, i);
+ entries[i].mKey = getTxtInfoKey(env, jniKeyObject);
+
+ jbyteArray datas =
+ (jbyteArray) env->CallObjectMethod(sMdnsCallbackObject, sGetAttributeDataMethod, attributes, jniKeyObject);
+ if (datas != nullptr)
+ {
+ size_t dataSize = env->GetArrayLength(datas);
+ uint8_t * data = new uint8_t[dataSize + 1];
+ jbyte * jnidata = env->GetByteArrayElements(datas, nullptr);
+ for (size_t j = 0; j < dataSize; j++)
+ {
+ data[j] = (uint8_t) jnidata[j];
+ }
+ entries[i].mDataSize = dataSize;
+ entries[i].mData = data;
+ }
+ else
+ {
+ entries[i].mDataSize = 0;
+ entries[i].mData = nullptr;
+ }
+ }
+ service.mTextEntrySize = size;
+ service.mTextEntries = entries;
+ }
+ else
+ {
+ ChipLogError(Discovery, "attributes is null");
+ service.mTextEntrySize = 0;
+ service.mTextEntries = nullptr;
+ }
dispatch(CHIP_NO_ERROR, &service);
+
+ if (service.mTextEntries != nullptr)
+ {
+ size_t size = service.mTextEntrySize;
+ for (size_t i = 0; i < size; i++)
+ {
+ if (service.mTextEntries[i].mData != nullptr)
+ {
+ delete[] service.mTextEntries[i].mData;
+ }
+ }
+ delete[] service.mTextEntries;
+ }
+}
+
+void HandleBrowse(jobjectArray instanceName, jstring serviceType, jlong callbackHandle, jlong contextHandle)
+{
+ VerifyOrReturn(callbackHandle != 0, ChipLogError(Discovery, "HandleDiscover called with callback equal to nullptr"));
+
+ const auto dispatch = [callbackHandle, contextHandle](CHIP_ERROR error, DnssdService * service = nullptr, size_t size = 0) {
+ DnssdBrowseCallback callback = reinterpret_cast(callbackHandle);
+ callback(reinterpret_cast(contextHandle), service, size, error);
+ };
+
+ JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
+ JniUtfString jniServiceType(env, serviceType);
+
+ VerifyOrReturn(strlen(jniServiceType.c_str()) <= kDnssdTypeAndProtocolMaxSize, dispatch(CHIP_ERROR_INVALID_ARGUMENT));
+
+ size_t size = env->GetArrayLength(instanceName);
+ DnssdService * service = new DnssdService[size];
+ for (size_t i = 0; i < size; i++)
+ {
+ JniUtfString jniInstanceName(env, (jstring) env->GetObjectArrayElement(instanceName, i));
+ VerifyOrReturn(strlen(jniInstanceName.c_str()) <= Operational::kInstanceNameMaxLength,
+ dispatch(CHIP_ERROR_INVALID_ARGUMENT));
+
+ CopyString(service[i].mName, jniInstanceName.c_str());
+ CopyString(service[i].mType, jniServiceType.c_str());
+ }
+
+ dispatch(CHIP_NO_ERROR, service, size);
+ delete[] service;
+}
+
+const char * getTxtInfoKey(JNIEnv * env, jstring key)
+{
+ const char * keyList[] = { "D", "VP", "AP", "CM", "DT", "DN", "RI", "PI", "PH", "CRI", "CRA", "T" };
+ JniUtfString jniKey(env, key);
+ for (auto & info : keyList)
+ {
+ if (strcmp(info, jniKey.c_str()) == 0)
+ {
+ return info;
+ }
+ }
+ return "";
}
} // namespace Dnssd
diff --git a/src/platform/android/DnssdImpl.h b/src/platform/android/DnssdImpl.h
index 0a6b30870b081b..01e11b0b028077 100644
--- a/src/platform/android/DnssdImpl.h
+++ b/src/platform/android/DnssdImpl.h
@@ -27,13 +27,16 @@ namespace Dnssd {
* that implements chip.devicecontroller.mdns.ServiceResolver interface, and an object of a class that implements
* chip.devicecontroller.mdns.ChipMdnsCallback interface.
*/
-void InitializeWithObjects(jobject resolverObject, jobject chipMdnsCallbackObject);
+void InitializeWithObjects(jobject resolverObject, jobject discoverObject, jobject chipMdnsCallbackObject);
/**
* Pass results of the service resolution to the CHIP stack.
*/
-void HandleResolve(jstring instanceName, jstring serviceType, jstring address, jint port, jlong callbackHandle,
- jlong contextHandle);
+void HandleResolve(jstring instanceName, jstring serviceType, jstring hostName, jstring address, jint port, jobject attributes,
+ jlong callbackHandle, jlong contextHandle);
+
+void HandleBrowse(jobjectArray instanceName, jstring serviceType, jlong callbackHandle, jlong contextHandle);
+const char * getTxtInfoKey(JNIEnv * env, jstring key);
} // namespace Dnssd
} // namespace chip
diff --git a/src/platform/android/java/chip/platform/AndroidChipPlatform.java b/src/platform/android/java/chip/platform/AndroidChipPlatform.java
index dbd294043e6537..59b772e990dbcd 100644
--- a/src/platform/android/java/chip/platform/AndroidChipPlatform.java
+++ b/src/platform/android/java/chip/platform/AndroidChipPlatform.java
@@ -17,19 +17,43 @@
*/
package chip.platform;
+import android.content.Context;
+
public final class AndroidChipPlatform {
private BleManager mBleManager = null;
- public AndroidChipPlatform(
+ /** Constructs AndroidChipPlatform with default implementations for all dependencies. */
+ public static AndroidChipPlatform defaultInstance(Context context) {
+ return new AndroidChipPlatform(
+ new AndroidBleManager(),
+ new PreferencesKeyValueStoreManager(context),
+ new PreferencesConfigurationManager(context),
+ new NsdManagerServiceResolver(context),
+ new NsdManagerServiceBrowser(context),
+ new ChipMdnsCallbackImpl());
+ }
+
+ public static AndroidChipPlatform newInstance(
+ BleManager ble,
+ KeyValueStoreManager kvm,
+ ConfigurationManager cfg,
+ ServiceResolver resolver,
+ ServiceBrowser browser,
+ ChipMdnsCallback chipMdnsCallback) {
+ return new AndroidChipPlatform(ble, kvm, cfg, resolver, browser, chipMdnsCallback);
+ }
+
+ private AndroidChipPlatform(
BleManager ble,
KeyValueStoreManager kvm,
ConfigurationManager cfg,
ServiceResolver resolver,
+ ServiceBrowser browser,
ChipMdnsCallback chipMdnsCallback) {
setBLEManager(ble);
setKeyValueStoreManager(kvm);
setConfigurationManager(cfg);
- setServiceResolver(resolver, chipMdnsCallback);
+ setDnssdDelegates(resolver, browser, chipMdnsCallback);
}
// for BLEManager
@@ -73,12 +97,13 @@ public native void handleUnsubscribeComplete(
private native void setConfigurationManager(ConfigurationManager manager);
// for ServiceResolver
- private void setServiceResolver(ServiceResolver resolver, ChipMdnsCallback chipMdnsCallback) {
+ private void setDnssdDelegates(
+ ServiceResolver resolver, ServiceBrowser browser, ChipMdnsCallback chipMdnsCallback) {
if (resolver != null) {
- nativeSetServiceResolver(resolver, chipMdnsCallback);
+ nativeSetDnssdDelegates(resolver, browser, chipMdnsCallback);
}
}
- private native void nativeSetServiceResolver(
- ServiceResolver resolver, ChipMdnsCallback chipMdnsCallback);
+ private native void nativeSetDnssdDelegates(
+ ServiceResolver resolver, ServiceBrowser browser, ChipMdnsCallback chipMdnsCallback);
}
diff --git a/src/platform/android/java/chip/platform/ChipMdnsCallback.java b/src/platform/android/java/chip/platform/ChipMdnsCallback.java
index 58304997a07ef4..66d94aaf1b8174 100644
--- a/src/platform/android/java/chip/platform/ChipMdnsCallback.java
+++ b/src/platform/android/java/chip/platform/ChipMdnsCallback.java
@@ -17,13 +17,20 @@
*/
package chip.platform;
+import java.util.Map;
+
/** Interface for communicating with the CHIP mDNS stack. */
public interface ChipMdnsCallback {
void handleServiceResolve(
String instanceName,
String serviceType,
+ String hostName,
String address,
int port,
+ Map attributes,
long callbackHandle,
long contextHandle);
+
+ void handleServiceBrowse(
+ String[] instanceName, String serviceType, long callbackHandle, long contextHandle);
}
diff --git a/src/platform/android/java/chip/platform/ChipMdnsCallbackImpl.java b/src/platform/android/java/chip/platform/ChipMdnsCallbackImpl.java
index b57189994dab58..6a3d9304ac4665 100644
--- a/src/platform/android/java/chip/platform/ChipMdnsCallbackImpl.java
+++ b/src/platform/android/java/chip/platform/ChipMdnsCallbackImpl.java
@@ -17,12 +17,27 @@
*/
package chip.platform;
+import java.util.Map;
+
public class ChipMdnsCallbackImpl implements ChipMdnsCallback {
public native void handleServiceResolve(
String instanceName,
String serviceType,
+ String hostName,
String address,
int port,
+ Map attributes,
long callbackHandle,
long contextHandle);
+
+ public native void handleServiceBrowse(
+ String[] instanceName, String serviceType, long callbackHandle, long contextHandle);
+
+ public String[] getAttributeKeys(Map attributes) {
+ return attributes.keySet().toArray(new String[attributes.size()]);
+ }
+
+ public byte[] getAttributeData(Map attributes, String key) {
+ return attributes.get(key);
+ }
}
diff --git a/src/platform/android/java/chip/platform/NsdManagerServiceBrowser.java b/src/platform/android/java/chip/platform/NsdManagerServiceBrowser.java
new file mode 100644
index 00000000000000..cd83ce564c09e2
--- /dev/null
+++ b/src/platform/android/java/chip/platform/NsdManagerServiceBrowser.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2021 Project CHIP Authors
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package chip.platform;
+
+import android.content.Context;
+import android.net.nsd.NsdManager;
+import android.net.nsd.NsdServiceInfo;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiManager.MulticastLock;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+public class NsdManagerServiceBrowser implements ServiceBrowser {
+ private static final String TAG = NsdManagerServiceBrowser.class.getSimpleName();
+ private static final long DISCOVER_SERVICE_TIMEOUT = 5000;
+ private final NsdManager nsdManager;
+ private MulticastLock multicastLock;
+ private Handler mainThreadHandler;
+
+ private HashMap callbackMap;
+
+ public NsdManagerServiceBrowser(Context context) {
+ this.nsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE);
+ this.mainThreadHandler = new Handler(Looper.getMainLooper());
+
+ this.multicastLock =
+ ((WifiManager) context.getSystemService(Context.WIFI_SERVICE))
+ .createMulticastLock("chipDiscoverMulticastLock");
+ this.multicastLock.setReferenceCounted(true);
+ callbackMap = new HashMap<>();
+ }
+
+ @Override
+ public void browse(
+ final String serviceType,
+ final long callbackHandle,
+ final long contextHandle,
+ final ChipMdnsCallback chipMdnsCallback) {
+ Runnable timeoutRunnable =
+ new Runnable() {
+ @Override
+ public void run() {
+ stopDiscover(callbackHandle, chipMdnsCallback);
+ }
+ };
+ startDiscover(serviceType, callbackHandle, contextHandle, chipMdnsCallback);
+ mainThreadHandler.postDelayed(timeoutRunnable, DISCOVER_SERVICE_TIMEOUT);
+ }
+
+ public void startDiscover(
+ final String serviceType,
+ final long callbackHandle,
+ final long contextHandle,
+ final ChipMdnsCallback chipMdnsCallback) {
+ if (callbackMap.containsKey(callbackHandle)) {
+ Log.d(TAG, "Invalid callbackHandle");
+ return;
+ }
+
+ NsdManagerDiscovery discovery =
+ new NsdManagerDiscovery(serviceType, callbackHandle, contextHandle);
+ multicastLock.acquire();
+
+ Log.d(TAG, "Starting service discovering for '" + serviceType + "'");
+
+ this.nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD, discovery);
+ callbackMap.put(callbackHandle, discovery);
+ }
+
+ public void stopDiscover(final long callbackHandle, final ChipMdnsCallback chipMdnsCallback) {
+ if (!callbackMap.containsKey(callbackHandle)) {
+ Log.d(TAG, "Invalid callbackHandle");
+ return;
+ }
+
+ NsdManagerDiscovery discovery = callbackMap.remove(callbackHandle);
+ if (multicastLock.isHeld()) {
+ multicastLock.release();
+ }
+
+ this.nsdManager.stopServiceDiscovery(discovery);
+ discovery.handleServiceBrowse(chipMdnsCallback);
+ }
+
+ public class NsdManagerDiscovery implements NsdManager.DiscoveryListener {
+ private String serviceType;
+ private long callbackHandle;
+ private long contextHandle;
+ private ArrayList serviceNameList = new ArrayList<>();
+
+ public NsdManagerDiscovery(String serviceType, long callbackHandle, long contextHandle) {
+ this.serviceType = serviceType;
+ this.callbackHandle = callbackHandle;
+ this.contextHandle = contextHandle;
+ }
+
+ @Override
+ public void onStartDiscoveryFailed(String serviceType, int errorCode) {
+ Log.w(TAG, "Failed to start discovery service '" + serviceType + "': " + errorCode);
+ }
+
+ @Override
+ public void onDiscoveryStarted(String serviceType) {
+ Log.i(TAG, "Started service '" + serviceType + "'");
+ }
+
+ @Override
+ public void onServiceFound(NsdServiceInfo serviceInfo) {
+ Log.i(TAG, "Found service '" + serviceInfo.getServiceName() + "'");
+ serviceNameList.add(serviceInfo.getServiceName());
+ }
+
+ @Override
+ public void onServiceLost(NsdServiceInfo serviceInfo) {
+ Log.i(TAG, "Lost service '" + serviceType + "'");
+ }
+
+ @Override
+ public void onStopDiscoveryFailed(String serviceType, int errorCode) {
+ Log.w(TAG, "Failed to stop discovery service '" + serviceType + "': " + errorCode);
+ }
+
+ @Override
+ public void onDiscoveryStopped(String serviceType) {
+ Log.w(TAG, "Succeed to stop discovery service '" + serviceType);
+ }
+
+ public void handleServiceBrowse(ChipMdnsCallback chipMdnsCallback) {
+ chipMdnsCallback.handleServiceBrowse(
+ serviceNameList.toArray(new String[serviceNameList.size()]),
+ serviceType,
+ callbackHandle,
+ contextHandle);
+ }
+ }
+}
diff --git a/src/platform/android/java/chip/platform/NsdManagerServiceResolver.java b/src/platform/android/java/chip/platform/NsdManagerServiceResolver.java
index f5bb8553466d4e..8baf8ebe938e38 100644
--- a/src/platform/android/java/chip/platform/NsdManagerServiceResolver.java
+++ b/src/platform/android/java/chip/platform/NsdManagerServiceResolver.java
@@ -64,8 +64,10 @@ public void resolve(
new Runnable() {
@Override
public void run() {
- // Ensure we always release the multicast lock. It's possible that we release the
- // multicast lock here before ResolveListener returns, but since NsdManager has no API
+ // Ensure we always release the multicast lock. It's possible that we release
+ // the
+ // multicast lock here before ResolveListener returns, but since NsdManager has
+ // no API
// to cancel service resolution, there's not much we can do here.
if (multicastLock.isHeld()) {
multicastLock.release();
@@ -82,8 +84,7 @@ public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
TAG,
"Failed to resolve service '" + serviceInfo.getServiceName() + "': " + errorCode);
chipMdnsCallback.handleServiceResolve(
- instanceName, serviceType, null, 0, callbackHandle, contextHandle);
-
+ instanceName, serviceType, null, null, 0, null, callbackHandle, contextHandle);
if (multicastLock.isHeld()) {
multicastLock.release();
}
@@ -102,8 +103,10 @@ public void onServiceResolved(NsdServiceInfo serviceInfo) {
chipMdnsCallback.handleServiceResolve(
instanceName,
serviceType,
+ serviceInfo.getHost().getHostName(),
serviceInfo.getHost().getHostAddress(),
serviceInfo.getPort(),
+ serviceInfo.getAttributes(),
callbackHandle,
contextHandle);
diff --git a/src/platform/android/java/chip/platform/ServiceBrowser.java b/src/platform/android/java/chip/platform/ServiceBrowser.java
new file mode 100644
index 00000000000000..d807ff8e1999a9
--- /dev/null
+++ b/src/platform/android/java/chip/platform/ServiceBrowser.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2021 Project CHIP Authors
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package chip.platform;
+
+/** Interface for browsing network services. */
+public interface ServiceBrowser {
+ public void browse(
+ final String serviceType,
+ final long callbackHandle,
+ final long contextHandle,
+ final ChipMdnsCallback chipMdnsCallback);
+}