Skip to content

Commit

Permalink
[android] Use common DNS-SD code (project-chip#8543)
Browse files Browse the repository at this point in the history
* [android] Use common DNS-SD code

Currently, Android contains temporary code for resolving
a node ID to an IP address. Rewrite this piece to use common
CHIP DNS-SD layer by providing an adapter between
Discovery_ImplPlatform and NsdManager from the Android
library.

* Fix weird doxygen error

* Address code-review comments

* Support node IDs > 2^63

* Add TODO about interface ID
  • Loading branch information
Damian-Nordic authored and pull[bot] committed Sep 3, 2021
1 parent f46bf40 commit da1f75d
Show file tree
Hide file tree
Showing 11 changed files with 344 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import chip.devicecontroller.ChipDeviceController
import chip.devicecontroller.NsdManagerServiceResolver
import chip.setuppayload.SetupPayloadParser.UnrecognizedQrCodeException
import com.google.chip.chiptool.attestation.AttestationTestFragment
import com.google.chip.chiptool.clusterclient.OnOffClientFragment
import com.google.chip.chiptool.echoclient.EchoClientFragment
import com.google.chip.chiptool.provisioning.DeviceProvisioningFragment
import com.google.chip.chiptool.provisioning.ProvisionNetworkType
Expand All @@ -39,6 +39,7 @@ import com.google.chip.chiptool.setuppayloadscanner.CHIPDeviceInfo
import chip.devicecontroller.PreferencesKeyValueStoreManager
import chip.setuppayload.SetupPayload
import chip.setuppayload.SetupPayloadParser
import com.google.chip.chiptool.clusterclient.OnOffClientFragment

class CHIPToolActivity :
AppCompatActivity(),
Expand All @@ -54,6 +55,7 @@ class CHIPToolActivity :

if (savedInstanceState == null) {
ChipDeviceController.setKeyValueStoreManager(PreferencesKeyValueStoreManager(this))
ChipDeviceController.setServiceResolver(NsdManagerServiceResolver(this))
val fragment = SelectActionFragment.newInstance()
supportFragmentManager
.beginTransaction()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
package com.google.chip.chiptool.clusterclient

import android.content.Context
import android.net.nsd.NsdManager
import android.net.nsd.NsdServiceInfo
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
Expand Down Expand Up @@ -110,39 +107,14 @@ class OnOffClientFragment : Fragment() {
}

private fun updateAddressClick() {
val serviceInfo = NsdServiceInfo().apply {
serviceName = "%016X-%016X".format(
fabricIdEd.text.toString().toLong(),
deviceIdEd.text.toString().toLong()
try{
deviceController.updateDevice(
fabricIdEd.text.toString().toULong().toLong(),
deviceIdEd.text.toString().toULong().toLong()
)
serviceType = "_matter._tcp"
}

// TODO: implement the common CHIP mDNS interface for Android and make CHIP stack call the resolver
val resolverListener = object : NsdManager.ResolveListener {
override fun onResolveFailed(serviceInfo: NsdServiceInfo?, errorCode: Int) {
showMessage("Address resolution failed: $errorCode")
}

override fun onServiceResolved(serviceInfo: NsdServiceInfo?) {
val hostAddress = serviceInfo?.host?.hostAddress ?: ""
val port = serviceInfo?.port ?: 0

showMessage("Address: ${hostAddress}:${port}")

if (hostAddress == "" || port == 0)
return

try {
deviceController.updateAddress(deviceIdEd.text.toString().toLong(), hostAddress, port)
} catch (e: ChipDeviceControllerException) {
showMessage(e.toString())
}
}
}

(requireContext().getSystemService(Context.NSD_SERVICE) as NsdManager).apply {
resolveService(serviceInfo, resolverListener)
showMessage("Address update started")
} catch (ex: Exception) {
showMessage("Address update failed: $ex")
}
}

Expand Down
4 changes: 4 additions & 0 deletions src/controller/java/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ shared_library("jni") {
"JniReferences.cpp",
"JniReferences.h",
"JniTypeWrappers.h",
"MdnsImpl.cpp",
"MdnsImpl.h",
"StackLock.h",
"gen/CHIPClusters-JNI.cpp",
]
Expand Down Expand Up @@ -67,7 +69,9 @@ android_library("java") {
"src/chip/devicecontroller/ChipDeviceController.java",
"src/chip/devicecontroller/ChipDeviceControllerException.java",
"src/chip/devicecontroller/KeyValueStoreManager.java",
"src/chip/devicecontroller/NsdManagerServiceResolver.java",
"src/chip/devicecontroller/PreferencesKeyValueStoreManager.java",
"src/chip/devicecontroller/ServiceResolver.java",
]

javac_flags = [ "-Xlint:deprecation" ]
Expand Down
34 changes: 21 additions & 13 deletions src/controller/java/CHIPDeviceController-JNI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include "CHIPJNIError.h"
#include "JniReferences.h"
#include "JniTypeWrappers.h"
#include "MdnsImpl.h"
#include "StackLock.h"

#include <app/chip-zcl-zpro-codec.h>
Expand Down Expand Up @@ -261,6 +262,22 @@ JNI_METHOD(void, setKeyValueStoreManager)(JNIEnv * env, jclass self, jobject man
chip::DeviceLayer::PersistedStorage::KeyValueStoreMgrImpl().InitializeWithObject(manager);
}

JNI_METHOD(void, setServiceResolver)(JNIEnv * env, jclass self, jobject resolver)
{
using namespace chip::Mdns;
StackLockGuard lock(JniReferences::GetInstance().GetStackLock());
InitializeWithObject(resolver);
}

JNI_METHOD(void, handleServiceResolve)
(JNIEnv * env, jclass self, jstring instanceName, jstring serviceType, jstring address, jint port, jlong callbackHandle,
jlong contextHandle)
{
using namespace chip::Mdns;
StackLockGuard lock(JniReferences::GetInstance().GetStackLock());
HandleResolve(instanceName, serviceType, address, port, callbackHandle, contextHandle);
}

JNI_METHOD(void, pairDevice)
(JNIEnv * env, jobject self, jlong handle, jlong deviceId, jint connObj, jlong pinCode, jbyteArray csrNonce)
{
Expand Down Expand Up @@ -421,25 +438,16 @@ JNI_METHOD(jstring, getIpAddress)(JNIEnv * env, jobject self, jlong handle, jlon
return env->NewStringUTF(addrStr);
}

JNI_METHOD(void, updateAddress)(JNIEnv * env, jobject self, jlong handle, jlong deviceId, jstring address, jint port)
JNI_METHOD(void, updateDevice)(JNIEnv * env, jobject self, jlong handle, jlong fabricId, jlong deviceId)
{
StackLockGuard lock(JniReferences::GetInstance().GetStackLock());
Device * chipDevice = nullptr;
CHIP_ERROR err = CHIP_NO_ERROR;

GetCHIPDevice(env, handle, deviceId, &chipDevice);

Inet::IPAddress ipAddress = {};
JniUtfString addressAccessor(env, address);
VerifyOrExit(Inet::IPAddress::FromString(addressAccessor.c_str(), ipAddress), err = CHIP_ERROR_INVALID_ADDRESS);
VerifyOrExit(CanCastTo<uint16_t>(port), err = CHIP_ERROR_INVALID_ADDRESS);

err = chipDevice->UpdateAddress(Transport::PeerAddress::UDP(ipAddress, port));
AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle);
CHIP_ERROR err = wrapper->Controller()->UpdateDevice(static_cast<chip::NodeId>(deviceId), static_cast<uint64_t>(fabricId));

exit:
if (err != CHIP_NO_ERROR)
{
ChipLogError(Controller, "Failed to update address");
ChipLogError(Controller, "Failed to update device");
ThrowError(env, err);
}
}
Expand Down
155 changes: 155 additions & 0 deletions src/controller/java/MdnsImpl.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
/*
*
* Copyright (c) 2021 Project CHIP Authors
*
* 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.
*/

#include "MdnsImpl.h"

#include "CHIPJNIError.h"
#include "JniReferences.h"
#include "JniTypeWrappers.h"

#include <lib/mdns/platform/Mdns.h>
#include <support/CHIPMemString.h>
#include <support/CodeUtils.h>
#include <support/SafeInt.h>
#include <support/logging/CHIPLogging.h>

#include <string>

namespace chip {
namespace Mdns {

using namespace chip::Controller;
using namespace chip::Platform;

namespace {
jobject sResolverObject = nullptr;
jmethodID sResolveMethod = nullptr;
} // namespace

// Implemention of functions declared in lib/mdns/platform/Mdns.h

CHIP_ERROR ChipMdnsInit(MdnsAsyncReturnCallback initCallback, MdnsAsyncReturnCallback errorCallback, void * context)
{
return CHIP_NO_ERROR;
}

CHIP_ERROR ChipMdnsPublishService(const MdnsService * service)
{
return CHIP_ERROR_NOT_IMPLEMENTED;
}

CHIP_ERROR ChipMdnsStopPublish()
{
return CHIP_NO_ERROR;
}

CHIP_ERROR ChipMdnsStopPublishService(const MdnsService * service)
{
return CHIP_ERROR_NOT_IMPLEMENTED;
}

CHIP_ERROR ChipMdnsBrowse(const char * type, MdnsServiceProtocol protocol, Inet::IPAddressType addressType,
Inet::InterfaceId interface, MdnsBrowseCallback callback, void * context)
{
// TODO: Implement DNS-SD browse for Android
return CHIP_ERROR_NOT_IMPLEMENTED;
}

CHIP_ERROR ChipMdnsResolve(MdnsService * service, Inet::InterfaceId interface, MdnsResolveCallback callback, void * context)
{
VerifyOrReturnError(service != nullptr && callback != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(sResolverObject != nullptr && sResolveMethod != nullptr, CHIP_ERROR_INCORRECT_STATE);

std::string serviceType = service->mType;
serviceType += '.';
serviceType += (service->mProtocol == MdnsServiceProtocol::kMdnsProtocolUdp ? "_udp" : "_tcp");

JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
UtfString jniInstanceName(env, service->mName);
UtfString jniServiceType(env, serviceType.c_str());

env->CallVoidMethod(sResolverObject, sResolveMethod, jniInstanceName.jniValue(), jniServiceType.jniValue(),
reinterpret_cast<jlong>(callback), reinterpret_cast<jlong>(context));

if (env->ExceptionCheck())
{
ChipLogError(Discovery, "Java exception in ChipMdnsResolve");
env->ExceptionDescribe();
env->ExceptionClear();
return CHIP_JNI_ERROR_EXCEPTION_THROWN;
}

return CHIP_NO_ERROR;
}

// Implementation of other methods required by the CHIP stack

void GetMdnsTimeout(timeval & timeout) {}
void HandleMdnsTimeout() {}

// Implemention of Java-specific functions

void InitializeWithObject(jobject resolverObject)
{
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
sResolverObject = env->NewGlobalRef(resolverObject);
jclass resolverClass = env->GetObjectClass(sResolverObject);

VerifyOrReturn(resolverClass != nullptr, ChipLogError(Discovery, "Failed to get Resolver Java class"));

sResolveMethod = env->GetMethodID(resolverClass, "resolve", "(Ljava/lang/String;Ljava/lang/String;JJ)V");

if (sResolveMethod == nullptr)
{
ChipLogError(Discovery, "Failed to access Resolver 'resolve' method");
env->ExceptionClear();
}
}

void HandleResolve(jstring instanceName, jstring serviceType, jstring address, jint port, jlong callbackHandle, jlong contextHandle)
{
VerifyOrReturn(callbackHandle != 0, ChipLogError(Discovery, "HandleResolve called with callback equal to nullptr"));

const auto dispatch = [callbackHandle, contextHandle](CHIP_ERROR error, MdnsService * service = nullptr) {
MdnsResolveCallback callback = reinterpret_cast<MdnsResolveCallback>(callbackHandle);
callback(reinterpret_cast<void *>(contextHandle), service, error);
};

VerifyOrReturn(address != nullptr && port != 0, dispatch(CHIP_ERROR_UNKNOWN_RESOURCE_ID));

JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
JniUtfString jniInstanceName(env, instanceName);
JniUtfString jniServiceType(env, serviceType);
JniUtfString jniAddress(env, address);
Inet::IPAddress ipAddress;

VerifyOrReturn(strlen(jniInstanceName.c_str()) <= kMdnsInstanceNameMaxSize, dispatch(CHIP_ERROR_INVALID_ARGUMENT));
VerifyOrReturn(strlen(jniServiceType.c_str()) <= kMdnsTypeAndProtocolMaxSize, dispatch(CHIP_ERROR_INVALID_ARGUMENT));
VerifyOrReturn(CanCastTo<uint16_t>(port), dispatch(CHIP_ERROR_INVALID_ARGUMENT));
VerifyOrReturn(Inet::IPAddress::FromString(jniAddress.c_str(), ipAddress), dispatch(CHIP_ERROR_INVALID_ARGUMENT));

MdnsService service = {};
CopyString(service.mName, jniInstanceName.c_str());
CopyString(service.mType, jniServiceType.c_str());
service.mAddress.SetValue(ipAddress);
service.mPort = static_cast<uint16_t>(port);

dispatch(CHIP_NO_ERROR, &service);
}

} // namespace Mdns
} // namespace chip
38 changes: 38 additions & 0 deletions src/controller/java/MdnsImpl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
*
* Copyright (c) 2021 Project CHIP Authors
*
* 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.
*/

#pragma once

#include <jni.h>

namespace chip {
namespace Mdns {

/**
* Initialize DNS-SD implementation for Android with an object of a class
* that implements chip.devicecontroller.ServiceResolver interface.
*/
void InitializeWithObject(jobject resolverObject);

/**
* Pass results of the service resolution to the CHIP stack.
*/
void HandleResolve(jstring instanceName, jstring serviceType, jstring address, jint port, jlong callbackHandle,
jlong contextHandle);

} // namespace Mdns
} // namespace chip
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,8 @@ public String getIpAddress(long deviceId) {
return getIpAddress(deviceControllerPtr, deviceId);
}

public void updateAddress(long deviceId, String address, int port) {
updateAddress(deviceControllerPtr, deviceId, address, port);
public void updateDevice(long fabricId, long deviceId) {
updateDevice(deviceControllerPtr, fabricId, deviceId);
}

public void sendMessage(long deviceId, String message) {
Expand Down Expand Up @@ -225,8 +225,7 @@ private native void pairDevice(

private native String getIpAddress(long deviceControllerPtr, long deviceId);

private native void updateAddress(
long deviceControllerPtr, long deviceId, String address, int port);
private native void updateDevice(long deviceControllerPtr, long fabricId, long deviceId);

private native void sendMessage(long deviceControllerPtr, long deviceId, String message);

Expand All @@ -242,6 +241,16 @@ private native void enableThreadNetwork(

public static native void setKeyValueStoreManager(KeyValueStoreManager manager);

public static native void setServiceResolver(ServiceResolver resolver);

public static native void handleServiceResolve(
String instanceName,
String serviceType,
String address,
int port,
long callbackHandle,
long contextHandle);

static {
System.loadLibrary("CHIPController");
}
Expand Down
Loading

0 comments on commit da1f75d

Please sign in to comment.