Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / src / controller / java / CHIPDeviceController-JNI.cpp
1 /*
2  *   Copyright (c) 2020-2021 Project CHIP Authors
3  *   All rights reserved.
4  *
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
8  *
9  *       http://www.apache.org/licenses/LICENSE-2.0
10  *
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.
16  *
17  */
18
19 /**
20  *    @file
21  *      Implementation of JNI bridge for CHIP Device Controller for Android apps
22  *
23  */
24
25 #include "AndroidBleApplicationDelegate.h"
26 #include "AndroidBleConnectionDelegate.h"
27 #include "AndroidBlePlatformDelegate.h"
28 #include "AndroidDeviceControllerWrapper.h"
29 #include "CHIPJNIError.h"
30
31 #include <app/chip-zcl-zpro-codec.h>
32 #include <atomic>
33 #include <ble/BleUUID.h>
34 #include <controller/CHIPDeviceController.h>
35 #include <jni.h>
36 #include <pthread.h>
37 #include <support/CHIPMem.h>
38 #include <support/CodeUtils.h>
39 #include <support/ErrorStr.h>
40 #include <support/SafeInt.h>
41 #include <support/logging/CHIPLogging.h>
42
43 // Choose an approximation of PTHREAD_NULL if pthread.h doesn't define one.
44 #ifndef PTHREAD_NULL
45 #define PTHREAD_NULL 0
46 #endif // PTHREAD_NULL
47
48 using namespace chip;
49 using namespace chip::Inet;
50 using namespace chip::Controller;
51
52 #define JNI_METHOD(RETURN, METHOD_NAME)                                                                                            \
53     extern "C" JNIEXPORT RETURN JNICALL Java_chip_devicecontroller_ChipDeviceController_##METHOD_NAME
54
55 #define JNI_ANDROID_CHIP_STACK_METHOD(RETURN, METHOD_NAME)                                                                         \
56     extern "C" JNIEXPORT RETURN JNICALL Java_chip_devicecontroller_AndroidChipStack_##METHOD_NAME
57
58 #define CDC_JNI_CALLBACK_LOCAL_REF_COUNT 256
59
60 static void GetCHIPDevice(JNIEnv * env, long wrapperHandle, uint64_t deviceId, Device ** device);
61 static void HandleNotifyChipConnectionClosed(BLE_CONNECTION_OBJECT connObj);
62 static bool HandleSendCharacteristic(BLE_CONNECTION_OBJECT connObj, const uint8_t * svcId, const uint8_t * charId,
63                                      const uint8_t * characteristicData, uint32_t characteristicDataLen);
64 static bool HandleSubscribeCharacteristic(BLE_CONNECTION_OBJECT connObj, const uint8_t * svcId, const uint8_t * charId);
65 static bool HandleUnsubscribeCharacteristic(BLE_CONNECTION_OBJECT connObj, const uint8_t * svcId, const uint8_t * charId);
66 static bool HandleCloseConnection(BLE_CONNECTION_OBJECT connObj);
67 static uint16_t HandleGetMTU(BLE_CONNECTION_OBJECT connObj);
68 static void HandleNewConnection(void * appState, const uint16_t discriminator);
69 static void ThrowError(JNIEnv * env, CHIP_ERROR errToThrow);
70 static void ReportError(JNIEnv * env, CHIP_ERROR cbErr, const char * cbName);
71 static void * IOThreadMain(void * arg);
72 static CHIP_ERROR GetClassRef(JNIEnv * env, const char * clsType, jclass & outCls);
73 static CHIP_ERROR N2J_ByteArray(JNIEnv * env, const uint8_t * inArray, uint32_t inArrayLen, jbyteArray & outArray);
74 static CHIP_ERROR N2J_Error(JNIEnv * env, CHIP_ERROR inErr, jthrowable & outEx);
75
76 namespace {
77
78 JavaVM * sJVM;
79 System::Layer sSystemLayer;
80 Inet::InetLayer sInetLayer;
81
82 #if CONFIG_NETWORK_LAYER_BLE
83 Ble::BleLayer sBleLayer;
84 AndroidBleApplicationDelegate sBleApplicationDelegate;
85 AndroidBlePlatformDelegate sBlePlatformDelegate;
86 AndroidBleConnectionDelegate sBleConnectionDelegate;
87 #endif
88
89 pthread_mutex_t sStackLock = PTHREAD_MUTEX_INITIALIZER;
90 pthread_t sIOThread        = PTHREAD_NULL;
91 bool sShutdown             = false;
92
93 jclass sAndroidChipStackCls              = NULL;
94 jclass sChipDeviceControllerExceptionCls = NULL;
95
96 /** A scoped lock/unlock around a mutex. */
97 class ScopedPthreadLock
98 {
99 public:
100     ScopedPthreadLock(pthread_mutex_t * mutex) : mMutex(mutex) { pthread_mutex_lock(mMutex); }
101     ~ScopedPthreadLock() { pthread_mutex_unlock(mMutex); }
102
103 private:
104     pthread_mutex_t * mMutex;
105 };
106
107 // Use StackUnlockGuard to temporarily unlock the CHIP BLE stack, e.g. when calling application
108 // or Android BLE code as a result of a BLE event.
109 struct StackUnlockGuard
110 {
111     StackUnlockGuard() { pthread_mutex_unlock(&sStackLock); }
112     ~StackUnlockGuard() { pthread_mutex_lock(&sStackLock); }
113 };
114
115 class JniUtfString
116 {
117 public:
118     JniUtfString(JNIEnv * env, jstring string) : mEnv(env), mString(string) { mChars = env->GetStringUTFChars(string, 0); }
119     ~JniUtfString() { mEnv->ReleaseStringUTFChars(mString, mChars); }
120
121     const char * c_str() const { return mChars; }
122
123 private:
124     JNIEnv * mEnv;
125     jstring mString;
126     const char * mChars;
127 };
128
129 class JniByteArray
130 {
131 public:
132     JniByteArray(JNIEnv * env, jbyteArray array) :
133         mEnv(env), mArray(array), mData(env->GetByteArrayElements(array, nullptr)), mDataLength(env->GetArrayLength(array))
134     {}
135     ~JniByteArray() { mEnv->ReleaseByteArrayElements(mArray, mData, 0); }
136
137     const jbyte * data() const { return mData; }
138     jsize size() const { return mDataLength; }
139
140 private:
141     JNIEnv * mEnv;
142     jbyteArray mArray;
143     jbyte * mData;
144     jsize mDataLength;
145 };
146
147 } // namespace
148
149 // NOTE: Remote device ID is in sync with the echo server device id
150 // At some point, we may want to add an option to connect to a device without
151 // knowing its id, because the ID can be learned on the first response that is received.
152 chip::NodeId kLocalDeviceId  = chip::kTestControllerNodeId;
153 chip::NodeId kRemoteDeviceId = chip::kTestDeviceNodeId;
154
155 jint JNI_OnLoad(JavaVM * jvm, void * reserved)
156 {
157     CHIP_ERROR err = CHIP_NO_ERROR;
158     JNIEnv * env;
159     int pthreadErr = 0;
160
161     ChipLogProgress(Controller, "JNI_OnLoad() called");
162
163     chip::Platform::MemoryInit();
164
165     // Save a reference to the JVM.  Will need this to call back into Java.
166     sJVM = jvm;
167
168     // Get a JNI environment object.
169     sJVM->GetEnv((void **) &env, JNI_VERSION_1_6);
170
171     ChipLogProgress(Controller, "Loading Java class references.");
172
173     // Get various class references need by the API.
174     err = GetClassRef(env, "chip/devicecontroller/AndroidChipStack", sAndroidChipStackCls);
175     SuccessOrExit(err);
176     err = GetClassRef(env, "chip/devicecontroller/ChipDeviceControllerException", sChipDeviceControllerExceptionCls);
177     SuccessOrExit(err);
178     ChipLogProgress(Controller, "Java class references loaded.");
179
180     // Initialize the CHIP System Layer.
181     err = sSystemLayer.Init(NULL);
182     SuccessOrExit(err);
183
184     // Initialize the CHIP Inet layer.
185     err = sInetLayer.Init(sSystemLayer, NULL);
186     SuccessOrExit(err);
187     ChipLogProgress(Controller, "Inet layer initialized.");
188
189 #if CONFIG_NETWORK_LAYER_BLE
190     ChipLogProgress(Controller, "BLE Layer being configured.");
191
192     // Initialize the BleApplicationDelegate
193     sBleApplicationDelegate.SetNotifyChipConnectionClosedCallback(HandleNotifyChipConnectionClosed);
194     // Initialize the BlePlatformDelegate
195     sBlePlatformDelegate.SetSendWriteRequestCallback(HandleSendCharacteristic);
196     sBlePlatformDelegate.SetSubscribeCharacteristicCallback(HandleSubscribeCharacteristic);
197     sBlePlatformDelegate.SetUnsubscribeCharacteristicCallback(HandleUnsubscribeCharacteristic);
198     sBlePlatformDelegate.SetCloseConnectionCallback(HandleCloseConnection);
199     sBlePlatformDelegate.SetGetMTUCallback(HandleGetMTU);
200     // Initialize the BleConnectionDelegate
201     sBleConnectionDelegate.SetNewConnectionCallback(HandleNewConnection);
202
203     ChipLogProgress(Controller, "Asking for BLE Layer initialization.");
204     // Initialize the BleLayer object.
205     err = sBleLayer.Init(&sBlePlatformDelegate, &sBleConnectionDelegate, &sBleApplicationDelegate, &sSystemLayer);
206     SuccessOrExit(err);
207
208     ChipLogProgress(Controller, "BLE was initialized.");
209 #endif
210
211     // Create and start the IO thread.
212     sShutdown  = false;
213     pthreadErr = pthread_create(&sIOThread, NULL, IOThreadMain, NULL);
214     VerifyOrExit(pthreadErr == 0, err = System::MapErrorPOSIX(pthreadErr));
215
216 exit:
217     if (err != CHIP_NO_ERROR)
218     {
219         ThrowError(env, err);
220         JNI_OnUnload(jvm, reserved);
221     }
222
223     return (err == CHIP_NO_ERROR) ? JNI_VERSION_1_6 : JNI_ERR;
224 }
225
226 void JNI_OnUnload(JavaVM * jvm, void * reserved)
227 {
228     ChipLogProgress(Controller, "JNI_OnUnload() called");
229
230     // If the IO thread has been started, shut it down and wait for it to exit.
231     if (sIOThread != PTHREAD_NULL)
232     {
233         sShutdown = true;
234         sSystemLayer.WakeSelect();
235         pthread_join(sIOThread, NULL);
236     }
237
238 #if CONFIG_NETWORK_LAYER_BLE
239     sBleLayer.Shutdown();
240 #endif
241
242     sSystemLayer.Shutdown();
243     sInetLayer.Shutdown();
244     sJVM = NULL;
245
246     chip::Platform::MemoryShutdown();
247 }
248
249 JNI_METHOD(jlong, newDeviceController)(JNIEnv * env, jobject self)
250 {
251     CHIP_ERROR err                           = CHIP_NO_ERROR;
252     AndroidDeviceControllerWrapper * wrapper = NULL;
253     long result                              = 0;
254
255     ChipLogProgress(Controller, "newDeviceController() called");
256
257     wrapper = AndroidDeviceControllerWrapper::AllocateNew(sJVM, self, kLocalDeviceId, &sSystemLayer, &sInetLayer, &err);
258     SuccessOrExit(err);
259
260     result = wrapper->ToJNIHandle();
261
262 exit:
263     if (err != CHIP_NO_ERROR)
264     {
265         if (wrapper != NULL)
266         {
267             delete wrapper;
268         }
269
270         if (err != CHIP_JNI_ERROR_EXCEPTION_THROWN)
271         {
272             ThrowError(env, err);
273         }
274     }
275
276     return result;
277 }
278
279 JNI_METHOD(void, pairDevice)(JNIEnv * env, jobject self, jlong handle, jlong deviceId, jint connObj, jlong pinCode)
280 {
281     CHIP_ERROR err                           = CHIP_NO_ERROR;
282     AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle);
283
284     ChipLogProgress(Controller, "pairDevice() called with device ID, connection object, and pincode");
285
286     {
287         ScopedPthreadLock lock(&sStackLock);
288         sBleLayer.mAppState         = (void *) self;
289         RendezvousParameters params = RendezvousParameters()
290                                           .SetSetupPINCode(pinCode)
291                                           .SetConnectionObject(reinterpret_cast<BLE_CONNECTION_OBJECT>(connObj))
292                                           .SetBleLayer(&sBleLayer)
293                                           .SetPeerAddress(Transport::PeerAddress::BLE());
294         err = wrapper->Controller()->PairDevice(deviceId, params);
295     }
296
297     if (err != CHIP_NO_ERROR)
298     {
299         ChipLogError(Controller, "Failed to pair the device.");
300         ThrowError(env, err);
301     }
302 }
303
304 JNI_METHOD(void, unpairDevice)(JNIEnv * env, jobject self, jlong handle, jlong deviceId)
305 {
306     CHIP_ERROR err                           = CHIP_NO_ERROR;
307     AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle);
308
309     ChipLogProgress(Controller, "unpairDevice() called with device ID");
310
311     {
312         ScopedPthreadLock lock(&sStackLock);
313         err = wrapper->Controller()->UnpairDevice(deviceId);
314     }
315
316     if (err != CHIP_NO_ERROR)
317     {
318         ChipLogError(Controller, "Failed to unpair the device.");
319         ThrowError(env, err);
320     }
321 }
322
323 JNI_METHOD(void, stopDevicePairing)(JNIEnv * env, jobject self, jlong handle, jlong deviceId)
324 {
325     CHIP_ERROR err                           = CHIP_NO_ERROR;
326     AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle);
327
328     ChipLogProgress(Controller, "stopDevicePairing() called with device ID");
329
330     {
331         ScopedPthreadLock lock(&sStackLock);
332         err = wrapper->Controller()->StopPairing(deviceId);
333     }
334
335     if (err != CHIP_NO_ERROR)
336     {
337         ChipLogError(Controller, "Failed to unpair the device.");
338         ThrowError(env, err);
339     }
340 }
341
342 JNI_METHOD(void, sendWiFiCredentials)(JNIEnv * env, jobject self, jlong handle, jstring ssid, jstring password)
343 {
344     JniUtfString ssidStr(env, ssid);
345     JniUtfString passwordStr(env, password);
346
347     ChipLogProgress(Controller, "Sending Wi-Fi credentials for: %s", ssidStr.c_str());
348     {
349         ScopedPthreadLock lock(&sStackLock);
350         AndroidDeviceControllerWrapper::FromJNIHandle(handle)->SendNetworkCredentials(ssidStr.c_str(), passwordStr.c_str());
351     }
352 }
353
354 JNI_METHOD(void, sendThreadCredentials)
355 (JNIEnv * env, jobject self, jlong handle, jint channel, jint panId, jbyteArray xpanId, jbyteArray masterKey)
356 {
357     using namespace chip::DeviceLayer::Internal;
358
359     JniByteArray xpanIdBytes(env, xpanId);
360     JniByteArray masterKeyBytes(env, masterKey);
361
362     VerifyOrReturn(CanCastTo<uint8_t>(channel), ChipLogError(Controller, "sendThreadCredentials() called with invalid Channel"));
363     VerifyOrReturn(CanCastTo<uint16_t>(panId), ChipLogError(Controller, "sendThreadCredentials() called with invalid PAN ID"));
364     VerifyOrReturn(xpanIdBytes.size() <= static_cast<jsize>(kThreadExtendedPANIdLength),
365                    ChipLogError(Controller, "sendThreadCredentials() called with invalid XPAN ID"));
366     VerifyOrReturn(masterKeyBytes.size() <= static_cast<jsize>(kThreadMasterKeyLength),
367                    ChipLogError(Controller, "sendThreadCredentials() called with invalid Master Key"));
368
369     DeviceNetworkInfo threadData                = {};
370     threadData.ThreadChannel                    = channel;
371     threadData.ThreadPANId                      = panId;
372     threadData.FieldPresent.ThreadExtendedPANId = 1;
373     memcpy(threadData.ThreadExtendedPANId, xpanIdBytes.data(), xpanIdBytes.size());
374     memcpy(threadData.ThreadMasterKey, masterKeyBytes.data(), masterKeyBytes.size());
375
376     ScopedPthreadLock lock(&sStackLock);
377     AndroidDeviceControllerWrapper::FromJNIHandle(handle)->SendThreadCredentials(threadData);
378 }
379
380 JNI_METHOD(void, pairTestDeviceWithoutSecurity)(JNIEnv * env, jobject self, jlong handle, jstring deviceAddr)
381 {
382     CHIP_ERROR err                           = CHIP_NO_ERROR;
383     AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle);
384     chip::Inet::IPAddress deviceIPAddr;
385
386     ChipLogProgress(Controller, "pairTestDeviceWithoutSecurity() called with IP Address");
387
388     const char * deviceAddrStr = env->GetStringUTFChars(deviceAddr, 0);
389     deviceIPAddr.FromString(deviceAddrStr, deviceIPAddr);
390     env->ReleaseStringUTFChars(deviceAddr, deviceAddrStr);
391
392     {
393         ScopedPthreadLock lock(&sStackLock);
394         Controller::SerializedDevice mSerializedTestDevice;
395         err = wrapper->Controller()->PairTestDeviceWithoutSecurity(kRemoteDeviceId, chip::Transport::PeerAddress::UDP(deviceIPAddr),
396                                                                    mSerializedTestDevice);
397     }
398
399     if (err != CHIP_NO_ERROR)
400     {
401         ChipLogError(Controller, "Failed to connect to device.");
402         ThrowError(env, err);
403     }
404 }
405
406 JNI_METHOD(void, disconnectDevice)(JNIEnv * env, jobject self, jlong handle, jlong deviceId)
407 {
408     AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle);
409     CHIP_ERROR err                           = CHIP_NO_ERROR;
410     Device * chipDevice                      = nullptr;
411
412     ChipLogProgress(Controller, "disconnectDevice() called with deviceId");
413
414     {
415         ScopedPthreadLock lock(&sStackLock);
416         err = wrapper->Controller()->GetDevice(deviceId, &chipDevice);
417     }
418
419     if (err != CHIP_NO_ERROR || !chipDevice)
420     {
421         ChipLogError(Controller, "Failed to get paired device.");
422         ThrowError(env, err);
423     }
424
425     wrapper->Controller()->ReleaseDevice(chipDevice);
426 }
427
428 JNI_METHOD(jboolean, isActive)(JNIEnv * env, jobject self, jlong handle)
429 {
430     Device * chipDevice = reinterpret_cast<Device *>(handle);
431
432     {
433         ScopedPthreadLock lock(&sStackLock);
434         return chipDevice->IsActive();
435     }
436 }
437
438 void GetCHIPDevice(JNIEnv * env, long wrapperHandle, uint64_t deviceId, Device ** chipDevice)
439 {
440     AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(wrapperHandle);
441     CHIP_ERROR err                           = CHIP_NO_ERROR;
442
443     {
444         ScopedPthreadLock lock(&sStackLock);
445         err = wrapper->Controller()->GetDevice(deviceId, chipDevice);
446     }
447
448     if (err != CHIP_NO_ERROR || !chipDevice)
449     {
450         ChipLogError(Controller, "Failed to get paired device.");
451         ThrowError(env, err);
452     }
453 }
454
455 JNI_METHOD(jstring, getIpAddress)(JNIEnv * env, jobject self, jlong handle, jlong deviceId)
456 {
457     Device * chipDevice = nullptr;
458
459     GetCHIPDevice(env, handle, deviceId, &chipDevice);
460
461     chip::Inet::IPAddress addr;
462     uint16_t port;
463     char addrStr[50];
464
465     {
466         ScopedPthreadLock lock(&sStackLock);
467         if (!chipDevice->GetAddress(addr, port))
468             return nullptr;
469     }
470
471     addr.ToString(addrStr);
472     return env->NewStringUTF(addrStr);
473 }
474
475 JNI_METHOD(void, sendMessage)(JNIEnv * env, jobject self, jlong handle, jlong deviceId, jstring messageObj)
476 {
477     CHIP_ERROR err      = CHIP_NO_ERROR;
478     Device * chipDevice = nullptr;
479
480     ChipLogProgress(Controller, "sendMessage() called with device id and message object");
481
482     GetCHIPDevice(env, handle, deviceId, &chipDevice);
483
484     const char * messageStr = env->GetStringUTFChars(messageObj, 0);
485     size_t messageLen       = strlen(messageStr);
486
487     {
488         ScopedPthreadLock lock(&sStackLock);
489
490         System::PacketBufferHandle buffer = System::PacketBufferHandle::NewWithData(messageStr, messageLen);
491         if (buffer.IsNull())
492         {
493             err = CHIP_ERROR_NO_MEMORY;
494         }
495         else
496         {
497             err = chipDevice->SendMessage(std::move(buffer));
498         }
499     }
500
501     env->ReleaseStringUTFChars(messageObj, messageStr);
502
503     if (err != CHIP_NO_ERROR)
504     {
505         ChipLogError(Controller, "Failed to send message.");
506         ThrowError(env, err);
507     }
508 }
509
510 JNI_METHOD(void, sendCommand)(JNIEnv * env, jobject self, jlong handle, jlong deviceId, jobject commandObj, jint aValue)
511 {
512     CHIP_ERROR err      = CHIP_NO_ERROR;
513     Device * chipDevice = nullptr;
514
515     GetCHIPDevice(env, handle, deviceId, &chipDevice);
516
517     ChipLogProgress(Controller, "sendCommand() called");
518
519     jclass commandCls         = env->GetObjectClass(commandObj);
520     jmethodID commandMethodID = env->GetMethodID(commandCls, "getValue", "()I");
521     jint commandID            = env->CallIntMethod(commandObj, commandMethodID);
522
523     {
524         ScopedPthreadLock lock(&sStackLock);
525         System::PacketBufferHandle buffer;
526
527         // Hardcode endpoint to 1 for now
528         uint8_t endpoint = 1;
529
530         switch (commandID)
531         {
532         case 0:
533             buffer = encodeOnOffClusterOffCommand(0, endpoint);
534             break;
535         case 1:
536             buffer = encodeOnOffClusterOnCommand(0, endpoint);
537             break;
538         case 2:
539             buffer = encodeOnOffClusterToggleCommand(0, endpoint);
540             break;
541         case 3:
542             buffer = encodeLevelControlClusterMoveToLevelCommand(0, endpoint, (uint8_t)(aValue & 0xff), 0xFFFF, 0, 0);
543             break;
544         default:
545             ChipLogError(Controller, "Unknown command: %d", commandID);
546             return;
547         }
548
549         if (buffer.IsNull())
550         {
551             err = CHIP_ERROR_NO_MEMORY;
552         }
553         else
554         {
555             err = chipDevice->SendMessage(std::move(buffer));
556         }
557     }
558
559     if (err != CHIP_NO_ERROR)
560     {
561         ChipLogError(Controller, "Failed to send CHIP command.");
562         ThrowError(env, err);
563     }
564 }
565
566 JNI_METHOD(jboolean, openPairingWindow)(JNIEnv * env, jobject self, jlong handle, jlong deviceId, jint duration)
567 {
568     CHIP_ERROR err      = CHIP_NO_ERROR;
569     Device * chipDevice = nullptr;
570     chip::SetupPayload setupPayload;
571
572     GetCHIPDevice(env, handle, deviceId, &chipDevice);
573
574     {
575         ScopedPthreadLock lock(&sStackLock);
576         err = chipDevice->OpenPairingWindow(duration, chip::Controller::Device::PairingWindowOption::kOriginalSetupCode,
577                                             setupPayload);
578     }
579
580     if (err != CHIP_NO_ERROR)
581     {
582         ChipLogError(Controller, "OpenPairingWindow failed: %d", err);
583         return false;
584     }
585
586     return true;
587 }
588
589 static bool JavaBytesToUUID(JNIEnv * env, jbyteArray value, chip::Ble::ChipBleUUID & uuid)
590 {
591     const auto valueBegin  = env->GetByteArrayElements(value, nullptr);
592     const auto valueLength = env->GetArrayLength(value);
593     bool result            = true;
594
595     VerifyOrExit(valueBegin && valueLength == sizeof(uuid.bytes), result = false);
596     memcpy(uuid.bytes, valueBegin, valueLength);
597
598 exit:
599     env->ReleaseByteArrayElements(value, valueBegin, 0);
600     return result;
601 }
602
603 JNI_ANDROID_CHIP_STACK_METHOD(void, handleIndicationReceived)
604 (JNIEnv * env, jobject self, jint conn, jbyteArray svcId, jbyteArray charId, jbyteArray value)
605 {
606     BLE_CONNECTION_OBJECT const connObj = reinterpret_cast<BLE_CONNECTION_OBJECT>(conn);
607     const auto valueBegin               = env->GetByteArrayElements(value, nullptr);
608     const auto valueLength              = env->GetArrayLength(value);
609
610     chip::Ble::ChipBleUUID svcUUID;
611     chip::Ble::ChipBleUUID charUUID;
612     chip::System::PacketBufferHandle buffer;
613
614     VerifyOrExit(JavaBytesToUUID(env, svcId, svcUUID),
615                  ChipLogError(Controller, "handleIndicationReceived() called with invalid service ID"));
616     VerifyOrExit(JavaBytesToUUID(env, charId, charUUID),
617                  ChipLogError(Controller, "handleIndicationReceived() called with invalid characteristic ID"));
618
619     buffer = System::PacketBufferHandle::NewWithData(valueBegin, valueLength);
620     VerifyOrExit(!buffer.IsNull(), ChipLogError(Controller, "Failed to allocate packet buffer"));
621
622     pthread_mutex_lock(&sStackLock);
623     sBleLayer.HandleIndicationReceived(connObj, &svcUUID, &charUUID, std::move(buffer));
624     pthread_mutex_unlock(&sStackLock);
625 exit:
626     env->ReleaseByteArrayElements(value, valueBegin, 0);
627 }
628
629 JNI_ANDROID_CHIP_STACK_METHOD(void, handleWriteConfirmation)
630 (JNIEnv * env, jobject self, jint conn, jbyteArray svcId, jbyteArray charId)
631 {
632     BLE_CONNECTION_OBJECT const connObj = reinterpret_cast<BLE_CONNECTION_OBJECT>(conn);
633
634     chip::Ble::ChipBleUUID svcUUID;
635     chip::Ble::ChipBleUUID charUUID;
636     VerifyOrExit(JavaBytesToUUID(env, svcId, svcUUID),
637                  ChipLogError(Controller, "handleWriteConfirmation() called with invalid service ID"));
638     VerifyOrExit(JavaBytesToUUID(env, charId, charUUID),
639                  ChipLogError(Controller, "handleWriteConfirmation() called with invalid characteristic ID"));
640
641     pthread_mutex_lock(&sStackLock);
642     sBleLayer.HandleWriteConfirmation(connObj, &svcUUID, &charUUID);
643     pthread_mutex_unlock(&sStackLock);
644 exit:
645     return;
646 }
647
648 JNI_ANDROID_CHIP_STACK_METHOD(void, handleSubscribeComplete)
649 (JNIEnv * env, jobject self, jint conn, jbyteArray svcId, jbyteArray charId)
650 {
651     BLE_CONNECTION_OBJECT const connObj = reinterpret_cast<BLE_CONNECTION_OBJECT>(conn);
652
653     chip::Ble::ChipBleUUID svcUUID;
654     chip::Ble::ChipBleUUID charUUID;
655     VerifyOrExit(JavaBytesToUUID(env, svcId, svcUUID),
656                  ChipLogError(Controller, "handleSubscribeComplete() called with invalid service ID"));
657     VerifyOrExit(JavaBytesToUUID(env, charId, charUUID),
658                  ChipLogError(Controller, "handleSubscribeComplete() called with invalid characteristic ID"));
659
660     pthread_mutex_lock(&sStackLock);
661     sBleLayer.HandleSubscribeComplete(connObj, &svcUUID, &charUUID);
662     pthread_mutex_unlock(&sStackLock);
663 exit:
664     return;
665 }
666
667 JNI_ANDROID_CHIP_STACK_METHOD(void, handleUnsubscribeComplete)
668 (JNIEnv * env, jobject self, jint conn, jbyteArray svcId, jbyteArray charId)
669 {
670     BLE_CONNECTION_OBJECT const connObj = reinterpret_cast<BLE_CONNECTION_OBJECT>(conn);
671
672     chip::Ble::ChipBleUUID svcUUID;
673     chip::Ble::ChipBleUUID charUUID;
674     VerifyOrExit(JavaBytesToUUID(env, svcId, svcUUID),
675                  ChipLogError(Controller, "handleUnsubscribeComplete() called with invalid service ID"));
676     VerifyOrExit(JavaBytesToUUID(env, charId, charUUID),
677                  ChipLogError(Controller, "handleUnsubscribeComplete() called with invalid characteristic ID"));
678
679     pthread_mutex_lock(&sStackLock);
680     sBleLayer.HandleUnsubscribeComplete(connObj, &svcUUID, &charUUID);
681     pthread_mutex_unlock(&sStackLock);
682 exit:
683     return;
684 }
685
686 JNI_ANDROID_CHIP_STACK_METHOD(void, handleConnectionError)(JNIEnv * env, jobject self, jint conn)
687 {
688     BLE_CONNECTION_OBJECT const connObj = reinterpret_cast<BLE_CONNECTION_OBJECT>(conn);
689
690     pthread_mutex_lock(&sStackLock);
691     sBleLayer.HandleConnectionError(connObj, BLE_ERROR_APP_CLOSED_CONNECTION);
692     pthread_mutex_unlock(&sStackLock);
693 }
694
695 JNI_METHOD(void, deleteDeviceController)(JNIEnv * env, jobject self, jlong handle)
696 {
697     AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle);
698
699     ChipLogProgress(Controller, "deleteDeviceController() called");
700
701     if (wrapper != NULL)
702     {
703         delete wrapper;
704     }
705 }
706
707 void HandleNotifyChipConnectionClosed(BLE_CONNECTION_OBJECT connObj)
708 {
709     StackUnlockGuard unlockGuard;
710     CHIP_ERROR err = CHIP_NO_ERROR;
711     JNIEnv * env;
712     jmethodID method;
713     intptr_t tmpConnObj;
714
715     ChipLogProgress(Controller, "Received NotifyChipConnectionClosed");
716
717     sJVM->GetEnv((void **) &env, JNI_VERSION_1_6);
718
719     method = env->GetStaticMethodID(sAndroidChipStackCls, "onNotifyChipConnectionClosed", "(I)V");
720     VerifyOrExit(method != NULL, err = CHIP_JNI_ERROR_METHOD_NOT_FOUND);
721
722     ChipLogProgress(Controller, "Calling Java NotifyChipConnectionClosed");
723
724     env->ExceptionClear();
725     tmpConnObj = reinterpret_cast<intptr_t>(connObj);
726     env->CallStaticVoidMethod(sAndroidChipStackCls, method, static_cast<jint>(tmpConnObj));
727     VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN);
728
729 exit:
730     if (err != CHIP_NO_ERROR)
731     {
732         ReportError(env, err, __FUNCTION__);
733     }
734     env->ExceptionClear();
735 }
736
737 bool HandleSendCharacteristic(BLE_CONNECTION_OBJECT connObj, const uint8_t * svcId, const uint8_t * charId,
738                               const uint8_t * characteristicData, uint32_t characteristicDataLen)
739 {
740     StackUnlockGuard unlockGuard;
741     CHIP_ERROR err = CHIP_NO_ERROR;
742     JNIEnv * env;
743     jbyteArray svcIdObj;
744     jbyteArray charIdObj;
745     jbyteArray characteristicDataObj;
746     jmethodID method;
747     intptr_t tmpConnObj;
748     bool rc = false;
749
750     ChipLogProgress(Controller, "Received SendCharacteristic");
751
752     sJVM->GetEnv((void **) &env, JNI_VERSION_1_6);
753
754     err = N2J_ByteArray(env, svcId, 16, svcIdObj);
755     SuccessOrExit(err);
756
757     err = N2J_ByteArray(env, charId, 16, charIdObj);
758     SuccessOrExit(err);
759
760     err = N2J_ByteArray(env, characteristicData, characteristicDataLen, characteristicDataObj);
761     SuccessOrExit(err);
762
763     method = env->GetStaticMethodID(sAndroidChipStackCls, "onSendCharacteristic", "(I[B[B[B)Z");
764     VerifyOrExit(method != NULL, err = CHIP_JNI_ERROR_METHOD_NOT_FOUND);
765
766     ChipLogProgress(Controller, "Calling Java SendCharacteristic");
767
768     env->ExceptionClear();
769     tmpConnObj = reinterpret_cast<intptr_t>(connObj);
770     rc = (bool) env->CallStaticBooleanMethod(sAndroidChipStackCls, method, static_cast<jint>(tmpConnObj), svcIdObj, charIdObj,
771                                              characteristicDataObj);
772     VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN);
773
774 exit:
775     if (err != CHIP_NO_ERROR)
776     {
777         ReportError(env, err, __FUNCTION__);
778         rc = false;
779     }
780     env->ExceptionClear();
781
782     return rc;
783 }
784
785 bool HandleSubscribeCharacteristic(BLE_CONNECTION_OBJECT connObj, const uint8_t * svcId, const uint8_t * charId)
786 {
787     StackUnlockGuard unlockGuard;
788     CHIP_ERROR err = CHIP_NO_ERROR;
789     JNIEnv * env;
790     jbyteArray svcIdObj;
791     jbyteArray charIdObj;
792     jmethodID method;
793     intptr_t tmpConnObj;
794     bool rc = false;
795
796     ChipLogProgress(Controller, "Received SubscribeCharacteristic");
797
798     sJVM->GetEnv((void **) &env, JNI_VERSION_1_6);
799
800     err = N2J_ByteArray(env, svcId, 16, svcIdObj);
801     SuccessOrExit(err);
802
803     err = N2J_ByteArray(env, charId, 16, charIdObj);
804     SuccessOrExit(err);
805
806     method = env->GetStaticMethodID(sAndroidChipStackCls, "onSubscribeCharacteristic", "(I[B[B)Z");
807     VerifyOrExit(method != NULL, err = CHIP_JNI_ERROR_METHOD_NOT_FOUND);
808
809     ChipLogProgress(Controller, "Calling Java SubscribeCharacteristic");
810
811     env->ExceptionClear();
812     tmpConnObj = reinterpret_cast<intptr_t>(connObj);
813     rc = (bool) env->CallStaticBooleanMethod(sAndroidChipStackCls, method, static_cast<jint>(tmpConnObj), svcIdObj, charIdObj);
814     VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN);
815
816 exit:
817     if (err != CHIP_NO_ERROR)
818     {
819         ReportError(env, err, __FUNCTION__);
820         rc = false;
821     }
822     env->ExceptionClear();
823
824     return rc;
825 }
826
827 bool HandleUnsubscribeCharacteristic(BLE_CONNECTION_OBJECT connObj, const uint8_t * svcId, const uint8_t * charId)
828 {
829     StackUnlockGuard unlockGuard;
830     CHIP_ERROR err = CHIP_NO_ERROR;
831     JNIEnv * env;
832     jbyteArray svcIdObj;
833     jbyteArray charIdObj;
834     jmethodID method;
835     intptr_t tmpConnObj;
836     bool rc = false;
837
838     ChipLogProgress(Controller, "Received UnsubscribeCharacteristic");
839
840     sJVM->GetEnv((void **) &env, JNI_VERSION_1_6);
841
842     err = N2J_ByteArray(env, svcId, 16, svcIdObj);
843     SuccessOrExit(err);
844
845     err = N2J_ByteArray(env, charId, 16, charIdObj);
846     SuccessOrExit(err);
847
848     method = env->GetStaticMethodID(sAndroidChipStackCls, "onUnsubscribeCharacteristic", "(I[B[B)Z");
849     VerifyOrExit(method != NULL, err = CHIP_JNI_ERROR_METHOD_NOT_FOUND);
850
851     ChipLogProgress(Controller, "Calling Java UnsubscribeCharacteristic");
852
853     env->ExceptionClear();
854     tmpConnObj = reinterpret_cast<intptr_t>(connObj);
855     rc = (bool) env->CallStaticBooleanMethod(sAndroidChipStackCls, method, static_cast<jint>(tmpConnObj), svcIdObj, charIdObj);
856     VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN);
857
858 exit:
859     if (err != CHIP_NO_ERROR)
860     {
861         ReportError(env, err, __FUNCTION__);
862         rc = false;
863     }
864     env->ExceptionClear();
865
866     return rc;
867 }
868
869 bool HandleCloseConnection(BLE_CONNECTION_OBJECT connObj)
870 {
871     StackUnlockGuard unlockGuard;
872     CHIP_ERROR err = CHIP_NO_ERROR;
873     JNIEnv * env;
874     jmethodID method;
875     intptr_t tmpConnObj;
876     bool rc = false;
877
878     ChipLogProgress(Controller, "Received CloseConnection");
879
880     sJVM->GetEnv((void **) &env, JNI_VERSION_1_6);
881
882     method = env->GetStaticMethodID(sAndroidChipStackCls, "onCloseConnection", "(I)Z");
883     VerifyOrExit(method != NULL, err = CHIP_JNI_ERROR_METHOD_NOT_FOUND);
884
885     ChipLogProgress(Controller, "Calling Java CloseConnection");
886
887     env->ExceptionClear();
888     tmpConnObj = reinterpret_cast<intptr_t>(connObj);
889     rc         = (bool) env->CallStaticBooleanMethod(sAndroidChipStackCls, method, static_cast<jint>(tmpConnObj));
890     VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN);
891
892 exit:
893     if (err != CHIP_NO_ERROR)
894     {
895         ReportError(env, err, __FUNCTION__);
896         rc = false;
897     }
898     env->ExceptionClear();
899     return rc;
900 }
901
902 uint16_t HandleGetMTU(BLE_CONNECTION_OBJECT connObj)
903 {
904     StackUnlockGuard unlockGuard;
905     CHIP_ERROR err = CHIP_NO_ERROR;
906     JNIEnv * env;
907     jmethodID method;
908     intptr_t tmpConnObj;
909     uint16_t mtu = 0;
910
911     ChipLogProgress(Controller, "Received GetMTU");
912
913     sJVM->GetEnv((void **) &env, JNI_VERSION_1_6);
914
915     method = env->GetStaticMethodID(sAndroidChipStackCls, "onGetMTU", "(I)I");
916     VerifyOrExit(method != NULL, err = CHIP_JNI_ERROR_METHOD_NOT_FOUND);
917
918     ChipLogProgress(Controller, "Calling Java onGetMTU");
919
920     env->ExceptionClear();
921     tmpConnObj = reinterpret_cast<intptr_t>(connObj);
922     mtu        = (int16_t) env->CallStaticIntMethod(sAndroidChipStackCls, method, static_cast<jint>(tmpConnObj));
923     VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN);
924
925 exit:
926     if (err != CHIP_NO_ERROR)
927     {
928         ReportError(env, err, __FUNCTION__);
929         mtu = 0;
930     }
931     env->ExceptionClear();
932
933     return mtu;
934 }
935
936 void HandleNewConnection(void * appState, const uint16_t discriminator)
937 {
938     StackUnlockGuard unlockGuard;
939     CHIP_ERROR err = CHIP_NO_ERROR;
940     JNIEnv * env;
941     jmethodID method;
942     jclass deviceControllerCls;
943     AndroidDeviceControllerWrapper * wrapper = reinterpret_cast<AndroidDeviceControllerWrapper *>(appState);
944     jobject self                             = wrapper->JavaObjectRef();
945
946     ChipLogProgress(Controller, "Received New Connection");
947
948     sJVM->GetEnv((void **) &env, JNI_VERSION_1_6);
949
950     deviceControllerCls = env->GetObjectClass(self);
951     VerifyOrExit(deviceControllerCls != NULL, err = CHIP_JNI_ERROR_TYPE_NOT_FOUND);
952
953     method = env->GetMethodID(deviceControllerCls, "onConnectDeviceComplete", "()V");
954     VerifyOrExit(method != NULL, err = CHIP_JNI_ERROR_METHOD_NOT_FOUND);
955
956     ChipLogProgress(Controller, "Calling Java onConnectDeviceComplete");
957
958     env->ExceptionClear();
959     env->CallVoidMethod(self, method);
960     VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN);
961
962 exit:
963     if (err != CHIP_NO_ERROR)
964     {
965         ReportError(env, err, __FUNCTION__);
966     }
967     env->ExceptionClear();
968 }
969
970 void * IOThreadMain(void * arg)
971 {
972     JNIEnv * env;
973     JavaVMAttachArgs attachArgs;
974     struct timeval sleepTime;
975     fd_set readFDs, writeFDs, exceptFDs;
976     int numFDs = 0;
977
978     // Attach the IO thread to the JVM as a daemon thread.
979     // This allows the JVM to shutdown without waiting for this thread to exit.
980     attachArgs.version = JNI_VERSION_1_6;
981     attachArgs.name    = (char *) "CHIP Device Controller IO Thread";
982     attachArgs.group   = NULL;
983 #ifdef __ANDROID__
984     sJVM->AttachCurrentThreadAsDaemon(&env, (void *) &attachArgs);
985 #else
986     sJVM->AttachCurrentThreadAsDaemon((void **) &env, (void *) &attachArgs);
987 #endif
988
989     // Set to true to quit the loop. This is currently unused.
990     std::atomic<bool> quit;
991
992     ChipLogProgress(Controller, "IO thread starting");
993
994     // Lock the stack to prevent collisions with Java threads.
995     pthread_mutex_lock(&sStackLock);
996
997     // Loop until we are told to exit.
998     while (!quit.load(std::memory_order_relaxed))
999     {
1000         numFDs = 0;
1001         FD_ZERO(&readFDs);
1002         FD_ZERO(&writeFDs);
1003         FD_ZERO(&exceptFDs);
1004
1005         sleepTime.tv_sec  = 10;
1006         sleepTime.tv_usec = 0;
1007
1008         // Collect the currently active file descriptors.
1009         sSystemLayer.PrepareSelect(numFDs, &readFDs, &writeFDs, &exceptFDs, sleepTime);
1010         sInetLayer.PrepareSelect(numFDs, &readFDs, &writeFDs, &exceptFDs, sleepTime);
1011
1012         // Unlock the stack so that Java threads can make API calls.
1013         pthread_mutex_unlock(&sStackLock);
1014
1015         // Wait for for I/O or for the next timer to expire.
1016         int selectRes = select(numFDs, &readFDs, &writeFDs, &exceptFDs, &sleepTime);
1017
1018         // Break the loop if requested to shutdown.
1019         // if (sShutdown)
1020         // break;
1021
1022         // Re-lock the stack.
1023         pthread_mutex_lock(&sStackLock);
1024
1025         // Perform I/O and/or dispatch timers.
1026         sSystemLayer.HandleSelectResult(selectRes, &readFDs, &writeFDs, &exceptFDs);
1027         sInetLayer.HandleSelectResult(selectRes, &readFDs, &writeFDs, &exceptFDs);
1028     }
1029
1030     // Detach the thread from the JVM.
1031     sJVM->DetachCurrentThread();
1032
1033     return NULL;
1034 }
1035
1036 void ReportError(JNIEnv * env, CHIP_ERROR cbErr, const char * functName)
1037 {
1038     if (cbErr == CHIP_JNI_ERROR_EXCEPTION_THROWN)
1039     {
1040         ChipLogError(Controller, "Java exception thrown in %s", functName);
1041         env->ExceptionDescribe();
1042     }
1043     else
1044     {
1045         const char * errStr;
1046         switch (cbErr)
1047         {
1048         case CHIP_JNI_ERROR_TYPE_NOT_FOUND:
1049             errStr = "JNI type not found";
1050             break;
1051         case CHIP_JNI_ERROR_METHOD_NOT_FOUND:
1052             errStr = "JNI method not found";
1053             break;
1054         case CHIP_JNI_ERROR_FIELD_NOT_FOUND:
1055             errStr = "JNI field not found";
1056             break;
1057         default:
1058             errStr = ErrorStr(cbErr);
1059             break;
1060         }
1061         ChipLogError(Controller, "Error in %s : %s", functName, errStr);
1062     }
1063 }
1064
1065 void ThrowError(JNIEnv * env, CHIP_ERROR errToThrow)
1066 {
1067     CHIP_ERROR err = CHIP_NO_ERROR;
1068     jthrowable ex;
1069
1070     err = N2J_Error(env, errToThrow, ex);
1071     if (err == CHIP_NO_ERROR)
1072     {
1073         env->Throw(ex);
1074     }
1075 }
1076
1077 CHIP_ERROR GetClassRef(JNIEnv * env, const char * clsType, jclass & outCls)
1078 {
1079     CHIP_ERROR err = CHIP_NO_ERROR;
1080     jclass cls     = NULL;
1081
1082     cls = env->FindClass(clsType);
1083     VerifyOrExit(cls != NULL, err = CHIP_JNI_ERROR_TYPE_NOT_FOUND);
1084
1085     outCls = (jclass) env->NewGlobalRef((jobject) cls);
1086     VerifyOrExit(outCls != NULL, err = CHIP_JNI_ERROR_TYPE_NOT_FOUND);
1087
1088 exit:
1089     env->DeleteLocalRef(cls);
1090     return err;
1091 }
1092
1093 CHIP_ERROR N2J_ByteArray(JNIEnv * env, const uint8_t * inArray, uint32_t inArrayLen, jbyteArray & outArray)
1094 {
1095     CHIP_ERROR err = CHIP_NO_ERROR;
1096
1097     outArray = env->NewByteArray((int) inArrayLen);
1098     VerifyOrExit(outArray != NULL, err = CHIP_ERROR_NO_MEMORY);
1099
1100     env->ExceptionClear();
1101     env->SetByteArrayRegion(outArray, 0, inArrayLen, (jbyte *) inArray);
1102     VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN);
1103
1104 exit:
1105     return err;
1106 }
1107
1108 CHIP_ERROR N2J_Error(JNIEnv * env, CHIP_ERROR inErr, jthrowable & outEx)
1109 {
1110     CHIP_ERROR err      = CHIP_NO_ERROR;
1111     const char * errStr = NULL;
1112     jstring errStrObj   = NULL;
1113     jmethodID constructor;
1114
1115     constructor = env->GetMethodID(sChipDeviceControllerExceptionCls, "<init>", "(ILjava/lang/String;)V");
1116     VerifyOrExit(constructor != NULL, err = CHIP_JNI_ERROR_METHOD_NOT_FOUND);
1117
1118     switch (inErr)
1119     {
1120     case CHIP_JNI_ERROR_TYPE_NOT_FOUND:
1121         errStr = "CHIP Device Controller Error: JNI type not found";
1122         break;
1123     case CHIP_JNI_ERROR_METHOD_NOT_FOUND:
1124         errStr = "CHIP Device Controller Error: JNI method not found";
1125         break;
1126     case CHIP_JNI_ERROR_FIELD_NOT_FOUND:
1127         errStr = "CHIP Device Controller Error: JNI field not found";
1128         break;
1129     default:
1130         errStr = ErrorStr(inErr);
1131         break;
1132     }
1133     errStrObj = (errStr != NULL) ? env->NewStringUTF(errStr) : NULL;
1134
1135     env->ExceptionClear();
1136     outEx = (jthrowable) env->NewObject(sChipDeviceControllerExceptionCls, constructor, (jint) inErr, errStrObj);
1137     VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN);
1138
1139 exit:
1140     env->DeleteLocalRef(errStrObj);
1141     return err;
1142 }