blob: f0a0bace59ebb77f1d5cbc57eca3015d2601fa43 [file] [log] [blame]
/*
*
* Copyright (c) 2020-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 "DnssdImpl.h"
#include <lib/dnssd/platform/Dnssd.h>
#include <lib/support/CHIPJNIError.h>
#include <lib/support/CHIPMemString.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/JniReferences.h>
#include <lib/support/JniTypeWrappers.h>
#include <lib/support/SafeInt.h>
#include <lib/support/logging/CHIPLogging.h>
#include <platform/internal/CHIPDeviceLayerInternal.h>
#include <cstddef>
#include <jni.h>
#include <string>
namespace chip {
namespace Dnssd {
using namespace chip::Platform;
namespace {
jobject sResolverObject = nullptr;
jobject sMdnsCallbackObject = nullptr;
jmethodID sResolveMethod = nullptr;
jmethodID sPublishMethod = nullptr;
jmethodID sRemoveServicesMethod = nullptr;
} // namespace
// Implemention of functions declared in lib/dnssd/platform/Dnssd.h
CHIP_ERROR ChipDnssdInit(DnssdAsyncReturnCallback initCallback, DnssdAsyncReturnCallback errorCallback, void * context)
{
VerifyOrReturnError(initCallback != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(errorCallback != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
initCallback(context, CHIP_NO_ERROR);
return CHIP_NO_ERROR;
}
CHIP_ERROR ChipDnssdShutdown()
{
return CHIP_NO_ERROR;
}
CHIP_ERROR ChipDnssdRemoveServices()
{
VerifyOrReturnError(sResolverObject != nullptr && sRemoveServicesMethod != nullptr, CHIP_ERROR_INCORRECT_STATE);
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
{
DeviceLayer::StackUnlock unlock;
env->CallVoidMethod(sResolverObject, sRemoveServicesMethod);
}
if (env->ExceptionCheck())
{
ChipLogError(Discovery, "Java exception in ChipDnssdRemoveServices");
env->ExceptionDescribe();
env->ExceptionClear();
return CHIP_JNI_ERROR_EXCEPTION_THROWN;
}
return CHIP_NO_ERROR;
}
CHIP_ERROR ChipDnssdPublishService(const DnssdService * service)
{
VerifyOrReturnError(service != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(sResolverObject != nullptr && sPublishMethod != nullptr, CHIP_ERROR_INCORRECT_STATE);
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
UtfString jniName(env, service->mName);
UtfString jniHostName(env, service->mHostName);
std::string serviceType = service->mType;
serviceType += '.';
serviceType += (service->mProtocol == DnssdServiceProtocol::kDnssdProtocolUdp ? "_udp" : "_tcp");
UtfString jniServiceType(env, serviceType.c_str());
jclass stringClass = env->FindClass("java/lang/String");
jobjectArray keys = env->NewObjectArray(service->mTextEntrySize, stringClass, nullptr);
jclass arrayElemType = env->FindClass("[B");
jobjectArray datas = env->NewObjectArray(service->mTextEntrySize, arrayElemType, nullptr);
for (size_t i = 0; i < service->mTextEntrySize; i++)
{
UtfString jniKey(env, service->mTextEntries[i].mKey);
env->SetObjectArrayElement(keys, i, jniKey.jniValue());
ByteArray jniData(env, (const jbyte *) service->mTextEntries[i].mData, service->mTextEntries[i].mDataSize);
env->SetObjectArrayElement(datas, i, jniData.jniValue());
}
jobjectArray subTypes = env->NewObjectArray(service->mSubTypeSize, stringClass, nullptr);
for (size_t i = 0; i < service->mSubTypeSize; i++)
{
UtfString jniSubType(env, service->mSubTypes[i]);
env->SetObjectArrayElement(subTypes, i, jniSubType.jniValue());
}
{
DeviceLayer::StackUnlock unlock;
env->CallVoidMethod(sResolverObject, sPublishMethod, jniName.jniValue(), jniHostName.jniValue(), jniServiceType.jniValue(),
service->mPort, keys, datas, subTypes);
}
if (env->ExceptionCheck())
{
ChipLogError(Discovery, "Java exception in ChipDnssdPublishService");
env->ExceptionDescribe();
env->ExceptionClear();
return CHIP_JNI_ERROR_EXCEPTION_THROWN;
}
return CHIP_NO_ERROR;
}
CHIP_ERROR ChipDnssdFinalizeServiceUpdate()
{
return CHIP_NO_ERROR;
}
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;
}
CHIP_ERROR ChipDnssdResolve(DnssdService * service, Inet::InterfaceId interface, DnssdResolveCallback callback, void * context)
{
VerifyOrReturnError(service != nullptr && callback != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(sResolverObject != nullptr && sResolveMethod != nullptr, CHIP_ERROR_INCORRECT_STATE);
VerifyOrReturnError(sMdnsCallbackObject != nullptr, CHIP_ERROR_INCORRECT_STATE);
std::string serviceType = service->mType;
serviceType += '.';
serviceType += (service->mProtocol == DnssdServiceProtocol::kDnssdProtocolUdp ? "_udp" : "_tcp");
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
UtfString jniInstanceName(env, service->mName);
UtfString jniServiceType(env, serviceType.c_str());
{
DeviceLayer::StackUnlock unlock;
env->CallVoidMethod(sResolverObject, sResolveMethod, jniInstanceName.jniValue(), jniServiceType.jniValue(),
reinterpret_cast<jlong>(callback), reinterpret_cast<jlong>(context), sMdnsCallbackObject);
}
if (env->ExceptionCheck())
{
ChipLogError(Discovery, "Java exception in ChipDnssdResolve");
env->ExceptionDescribe();
env->ExceptionClear();
return CHIP_JNI_ERROR_EXCEPTION_THROWN;
}
return CHIP_NO_ERROR;
}
// Implemention of Java-specific functions
void InitializeWithObjects(jobject resolverObject, jobject mdnsCallbackObject)
{
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
sResolverObject = env->NewGlobalRef(resolverObject);
sMdnsCallbackObject = env->NewGlobalRef(mdnsCallbackObject);
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;JJLchip/platform/ChipMdnsCallback;)V");
if (sResolveMethod == nullptr)
{
ChipLogError(Discovery, "Failed to access Resolver 'resolve' 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");
if (sPublishMethod == nullptr)
{
ChipLogError(Discovery, "Failed to access Resolver 'publish' method");
env->ExceptionClear();
}
sRemoveServicesMethod = env->GetMethodID(resolverClass, "removeServices", "()V");
if (sRemoveServicesMethod == nullptr)
{
ChipLogError(Discovery, "Failed to access Resolver 'removeServices' 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, DnssdService * service = nullptr) {
DeviceLayer::StackLock lock;
DnssdResolveCallback callback = reinterpret_cast<DnssdResolveCallback>(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()) <= Operational::kInstanceNameMaxLength, dispatch(CHIP_ERROR_INVALID_ARGUMENT));
VerifyOrReturn(strlen(jniServiceType.c_str()) <= kDnssdTypeAndProtocolMaxSize, 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));
DnssdService 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 Dnssd
} // namespace chip