blob: ff0d73133ef20a08af45bc065f61b7b9c2d7a108 [file] [log] [blame]
/*
* Copyright (c) 2022 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 "AndroidCommissioningWindowOpener.h"
#include <app-common/zap-generated/cluster-objects.h>
#include <lib/core/CHIPSafeCasts.h>
#include <lib/support/CHIPMem.h>
#include <lib/support/JniReferences.h>
#include <lib/support/JniTypeWrappers.h>
#include <protocols/secure_channel/PASESession.h>
#include <setup_payload/ManualSetupPayloadGenerator.h>
#include <setup_payload/QRCodeSetupPayloadGenerator.h>
#include <string>
using namespace chip::app::Clusters;
using namespace chip::System::Clock;
namespace chip {
namespace Controller {
AndroidCommissioningWindowOpener::AndroidCommissioningWindowOpener(DeviceController * controller, jobject jCallbackObject) :
CommissioningWindowOpener(controller), mOnOpenCommissioningWindowCallback(OnOpenCommissioningWindowResponse, this),
mOnOpenBasicCommissioningWindowCallback(OnOpenBasicCommissioningWindowResponse, this)
{
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
if (mJavaCallback.Init(jCallbackObject) != CHIP_NO_ERROR)
{
ChipLogError(Controller, "Failed to create global reference for mJavaCallback");
return;
}
jclass callbackClass = env->GetObjectClass(jCallbackObject);
mOnSuccessMethod = env->GetMethodID(callbackClass, "onSuccess", "(JLjava/lang/String;Ljava/lang/String;)V");
if (mOnSuccessMethod == nullptr)
{
ChipLogError(Controller, "Failed to access callback 'onSuccess' method");
env->ExceptionClear();
}
mOnErrorMethod = env->GetMethodID(callbackClass, "onError", "(IJ)V");
if (mOnErrorMethod == nullptr)
{
ChipLogError(Controller, "Failed to access callback 'onError' method");
env->ExceptionClear();
}
}
CHIP_ERROR AndroidCommissioningWindowOpener::OpenBasicCommissioningWindow(DeviceController * controller, NodeId deviceId,
Seconds16 timeout, jobject jcallback)
{
// Not using Platform::New because we want to keep our constructor private.
auto * opener = new AndroidCommissioningWindowOpener(controller, jcallback);
if (opener == nullptr)
{
return CHIP_ERROR_NO_MEMORY;
}
CHIP_ERROR err = opener->CommissioningWindowOpener::OpenBasicCommissioningWindow(
deviceId, timeout, &opener->mOnOpenBasicCommissioningWindowCallback);
if (err != CHIP_NO_ERROR)
{
delete opener;
}
// Else will clean up when the callback is called.
return err;
}
CHIP_ERROR AndroidCommissioningWindowOpener::OpenCommissioningWindow(DeviceController * controller, NodeId deviceId,
Seconds16 timeout, uint32_t iteration, uint16_t discriminator,
Optional<uint32_t> setupPIN, Optional<ByteSpan> salt,
jobject jcallback, SetupPayload & payload,
bool readVIDPIDAttributes)
{
// Not using Platform::New because we want to keep our constructor private.
auto * opener = new AndroidCommissioningWindowOpener(controller, jcallback);
if (opener == nullptr)
{
return CHIP_ERROR_NO_MEMORY;
}
CHIP_ERROR err = opener->CommissioningWindowOpener::OpenCommissioningWindow(
deviceId, timeout, iteration, discriminator, setupPIN, salt, &opener->mOnOpenCommissioningWindowCallback, payload,
readVIDPIDAttributes);
if (err != CHIP_NO_ERROR)
{
delete opener;
}
// Else will clean up when the callback is called.
return err;
}
void AndroidCommissioningWindowOpener::OnOpenCommissioningWindowResponse(void * context, NodeId deviceId, CHIP_ERROR status,
chip::SetupPayload payload)
{
auto * self = static_cast<AndroidCommissioningWindowOpener *>(context);
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
VerifyOrReturn(env != nullptr, ChipLogError(Controller, "Could not get JNIEnv for current thread"));
JniLocalReferenceScope scope(env);
VerifyOrExit(self->mJavaCallback.HasValidObjectRef(), ChipLogError(Controller, "mJavaCallback is not allocated."));
if (status == CHIP_NO_ERROR)
{
std::string QRCode;
std::string manualPairingCode;
SuccessOrExit(status = ManualSetupPayloadGenerator(payload).payloadDecimalStringRepresentation(manualPairingCode));
SuccessOrExit(status = QRCodeSetupPayloadGenerator(payload).payloadBase38Representation(QRCode));
if (self->mOnSuccessMethod != nullptr)
{
UtfString jManualPairingCode(env, manualPairingCode.c_str());
UtfString jQRCode(env, QRCode.c_str());
env->CallVoidMethod(self->mJavaCallback.ObjectRef(), self->mOnSuccessMethod, static_cast<jlong>(deviceId),
jManualPairingCode.jniValue(), jQRCode.jniValue());
}
}
else
{
if (self->mOnErrorMethod != nullptr)
{
env->CallVoidMethod(self->mJavaCallback.ObjectRef(), self->mOnErrorMethod, static_cast<jint>(status.GetValue()),
static_cast<jlong>(deviceId));
}
}
exit:
delete self;
}
void AndroidCommissioningWindowOpener::OnOpenBasicCommissioningWindowResponse(void * context, NodeId deviceId, CHIP_ERROR status)
{
auto * self = static_cast<AndroidCommissioningWindowOpener *>(context);
if (self->mJavaCallback.HasValidObjectRef())
{
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
VerifyOrReturn(env != nullptr, ChipLogError(Controller, "Could not get JNIEnv for current thread"));
JniLocalReferenceScope scope(env);
if (status == CHIP_NO_ERROR)
{
if (self->mOnSuccessMethod != nullptr)
{
UtfString jManualPairingCode(env, "");
UtfString jQRCode(env, "");
env->CallVoidMethod(self->mJavaCallback.ObjectRef(), self->mOnSuccessMethod, static_cast<jlong>(deviceId),
jManualPairingCode.jniValue(), jQRCode.jniValue());
}
}
else
{
if (self->mOnErrorMethod != nullptr)
{
env->CallVoidMethod(self->mJavaCallback.ObjectRef(), self->mOnErrorMethod, static_cast<jint>(status.GetValue()),
static_cast<jlong>(deviceId));
}
}
}
delete self;
}
} // namespace Controller
} // namespace chip