blob: a6b7c37a1f1fb760ed223b0a74adc2cc0b5ecf2a [file] [log] [blame]
/*
* Copyright (c) 2020 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.
*
*/
#include "AndroidDeviceControllerWrapper.h"
#include <memory>
using chip::DeviceController::ChipDeviceController;
namespace {
bool FindMethod(JNIEnv * env, jobject object, const char * methodName, const char * methodSignature, jmethodID * methodId)
{
if ((env == nullptr) || (object == nullptr))
{
ChipLogError(Controller, "Missing java object for %s", methodName);
return false;
}
jclass javaClass = env->GetObjectClass(object);
if (javaClass == NULL)
{
ChipLogError(Controller, "Failed to get class for %s", methodName);
return false;
}
*methodId = env->GetMethodID(javaClass, methodName, methodSignature);
if (*methodId == NULL)
{
ChipLogError(Controller, "Failed to find method %s", methodName);
return false;
}
return true;
}
void CallVoidInt(JNIEnv * env, jobject object, const char * methodName, jint argument)
{
jmethodID method;
if (!FindMethod(env, object, methodName, "(I)V", &method))
{
return;
}
env->ExceptionClear();
env->CallVoidMethod(object, method, argument);
}
bool N2J_ByteArray(JNIEnv * env, const uint8_t * inArray, uint32_t inArrayLen, jbyteArray & outArray)
{
outArray = env->NewByteArray((int) inArrayLen);
if (outArray == nullptr)
{
return false;
}
env->ExceptionClear();
env->SetByteArrayRegion(outArray, 0, inArrayLen, (jbyte *) inArray);
return !env->ExceptionCheck();
}
} // namespace
AndroidDeviceControllerWrapper::~AndroidDeviceControllerWrapper()
{
if ((mJavaVM != nullptr) && (mJavaObjectRef != nullptr))
{
GetJavaEnv()->DeleteGlobalRef(mJavaObjectRef);
}
mController->AppState = nullptr;
mController->Shutdown();
}
void AndroidDeviceControllerWrapper::SetJavaObjectRef(JavaVM * vm, jobject obj)
{
mJavaVM = vm;
mJavaObjectRef = GetJavaEnv()->NewGlobalRef(obj);
}
JNIEnv * AndroidDeviceControllerWrapper::GetJavaEnv()
{
if (mJavaVM == nullptr)
{
return nullptr;
}
JNIEnv * env = nullptr;
mJavaVM->GetEnv((void **) &env, JNI_VERSION_1_6);
return env;
}
AndroidDeviceControllerWrapper * AndroidDeviceControllerWrapper::AllocateNew(chip::NodeId nodeId, chip::System::Layer * systemLayer,
chip::Inet::InetLayer * inetLayer,
CHIP_ERROR * errInfoOnFailure)
{
if (errInfoOnFailure == nullptr)
{
ChipLogError(Controller, "Missing error info");
return nullptr;
}
if (systemLayer == nullptr)
{
ChipLogError(Controller, "Missing system layer");
*errInfoOnFailure = CHIP_ERROR_INVALID_ARGUMENT;
return nullptr;
}
if (inetLayer == nullptr)
{
ChipLogError(Controller, "Missing inet layer");
*errInfoOnFailure = CHIP_ERROR_INVALID_ARGUMENT;
return nullptr;
}
*errInfoOnFailure = CHIP_NO_ERROR;
std::unique_ptr<ChipDeviceController> controller(new ChipDeviceController());
if (!controller)
{
*errInfoOnFailure = CHIP_ERROR_NO_MEMORY;
return nullptr;
}
std::unique_ptr<AndroidDeviceControllerWrapper> wrapper(new AndroidDeviceControllerWrapper(std::move(controller)));
*errInfoOnFailure = wrapper->Controller()->Init(nodeId, systemLayer, inetLayer, wrapper.get());
if (*errInfoOnFailure != CHIP_NO_ERROR)
{
return nullptr;
}
return wrapper.release();
}
void AndroidDeviceControllerWrapper::SendNetworkCredentials(const char * ssid, const char * password)
{
if (mCredentialsDelegate == nullptr)
{
ChipLogError(Controller, "No credential callback available to send credentials.");
return;
}
ChipLogProgress(Controller, "Sending network credentials for %s...", ssid);
mCredentialsDelegate->SendNetworkCredentials(ssid, password);
}
void AndroidDeviceControllerWrapper::OnNetworkCredentialsRequested(chip::RendezvousDeviceCredentialsDelegate * callback)
{
mCredentialsDelegate = callback;
JNIEnv * env = GetJavaEnv();
jmethodID method;
if (!FindMethod(env, mJavaObjectRef, "onNetworkCredentialsRequested", "()V", &method))
{
return;
}
env->ExceptionClear();
env->CallVoidMethod(mJavaObjectRef, method);
}
void AndroidDeviceControllerWrapper::OnOperationalCredentialsRequested(const char * csr, size_t csr_length,
chip::RendezvousDeviceCredentialsDelegate * callback)
{
mCredentialsDelegate = callback;
JNIEnv * env = GetJavaEnv();
jbyteArray jCsr;
if (!N2J_ByteArray(env, reinterpret_cast<const uint8_t *>(csr), csr_length, jCsr))
{
ChipLogError(Controller, "Failed to build byte array for operational credential request");
return;
}
jmethodID method;
if (!FindMethod(env, mJavaObjectRef, "onOperationalCredentialsRequested", "([B)V", &method))
{
return;
}
env->ExceptionClear();
env->CallVoidMethod(mJavaObjectRef, method, jCsr);
}
void AndroidDeviceControllerWrapper::OnStatusUpdate(chip::RendezvousSessionDelegate::Status status)
{
CallVoidInt(GetJavaEnv(), mJavaObjectRef, "onStatusUpdate", static_cast<jint>(status));
}
void AndroidDeviceControllerWrapper::OnPairingComplete(CHIP_ERROR error)
{
CallVoidInt(GetJavaEnv(), mJavaObjectRef, "onPairingComplete", static_cast<jint>(error));
}
void AndroidDeviceControllerWrapper::OnPairingDeleted(CHIP_ERROR error)
{
CallVoidInt(GetJavaEnv(), mJavaObjectRef, "onPairingDeleted", static_cast<jint>(error));
}
void AndroidDeviceControllerWrapper::DeprecatedHardcodeThreadCredentials()
{
if (mCredentialsDelegate == nullptr)
{
ChipLogError(Controller, "No credential callback available to send thread credentials.");
return;
}
using namespace chip::DeviceLayer::Internal;
// This is a dummy implementation of Thread provisioning which allows to test Rendezvous over BLE with
// Thread-enabled devices by sending OpenThread Border Router default credentials.
//
// TODO:
// 1. Figure out whether WiFi or Thread provisioning should be performed
// 2. Call Java code to prompt a user for credentials or use the commissioner component of the app
constexpr uint8_t XPAN_ID[kThreadExtendedPANIdLength] = { 0x11, 0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x22 };
constexpr uint8_t MESH_PREFIX[kThreadMeshPrefixLength] = { 0xFD, 0x11, 0x11, 0x11, 0x11, 0x22, 0x00, 0x00 };
constexpr uint8_t NETWORK_KEY[kThreadMasterKeyLength] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF };
DeviceNetworkInfo threadData = {};
memcpy(threadData.ThreadExtendedPANId, XPAN_ID, sizeof(XPAN_ID));
memcpy(threadData.ThreadMeshPrefix, MESH_PREFIX, sizeof(MESH_PREFIX));
memcpy(threadData.ThreadMasterKey, NETWORK_KEY, sizeof(NETWORK_KEY));
threadData.ThreadPANId = 0x1234;
threadData.ThreadChannel = 15;
threadData.FieldPresent.ThreadExtendedPANId = 1;
threadData.FieldPresent.ThreadMeshPrefix = 1;
mCredentialsDelegate->SendThreadCredentials(threadData);
}