2 * Copyright (c) 2020 Project CHIP Authors
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
18 #include "AndroidDeviceControllerWrapper.h"
19 #include "CHIPJNIError.h"
23 using chip::PersistentStorageResultDelegate;
24 using chip::Controller::DeviceCommissioner;
28 bool FindMethod(JNIEnv * env, jobject object, const char * methodName, const char * methodSignature, jmethodID * methodId)
30 if ((env == nullptr) || (object == nullptr))
32 ChipLogError(Controller, "Missing java object for %s", methodName);
36 jclass javaClass = env->GetObjectClass(object);
37 if (javaClass == NULL)
39 ChipLogError(Controller, "Failed to get class for %s", methodName);
43 *methodId = env->GetMethodID(javaClass, methodName, methodSignature);
44 if (*methodId == NULL)
46 ChipLogError(Controller, "Failed to find method %s", methodName);
53 void CallVoidInt(JNIEnv * env, jobject object, const char * methodName, jint argument)
57 if (!FindMethod(env, object, methodName, "(I)V", &method))
62 env->ExceptionClear();
63 env->CallVoidMethod(object, method, argument);
66 CHIP_ERROR N2J_ByteArray(JNIEnv * env, const uint8_t * inArray, uint32_t inArrayLen, jbyteArray & outArray)
68 CHIP_ERROR err = CHIP_NO_ERROR;
70 outArray = env->NewByteArray((int) inArrayLen);
71 VerifyOrExit(outArray != NULL, err = CHIP_ERROR_NO_MEMORY);
73 env->ExceptionClear();
74 env->SetByteArrayRegion(outArray, 0, inArrayLen, (jbyte *) inArray);
75 VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN);
81 CHIP_ERROR N2J_NewStringUTF(JNIEnv * env, const char * inStr, size_t inStrLen, jstring & outString)
83 CHIP_ERROR err = CHIP_NO_ERROR;
84 jbyteArray charArray = NULL;
85 jstring utf8Encoding = NULL;
86 jclass java_lang_String = NULL;
87 jmethodID ctor = NULL;
89 err = N2J_ByteArray(env, reinterpret_cast<const uint8_t *>(inStr), inStrLen, charArray);
92 utf8Encoding = env->NewStringUTF("UTF-8");
93 VerifyOrExit(utf8Encoding != NULL, err = CHIP_ERROR_NO_MEMORY);
95 java_lang_String = env->FindClass("java/lang/String");
96 VerifyOrExit(java_lang_String != NULL, err = CHIP_JNI_ERROR_TYPE_NOT_FOUND);
98 ctor = env->GetMethodID(java_lang_String, "<init>", "([BLjava/lang/String;)V");
99 VerifyOrExit(ctor != NULL, err = CHIP_JNI_ERROR_METHOD_NOT_FOUND);
101 outString = (jstring) env->NewObject(java_lang_String, ctor, charArray, utf8Encoding);
102 VerifyOrExit(outString != NULL, err = CHIP_ERROR_NO_MEMORY);
105 // error code propagated from here, so clear any possible
106 // exceptions that arose here
107 env->ExceptionClear();
109 if (utf8Encoding != NULL)
110 env->DeleteLocalRef(utf8Encoding);
111 if (charArray != NULL)
112 env->DeleteLocalRef(charArray);
117 CHIP_ERROR N2J_NewStringUTF(JNIEnv * env, const char * inStr, jstring & outString)
119 return N2J_NewStringUTF(env, inStr, strlen(inStr), outString);
124 AndroidDeviceControllerWrapper::~AndroidDeviceControllerWrapper()
126 if ((mJavaVM != nullptr) && (mJavaObjectRef != nullptr))
128 GetJavaEnv()->DeleteGlobalRef(mJavaObjectRef);
130 mController->Shutdown();
133 void AndroidDeviceControllerWrapper::SetJavaObjectRef(JavaVM * vm, jobject obj)
136 mJavaObjectRef = GetJavaEnv()->NewGlobalRef(obj);
139 JNIEnv * AndroidDeviceControllerWrapper::GetJavaEnv()
141 if (mJavaVM == nullptr)
146 JNIEnv * env = nullptr;
147 mJavaVM->GetEnv((void **) &env, JNI_VERSION_1_6);
152 AndroidDeviceControllerWrapper * AndroidDeviceControllerWrapper::AllocateNew(JavaVM * vm, jobject deviceControllerObj,
153 chip::NodeId nodeId, chip::System::Layer * systemLayer,
154 chip::Inet::InetLayer * inetLayer,
155 CHIP_ERROR * errInfoOnFailure)
157 if (errInfoOnFailure == nullptr)
159 ChipLogError(Controller, "Missing error info");
162 if (systemLayer == nullptr)
164 ChipLogError(Controller, "Missing system layer");
165 *errInfoOnFailure = CHIP_ERROR_INVALID_ARGUMENT;
168 if (inetLayer == nullptr)
170 ChipLogError(Controller, "Missing inet layer");
171 *errInfoOnFailure = CHIP_ERROR_INVALID_ARGUMENT;
175 *errInfoOnFailure = CHIP_NO_ERROR;
177 std::unique_ptr<DeviceCommissioner> controller(new DeviceCommissioner());
181 *errInfoOnFailure = CHIP_ERROR_NO_MEMORY;
184 std::unique_ptr<AndroidDeviceControllerWrapper> wrapper(new AndroidDeviceControllerWrapper(std::move(controller)));
186 wrapper->SetJavaObjectRef(vm, deviceControllerObj);
187 wrapper->Controller()->SetUdpListenPort(CHIP_PORT + 1);
188 *errInfoOnFailure = wrapper->Controller()->Init(nodeId, wrapper.get(), wrapper.get(), systemLayer, inetLayer);
190 if (*errInfoOnFailure != CHIP_NO_ERROR)
195 *errInfoOnFailure = wrapper->Controller()->ServiceEvents();
197 if (*errInfoOnFailure != CHIP_NO_ERROR)
202 return wrapper.release();
205 void AndroidDeviceControllerWrapper::SendNetworkCredentials(const char * ssid, const char * password)
207 if (mCredentialsDelegate == nullptr)
209 ChipLogError(Controller, "No credential callback available to send Wi-Fi credentials.");
213 ChipLogProgress(Controller, "Sending network credentials for %s...", ssid);
214 mCredentialsDelegate->SendNetworkCredentials(ssid, password);
217 void AndroidDeviceControllerWrapper::SendThreadCredentials(const chip::DeviceLayer::Internal::DeviceNetworkInfo & threadData)
219 if (mCredentialsDelegate == nullptr)
221 ChipLogError(Controller, "No credential callback available to send Thread credentials.");
225 ChipLogProgress(Controller, "Sending Thread credentials for channel %u, PAN ID %x...", threadData.ThreadChannel,
226 threadData.ThreadPANId);
227 mCredentialsDelegate->SendThreadCredentials(threadData);
230 void AndroidDeviceControllerWrapper::OnNetworkCredentialsRequested(chip::RendezvousDeviceCredentialsDelegate * callback)
232 mCredentialsDelegate = callback;
234 JNIEnv * env = GetJavaEnv();
237 if (!FindMethod(env, mJavaObjectRef, "onNetworkCredentialsRequested", "()V", &method))
242 env->ExceptionClear();
243 env->CallVoidMethod(mJavaObjectRef, method);
246 void AndroidDeviceControllerWrapper::OnOperationalCredentialsRequested(const char * csr, size_t csr_length,
247 chip::RendezvousDeviceCredentialsDelegate * callback)
249 mCredentialsDelegate = callback;
251 JNIEnv * env = GetJavaEnv();
254 if (!N2J_ByteArray(env, reinterpret_cast<const uint8_t *>(csr), csr_length, jCsr))
256 ChipLogError(Controller, "Failed to build byte array for operational credential request");
261 if (!FindMethod(env, mJavaObjectRef, "onOperationalCredentialsRequested", "([B)V", &method))
266 env->ExceptionClear();
267 env->CallVoidMethod(mJavaObjectRef, method, jCsr);
270 void AndroidDeviceControllerWrapper::OnStatusUpdate(chip::RendezvousSessionDelegate::Status status)
272 CallVoidInt(GetJavaEnv(), mJavaObjectRef, "onStatusUpdate", static_cast<jint>(status));
275 void AndroidDeviceControllerWrapper::OnPairingComplete(CHIP_ERROR error)
277 CallVoidInt(GetJavaEnv(), mJavaObjectRef, "onPairingComplete", static_cast<jint>(error));
280 void AndroidDeviceControllerWrapper::OnPairingDeleted(CHIP_ERROR error)
282 CallVoidInt(GetJavaEnv(), mJavaObjectRef, "onPairingDeleted", static_cast<jint>(error));
285 void AndroidDeviceControllerWrapper::OnMessage(chip::System::PacketBufferHandle msg) {}
287 void AndroidDeviceControllerWrapper::OnStatusChange(void) {}
289 void AndroidDeviceControllerWrapper::SetStorageDelegate(PersistentStorageResultDelegate * delegate)
291 mStorageResultDelegate = delegate;
294 CHIP_ERROR AndroidDeviceControllerWrapper::SyncGetKeyValue(const char * key, char * value, uint16_t & size)
296 jstring keyString = NULL;
297 jstring valueString = NULL;
298 const char * valueChars = nullptr;
299 CHIP_ERROR err = CHIP_NO_ERROR;
300 jclass storageCls = GetPersistentStorageClass();
301 jmethodID method = GetJavaEnv()->GetStaticMethodID(storageCls, "getKeyValue", "(Ljava/lang/String;)Ljava/lang/String;");
303 GetJavaEnv()->ExceptionClear();
305 err = N2J_NewStringUTF(GetJavaEnv(), key, keyString);
308 valueString = (jstring) GetJavaEnv()->CallStaticObjectMethod(storageCls, method, keyString);
310 if (valueString != NULL)
312 size_t stringLength = GetJavaEnv()->GetStringUTFLength(valueString);
313 if (stringLength > UINT16_MAX - 1)
315 err = CHIP_ERROR_BUFFER_TOO_SMALL;
319 if (value != nullptr)
321 valueChars = GetJavaEnv()->GetStringUTFChars(valueString, 0);
322 size = strlcpy(value, valueChars, size);
323 if (size < stringLength)
325 err = CHIP_ERROR_NO_MEMORY;
331 err = CHIP_ERROR_NO_MEMORY;
333 // Increment size to account for null termination
339 err = CHIP_ERROR_INVALID_ARGUMENT;
343 GetJavaEnv()->ExceptionClear();
344 if (valueChars != nullptr)
346 GetJavaEnv()->ReleaseStringUTFChars(valueString, valueChars);
348 GetJavaEnv()->DeleteLocalRef(keyString);
349 GetJavaEnv()->DeleteLocalRef(valueString);
353 void AndroidDeviceControllerWrapper::AsyncSetKeyValue(const char * key, const char * value)
355 jclass storageCls = GetPersistentStorageClass();
356 jmethodID method = GetJavaEnv()->GetStaticMethodID(storageCls, "setKeyValue", "(Ljava/lang/String;Ljava/lang/String;)V");
358 GetJavaEnv()->ExceptionClear();
360 jstring keyString = NULL;
361 jstring valueString = NULL;
362 CHIP_ERROR err = CHIP_NO_ERROR;
364 err = N2J_NewStringUTF(GetJavaEnv(), key, keyString);
366 err = N2J_NewStringUTF(GetJavaEnv(), value, valueString);
369 GetJavaEnv()->CallStaticVoidMethod(storageCls, method, keyString, valueString);
371 if (mStorageResultDelegate)
373 mStorageResultDelegate->OnPersistentStorageStatus(key, PersistentStorageResultDelegate::Operation::kSET, CHIP_NO_ERROR);
377 GetJavaEnv()->ExceptionClear();
378 GetJavaEnv()->DeleteLocalRef(keyString);
379 GetJavaEnv()->DeleteLocalRef(valueString);
382 void AndroidDeviceControllerWrapper::AsyncDeleteKeyValue(const char * key)
384 jclass storageCls = GetPersistentStorageClass();
385 jmethodID method = GetJavaEnv()->GetStaticMethodID(storageCls, "deleteKeyValue", "(Ljava/lang/String;)V");
387 GetJavaEnv()->ExceptionClear();
389 jstring keyString = NULL;
390 CHIP_ERROR err = CHIP_NO_ERROR;
392 err = N2J_NewStringUTF(GetJavaEnv(), key, keyString);
395 GetJavaEnv()->CallStaticVoidMethod(storageCls, method, keyString);
397 if (mStorageResultDelegate)
399 mStorageResultDelegate->OnPersistentStorageStatus(key, PersistentStorageResultDelegate::Operation::kDELETE, CHIP_NO_ERROR);
403 GetJavaEnv()->ExceptionClear();
404 GetJavaEnv()->DeleteLocalRef(keyString);