Skip to content

Commit

Permalink
KVS implementation for Android (#5738)
Browse files Browse the repository at this point in the history
* Starting to add an android version of a key store manager

* Add KVS include path via platform build config

* Add Key value store manager java class and initialization

* Added a key-value-store implementation

* Restyle fixes

* Remove file that was not intended to be added (idea compiler.xml)
  • Loading branch information
andy31415 authored Apr 9, 2021
1 parent 6cbb522 commit 1274bcf
Show file tree
Hide file tree
Showing 10 changed files with 454 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import com.google.chip.chiptool.setuppayloadscanner.BarcodeFragment
import com.google.chip.chiptool.setuppayloadscanner.CHIPDeviceDetailsFragment
import com.google.chip.chiptool.setuppayloadscanner.CHIPDeviceInfo
import com.google.chip.chiptool.setuppayloadscanner.QrCodeInfo
import chip.devicecontroller.KeyValueStoreManager
import chip.devicecontroller.PersistentStorage
import chip.setuppayload.SetupPayload
import chip.setuppayload.SetupPayloadParser
Expand All @@ -54,6 +55,7 @@ class CHIPToolActivity :
setContentView(R.layout.top_activity)

PersistentStorage.initialize(this);
KeyValueStoreManager.initialize(this);

if (savedInstanceState == null) {
val fragment = SelectActionFragment.newInstance()
Expand Down
178 changes: 178 additions & 0 deletions src/controller/java/AndroidKeyValueStoreManagerImpl.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
/*
*
* Copyright (c) 2021 Project CHIP Authors
*
* 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 <platform/KeyValueStoreManager.h>

#include <algorithm>
#include <string.h>

#include "CHIPJNIError.h"
#include "JniReferences.h"
#include "JniTypeWrappers.h"

#include <support/Base64.h>
#include <support/CodeUtils.h>
#include <support/logging/CHIPLogging.h>

namespace chip {
namespace DeviceLayer {
namespace PersistedStorage {
namespace {

constexpr size_t kMaxKvsValueBytes = 1024;
constexpr size_t kMaxKvsValueEncodedChars = BASE64_ENCODED_LEN(kMaxKvsValueBytes) + 1;

} // namespace

KeyValueStoreManagerImpl KeyValueStoreManagerImpl::sInstance;

CHIP_ERROR KeyValueStoreManagerImpl::_Get(const char * key, void * value, size_t value_size, size_t * read_bytes_size,
size_t offset)
{
ReturnErrorCodeIf(mEnv == nullptr, CHIP_ERROR_INCORRECT_STATE);
ReturnErrorCodeIf(mKeyValueStoreManagerClass == nullptr, CHIP_ERROR_INCORRECT_STATE);
ReturnErrorCodeIf(mGetMethod == nullptr, CHIP_ERROR_INCORRECT_STATE);
ReturnErrorCodeIf(offset != 0, CHIP_ERROR_INVALID_ARGUMENT);

UtfString javaKey(mEnv, key);

jobject javaValue = mEnv->CallStaticObjectMethod(mKeyValueStoreManagerClass, mGetMethod, javaKey.jniValue());
if (mEnv->ExceptionCheck())
{
ChipLogError(DeviceLayer, "Java exception in KVS::Get");
mEnv->ExceptionDescribe();
return CHIP_JNI_ERROR_EXCEPTION_THROWN;
}

if (javaValue == nullptr)
{
return CHIP_ERROR_KEY_NOT_FOUND;
}

JniUtfString utfValue(mEnv, (jstring) javaValue);
if (strlen(utfValue.c_str()) > kMaxKvsValueEncodedChars)
{
ChipLogError(DeviceLayer, "Unexpected large value received from KVS");
return CHIP_ERROR_NO_MEMORY;
}

uint8_t buffer[kMaxKvsValueBytes];
uint16_t decodedLength = chip::Base64Decode(utfValue.c_str(), strlen(utfValue.c_str()), buffer);
if (decodedLength == UINT16_MAX)
{
ChipLogError(DeviceLayer, "KVS base64 decoding failed");
return CHIP_ERROR_INTEGRITY_CHECK_FAILED;
}

if (read_bytes_size != nullptr)
{
*read_bytes_size = decodedLength;
}

if (value != nullptr)
{
memcpy(value, buffer, std::min<size_t>(value_size, decodedLength));
}

return CHIP_NO_ERROR;
}

CHIP_ERROR KeyValueStoreManagerImpl::_Delete(const char * key)
{
ReturnErrorCodeIf(mEnv == nullptr, CHIP_ERROR_INCORRECT_STATE);
ReturnErrorCodeIf(mKeyValueStoreManagerClass == nullptr, CHIP_ERROR_INCORRECT_STATE);
ReturnErrorCodeIf(mDeleteMethod == nullptr, CHIP_ERROR_INCORRECT_STATE);

UtfString javaKey(mEnv, key);

mEnv->CallStaticVoidMethod(mKeyValueStoreManagerClass, mDeleteMethod, javaKey.jniValue());

if (mEnv->ExceptionCheck())
{
ChipLogError(DeviceLayer, "Java exception in KVS::Delete");
mEnv->ExceptionDescribe();
mEnv->ExceptionClear();
return CHIP_JNI_ERROR_EXCEPTION_THROWN;
}

return CHIP_NO_ERROR;
}

CHIP_ERROR KeyValueStoreManagerImpl::_Put(const char * key, const void * value, size_t value_size)
{
ReturnErrorCodeIf(mEnv == nullptr, CHIP_ERROR_INCORRECT_STATE);
ReturnErrorCodeIf(mKeyValueStoreManagerClass == nullptr, CHIP_ERROR_INCORRECT_STATE);
ReturnErrorCodeIf(mSetMethod == nullptr, CHIP_ERROR_INCORRECT_STATE);
ReturnErrorCodeIf(value_size > kMaxKvsValueBytes, CHIP_ERROR_INVALID_ARGUMENT);

char base64Buffer[kMaxKvsValueEncodedChars];

size_t length = chip::Base64Encode(static_cast<const uint8_t *>(value), value_size, base64Buffer);

base64Buffer[length] = 0;

UtfString utfKey(mEnv, key);
UtfString utfBase64Value(mEnv, base64Buffer);

mEnv->CallStaticVoidMethod(mKeyValueStoreManagerClass, mSetMethod, utfKey.jniValue(), utfBase64Value.jniValue());

if (mEnv->ExceptionCheck())
{
ChipLogError(DeviceLayer, "Java exception in KVS::Delete");
mEnv->ExceptionDescribe();
mEnv->ExceptionClear();
return CHIP_JNI_ERROR_EXCEPTION_THROWN;
}

return CHIP_NO_ERROR;
}

void KeyValueStoreManagerImpl::InitializeMethodForward(JNIEnv * env)
{
mEnv = env;

CHIP_ERROR err = GetClassRef(env, "chip/devicecontroller/KeyValueStoreManager", mKeyValueStoreManagerClass);
if (err != CHIP_NO_ERROR)
{
ChipLogError(DeviceLayer, "Failed to get reference to KeyValueStoreManager");
return;
}

mGetMethod = env->GetStaticMethodID(mKeyValueStoreManagerClass, "get", "(Ljava/lang/String;)Ljava/lang/String;");
if (mGetMethod == nullptr)
{
ChipLogError(DeviceLayer, "Failed to access KVS 'get' method");
env->ExceptionClear();
}

mSetMethod = env->GetStaticMethodID(mKeyValueStoreManagerClass, "set", "(Ljava/lang/String;Ljava/lang/String;)V");
if (mSetMethod == nullptr)
{
ChipLogError(DeviceLayer, "Failed to access KVS 'set' method");
env->ExceptionClear();
}

mDeleteMethod = env->GetStaticMethodID(mKeyValueStoreManagerClass, "delete", "(Ljava/lang/String;)V");
if (mDeleteMethod == nullptr)
{
ChipLogError(DeviceLayer, "Failed to access KVS 'delete' method");
env->ExceptionClear();
}
}

} // namespace PersistedStorage
} // namespace DeviceLayer
} // namespace chip
75 changes: 75 additions & 0 deletions src/controller/java/AndroidKeyValueStoreManagerImpl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
*
* Copyright (c) 2021 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.
*/

#pragma once

#include <jni.h>

namespace chip {
namespace DeviceLayer {
namespace PersistedStorage {

class KeyValueStoreManagerImpl : public KeyValueStoreManager
{
public:
CHIP_ERROR _Get(const char * key, void * value, size_t value_size, size_t * read_bytes_size = nullptr, size_t offset = 0);
CHIP_ERROR _Delete(const char * key);
CHIP_ERROR _Put(const char * key, const void * value, size_t value_size);

void InitializeMethodForward(JNIEnv * env);

private:
JNIEnv * mEnv = nullptr;
jclass mKeyValueStoreManagerClass = nullptr;

jmethodID mSetMethod = nullptr;
jmethodID mGetMethod = nullptr;
jmethodID mDeleteMethod = nullptr;

// ===== Members for internal use by the following friends.
friend KeyValueStoreManager & KeyValueStoreMgr();
friend KeyValueStoreManagerImpl & KeyValueStoreMgrImpl();

static KeyValueStoreManagerImpl sInstance;
};

/**
* Returns the public interface of the KeyValueStoreManager singleton object.
*
* Chip applications should use this to access features of the KeyValueStoreManager object
* that are common to all platforms.
*/
inline KeyValueStoreManager & KeyValueStoreMgr()
{
return KeyValueStoreManagerImpl::sInstance;
}

/**
* Returns the platform-specific implementation of the KeyValueStoreManager singleton object.
*
* Chip applications can use this to gain access to features of the KeyValueStoreManager
* that are specific to the ESP32 platform.
*/
inline KeyValueStoreManagerImpl & KeyValueStoreMgrImpl()
{
return KeyValueStoreManagerImpl::sInstance;
}

} // namespace PersistedStorage
} // namespace DeviceLayer
} // namespace chip
6 changes: 6 additions & 0 deletions src/controller/java/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,13 @@ shared_library("jni") {
"AndroidBlePlatformDelegate.h",
"AndroidDeviceControllerWrapper.cpp",
"AndroidDeviceControllerWrapper.h",
"AndroidKeyValueStoreManagerImpl.cpp",
"AndroidKeyValueStoreManagerImpl.h",
"CHIPDeviceController-JNI.cpp",
"CHIPJNIError.h",
"JniReferences.cpp",
"JniReferences.h",
"JniTypeWrappers.h",
]

deps = [ "//src/lib" ]
Expand All @@ -53,6 +58,7 @@ android_library("java") {
"src/chip/devicecontroller/ChipCommandType.java",
"src/chip/devicecontroller/ChipDeviceController.java",
"src/chip/devicecontroller/ChipDeviceControllerException.java",
"src/chip/devicecontroller/KeyValueStoreManager.java",
"src/chip/devicecontroller/PersistentStorage.java",
]

Expand Down
54 changes: 5 additions & 49 deletions src/controller/java/CHIPDeviceController-JNI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,15 @@
#include "AndroidBlePlatformDelegate.h"
#include "AndroidDeviceControllerWrapper.h"
#include "CHIPJNIError.h"
#include "JniReferences.h"
#include "JniTypeWrappers.h"

#include <app/chip-zcl-zpro-codec.h>
#include <atomic>
#include <ble/BleUUID.h>
#include <controller/CHIPDeviceController.h>
#include <jni.h>
#include <platform/KeyValueStoreManager.h>
#include <pthread.h>
#include <support/CHIPMem.h>
#include <support/CodeUtils.h>
Expand Down Expand Up @@ -69,7 +72,6 @@ static void HandleNewConnection(void * appState, const uint16_t discriminator);
static void ThrowError(JNIEnv * env, CHIP_ERROR errToThrow);
static void ReportError(JNIEnv * env, CHIP_ERROR cbErr, const char * cbName);
static void * IOThreadMain(void * arg);
static CHIP_ERROR GetClassRef(JNIEnv * env, const char * clsType, jclass & outCls);
static CHIP_ERROR N2J_ByteArray(JNIEnv * env, const uint8_t * inArray, uint32_t inArrayLen, jbyteArray & outArray);
static CHIP_ERROR N2J_Error(JNIEnv * env, CHIP_ERROR inErr, jthrowable & outEx);

Expand Down Expand Up @@ -112,38 +114,6 @@ struct StackUnlockGuard
~StackUnlockGuard() { pthread_mutex_lock(&sStackLock); }
};

class JniUtfString
{
public:
JniUtfString(JNIEnv * env, jstring string) : mEnv(env), mString(string) { mChars = env->GetStringUTFChars(string, 0); }
~JniUtfString() { mEnv->ReleaseStringUTFChars(mString, mChars); }

const char * c_str() const { return mChars; }

private:
JNIEnv * mEnv;
jstring mString;
const char * mChars;
};

class JniByteArray
{
public:
JniByteArray(JNIEnv * env, jbyteArray array) :
mEnv(env), mArray(array), mData(env->GetByteArrayElements(array, nullptr)), mDataLength(env->GetArrayLength(array))
{}
~JniByteArray() { mEnv->ReleaseByteArrayElements(mArray, mData, 0); }

const jbyte * data() const { return mData; }
jsize size() const { return mDataLength; }

private:
JNIEnv * mEnv;
jbyteArray mArray;
jbyte * mData;
jsize mDataLength;
};

} // namespace

