| /* |
| * 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); |
| } |