blob: d37f1b727e7ed3d61c81afbce00832ba3a65098c [file] [log] [blame]
/*
*
* Copyright (c) 2024 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 "AndroidLogDownloadFromNode.h"
#include <app-common/zap-generated/cluster-enums.h>
#include <app-common/zap-generated/cluster-objects.h>
#include <controller/CHIPDeviceControllerFactory.h>
#include <protocols/bdx/BdxUri.h>
using namespace chip::app::Clusters;
namespace chip {
namespace Controller {
// Max Length is below 8
CharSpan toIntentCharSpan(DiagnosticLogs::IntentEnum intent)
{
switch (intent)
{
case DiagnosticLogs::IntentEnum::kEndUserSupport:
return CharSpan::fromCharString("EndUser");
case DiagnosticLogs::IntentEnum::kNetworkDiag:
return CharSpan::fromCharString("Network");
case DiagnosticLogs::IntentEnum::kCrashLogs:
return CharSpan::fromCharString("Crash");
default:
return CharSpan();
}
}
AndroidLogDownloadFromNode::AndroidLogDownloadFromNode(chip::Controller::DeviceController * controller, NodeId remoteNodeId,
DiagnosticLogs::IntentEnum intent, uint16_t timeout,
jobject jCallbackObject) :
mController(controller),
mOnDeviceConnectedCallback(&OnDeviceConnectedFn, this), mOnDeviceConnectionFailureCallback(&OnDeviceConnectionFailureFn, this),
mOnBdxTransferCallback(&OnBdxTransferCallback, this), mOnBdxTransferSuccessCallback(&OnBdxTransferSuccessCallback, this),
mOnBdxTransferFailureCallback(&OnBdxTransferFailureCallback, this)
{
mRemoteNodeId = remoteNodeId;
mIntent = intent;
mTimeout = timeout;
if (mJavaCallback.Init(jCallbackObject) != CHIP_NO_ERROR)
{
ChipLogError(Controller, "Fail to init mJavaObjectRef");
return;
}
}
CHIP_ERROR AndroidLogDownloadFromNode::LogDownloadFromNode(DeviceController * controller, NodeId remoteNodeId,
DiagnosticLogs::IntentEnum intent, uint16_t timeout, jobject jcallback)
{
VerifyOrReturnValue(controller != nullptr && jcallback != nullptr && remoteNodeId != kUndefinedNodeId,
CHIP_ERROR_INVALID_ARGUMENT);
auto * logDownload = new AndroidLogDownloadFromNode(controller, remoteNodeId, intent, timeout, jcallback);
VerifyOrReturnValue(logDownload != nullptr, CHIP_ERROR_NO_MEMORY);
CHIP_ERROR err = logDownload->GetConnectedDevice();
if (err != CHIP_NO_ERROR)
{
delete logDownload;
logDownload = nullptr;
}
// Else will clean up when the callback is called.
return err;
}
CHIP_ERROR AndroidLogDownloadFromNode::GetConnectedDevice()
{
return mController->GetConnectedDevice(mRemoteNodeId, &mOnDeviceConnectedCallback, &mOnDeviceConnectionFailureCallback);
}
CHIP_ERROR AndroidLogDownloadFromNode::SendRetrieveLogsRequest(Messaging::ExchangeManager & exchangeMgr,
const SessionHandle & sessionHandle)
{
DiagnosticLogs::Commands::RetrieveLogsRequest::Type request;
request.intent = mIntent;
request.requestedProtocol = DiagnosticLogs::TransferProtocolEnum::kBdx;
CHIP_ERROR err = chip::bdx::MakeURI(mRemoteNodeId, toIntentCharSpan(mIntent), mFileDesignator);
if (err != CHIP_NO_ERROR)
{
ChipLogError(Controller, "Make BDX URI failure : %" CHIP_ERROR_FORMAT, err.Format());
FinishLogDownloadFromNode(err);
}
mBdxReceiver =
new BdxDiagnosticLogsReceiver(&mOnBdxTransferCallback, &mOnBdxTransferSuccessCallback, &mOnBdxTransferFailureCallback,
mController->GetFabricIndex(), mRemoteNodeId, mFileDesignator);
VerifyOrReturnValue(mBdxReceiver != nullptr, CHIP_ERROR_NO_MEMORY);
auto systemState = DeviceControllerFactory::GetInstance().GetSystemState();
systemState->BDXTransferServer()->SetDelegate(mBdxReceiver);
if (mTimeout > 0)
{
mBdxReceiver->StartBDXTransferTimeout(mTimeout);
}
request.transferFileDesignator = MakeOptional(mFileDesignator);
ClusterBase cluster(exchangeMgr, sessionHandle, 0);
return cluster.InvokeCommand(request, this, OnResponseRetrieveLogs, OnCommandFailure);
}
void AndroidLogDownloadFromNode::OnDeviceConnectedFn(void * context, Messaging::ExchangeManager & exchangeMgr,
const SessionHandle & sessionHandle)
{
CHIP_ERROR err = CHIP_NO_ERROR;
auto * self = static_cast<AndroidLogDownloadFromNode *>(context);
VerifyOrReturn(self != nullptr, ChipLogProgress(Controller, "Device connected callback with null context. Ignoring"));
err = self->SendRetrieveLogsRequest(exchangeMgr, sessionHandle);
if (err != CHIP_NO_ERROR)
{
ChipLogError(Controller, "Log Download failure : %" CHIP_ERROR_FORMAT, err.Format());
self->FinishLogDownloadFromNode(err);
}
}
void AndroidLogDownloadFromNode::OnDeviceConnectionFailureFn(void * context, const ScopedNodeId & peerId, CHIP_ERROR err)
{
ChipLogProgress(Controller, "OnDeviceConnectionFailureFn: %" CHIP_ERROR_FORMAT, err.Format());
auto * self = static_cast<AndroidLogDownloadFromNode *>(context);
VerifyOrReturn(self != nullptr, ChipLogProgress(Controller, "Device connected failure callback with null context. Ignoring"));
self->FinishLogDownloadFromNode(err);
}
void AndroidLogDownloadFromNode::OnResponseRetrieveLogs(void * context,
const DiagnosticLogs::Commands::RetrieveLogsResponse::DecodableType & data)
{
auto * self = static_cast<AndroidLogDownloadFromNode *>(context);
VerifyOrReturn(self != nullptr,
ChipLogProgress(Controller, "Success Read Current Fabric index callback with null context. Ignoring"));
using namespace chip::app::Clusters::DiagnosticLogs;
if (data.status == StatusEnum::kSuccess)
{
ChipLogProgress(Controller, "Success. Will receive log from BDX protocol.")
}
else if (data.status == StatusEnum::kExhausted)
{
CHIP_ERROR err = CHIP_NO_ERROR;
self->OnTransferCallback(self->mController->GetFabricIndex(), self->mRemoteNodeId, data.logContent, &err);
self->FinishLogDownloadFromNode(err);
}
else if (data.status == StatusEnum::kNoLogs)
{
CHIP_ERROR err = CHIP_NO_ERROR;
self->OnTransferCallback(self->mController->GetFabricIndex(), self->mRemoteNodeId, ByteSpan(), &err);
self->FinishLogDownloadFromNode(err);
}
else if (data.status == StatusEnum::kBusy)
{
self->FinishLogDownloadFromNode(CHIP_ERROR_BUSY);
}
else if (data.status == StatusEnum::kDenied)
{
self->FinishLogDownloadFromNode(CHIP_ERROR_ACCESS_DENIED);
}
else
{
self->FinishLogDownloadFromNode(CHIP_ERROR_INVALID_DATA_LIST);
}
}
void AndroidLogDownloadFromNode::OnCommandFailure(void * context, CHIP_ERROR err)
{
ChipLogProgress(Controller, "OnCommandFailure %" CHIP_ERROR_FORMAT, err.Format());
auto * self = static_cast<AndroidLogDownloadFromNode *>(context);
VerifyOrReturn(self != nullptr, ChipLogProgress(Controller, "Send command failure callback with null context. Ignoring"));
self->FinishLogDownloadFromNode(err);
}
void AndroidLogDownloadFromNode::FinishLogDownloadFromNode(CHIP_ERROR err)
{
CHIP_ERROR jniErr = CHIP_NO_ERROR;
if (mBdxReceiver != nullptr)
{
if (mTimeout > 0)
{
mBdxReceiver->CancelBDXTransferTimeout();
}
delete mBdxReceiver;
mBdxReceiver = nullptr;
}
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
JniLocalReferenceScope scope(env);
if (err == CHIP_NO_ERROR)
{
ChipLogProgress(Controller, "Log Download succeeded.");
jmethodID onSuccessMethod;
// Java method signature : boolean onSuccess(int fabricIndex, long nodeId)
jniErr = JniReferences::GetInstance().FindMethod(env, mJavaCallback.ObjectRef(), "onSuccess", "(IJ)V", &onSuccessMethod);
VerifyOrReturn(jniErr == CHIP_NO_ERROR, ChipLogError(Controller, "Could not find onSuccess method"));
env->CallVoidMethod(mJavaCallback.ObjectRef(), onSuccessMethod, static_cast<jint>(mController->GetFabricIndex()),
static_cast<jlong>(mRemoteNodeId));
return;
}
ChipLogError(Controller, "Log Download Failed : %" CHIP_ERROR_FORMAT, err.Format());
jmethodID onErrorMethod;
// Java method signature : void onError(int fabricIndex, long nodeId, long errorCode)
jniErr = JniReferences::GetInstance().FindMethod(env, mJavaCallback.ObjectRef(), "onError", "(IJJ)V", &onErrorMethod);
VerifyOrReturn(jniErr == CHIP_NO_ERROR, ChipLogError(Controller, "Could not find onError method"));
env->CallVoidMethod(mJavaCallback.ObjectRef(), onErrorMethod, static_cast<jint>(mController->GetFabricIndex()),
static_cast<jlong>(mRemoteNodeId), static_cast<jlong>(err.AsInteger()));
}
void AndroidLogDownloadFromNode::OnBdxTransferCallback(void * context, FabricIndex fabricIndex, NodeId remoteNodeId,
const chip::ByteSpan & data, CHIP_ERROR * errInfoOnFailure)
{
auto * self = static_cast<AndroidLogDownloadFromNode *>(context);
VerifyOrReturn(self != nullptr, ChipLogProgress(Controller, "Send command failure callback with null context. Ignoring"));
self->OnTransferCallback(fabricIndex, remoteNodeId, data, errInfoOnFailure);
}
void AndroidLogDownloadFromNode::OnTransferCallback(FabricIndex fabricIndex, NodeId remoteNodeId, const chip::ByteSpan & data,
CHIP_ERROR * errInfoOnFailure)
{
VerifyOrReturn(mJavaCallback.HasValidObjectRef(), ChipLogError(Controller, "mJavaCallback is invalid"));
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
VerifyOrReturn(env != nullptr, ChipLogError(Controller, "Could not get JNIEnv for current thread"));
JniLocalReferenceScope scope(env);
jmethodID onTransferDataMethod;
// Java method signature : boolean onTransferData(int fabricIndex, long nodeId, byte[] data)
*errInfoOnFailure =
JniReferences::GetInstance().FindMethod(env, mJavaCallback.ObjectRef(), "onTransferData", "(IJ[B)Z", &onTransferDataMethod);
VerifyOrReturn(*errInfoOnFailure == CHIP_NO_ERROR, ChipLogError(Controller, "Could not find onTransferData method"));
chip::ByteArray dataByteArray(env, data);
jboolean ret = env->CallBooleanMethod(mJavaCallback.ObjectRef(), onTransferDataMethod, static_cast<jint>(fabricIndex),
static_cast<jlong>(remoteNodeId), dataByteArray.jniValue());
if (ret != JNI_TRUE)
{
ChipLogError(Controller, "Transfer will be rejected.") * errInfoOnFailure = CHIP_ERROR_INTERNAL;
}
}
void AndroidLogDownloadFromNode::OnBdxTransferSuccessCallback(void * context, FabricIndex fabricIndex, NodeId remoteNodeId)
{
ChipLogProgress(Controller, "OnBdxTransferSuccessCallback");
auto * self = static_cast<AndroidLogDownloadFromNode *>(context);
VerifyOrReturn(self != nullptr, ChipLogProgress(Controller, "Send command failure callback with null context. Ignoring"));
self->FinishLogDownloadFromNode(CHIP_NO_ERROR);
}
void AndroidLogDownloadFromNode::OnBdxTransferFailureCallback(void * context, FabricIndex fabricIndex, NodeId remoteNodeId,
CHIP_ERROR status)
{
ChipLogProgress(Controller, "OnBdxTransferFailureCallback: %" CHIP_ERROR_FORMAT, status.Format());
auto * self = static_cast<AndroidLogDownloadFromNode *>(context);
VerifyOrReturn(self != nullptr, ChipLogProgress(Controller, "Send command failure callback with null context. Ignoring"));
self->FinishLogDownloadFromNode(status);
}
} // namespace Controller
} // namespace chip