// NOTE: Remote device ID is in sync with the echo server device id
Expand All @@ -168,6 +138,8 @@ jint JNI_OnLoad(JavaVM * jvm, void * reserved)
// Get a JNI environment object.
sJVM->GetEnv((void **) &env, JNI_VERSION_1_6);

chip::DeviceLayer::PersistedStorage::KeyValueStoreMgrImpl().InitializeMethodForward(env);

ChipLogProgress(Controller, "Loading Java class references.");

// Get various class references need by the API.
Expand Down Expand Up @@ -1074,22 +1046,6 @@ void ThrowError(JNIEnv * env, CHIP_ERROR errToThrow)
}
}

CHIP_ERROR GetClassRef(JNIEnv * env, const char * clsType, jclass & outCls)
{
CHIP_ERROR err = CHIP_NO_ERROR;
jclass cls = NULL;

cls = env->FindClass(clsType);
VerifyOrExit(cls != NULL, err = CHIP_JNI_ERROR_TYPE_NOT_FOUND);

outCls = (jclass) env->NewGlobalRef((jobject) cls);
VerifyOrExit(outCls != NULL, err = CHIP_JNI_ERROR_TYPE_NOT_FOUND);

exit:
env->DeleteLocalRef(cls);
return err;
}

CHIP_ERROR N2J_ByteArray(JNIEnv * env, const uint8_t * inArray, uint32_t inArrayLen, jbyteArray & outArray)
{
CHIP_ERROR err = CHIP_NO_ERROR;
Expand Down
Loading

0 comments on commit 1274bcf

Please sign in to comment.