Skip to content

src/local_secure_storage/impl/Android.cpp

Namespaces

Name
sgns

Functions

Name
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM * vm, void * _reserved)

Defines

Name
LOG_TAG
LOGI(...)
LOGE(...)

Functions Documentation

function JNI_OnLoad

JNIEXPORT jint JNICALL JNI_OnLoad(
    JavaVM * vm,
    void * _reserved
)

Macros Documentation

define LOG_TAG

#define LOG_TAG "AndroidSecureStorage"

define LOGI

#define LOGI(
    ...
)
__android_log_print( ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__ )

define LOGE

#define LOGE(
    ...
)
__android_log_print( ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__ )

Source code

#include "Android.hpp"

#include <cstddef>
#include <stdexcept>

#include <android/log.h>
#include <rapidjson/stringbuffer.h>
#include <rapidjson/writer.h>

#include "outcome/outcome.hpp"

#define LOG_TAG "AndroidSecureStorage"
#define LOGI( ... ) __android_log_print( ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__ )
#define LOGE( ... ) __android_log_print( ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__ )

namespace
{
    JavaVM *g_jvm = nullptr;
}

extern "C" JNIEXPORT jint JNICALL JNI_OnLoad( JavaVM *vm, void *_reserved )
{
    LOGI( "SuperGenius SDK initializing" );

    g_jvm = vm;

    JNIEnv *env = nullptr;
    if ( vm->GetEnv( reinterpret_cast<void **>( &env ), JNI_VERSION_1_6 ) != JNI_OK )
    {
        LOGE( "Failed to get JNI environment" );
        return JNI_ERR;
    }

    LOGI( "SuperGenius SDK initialized successfully" );
    return JNI_VERSION_1_6;
}

namespace rj = rapidjson;

namespace sgns
{
    AndroidSecureStorage::AndroidSecureStorage( std::string identifier, JavaVM *jvm ): jvm_(jvm) {
        if ( jvm_ == nullptr )
        {
            if ( g_jvm == nullptr )
            {
                throw std::runtime_error( "Could not find JVM pointer" );
            }
            jvm_ = g_jvm;
        }

        JNIEnv *env = GetJNIEnv();
        if ( env == nullptr )
        {
            throw std::runtime_error( "Failed to get JNI environment" );
        }

        jclass local_class = env->FindClass( "ai/gnus/sdk/KeyStoreHelper" );
        if ( local_class == nullptr )
        {
            env->ExceptionDescribe();
            env->ExceptionClear();
            throw std::runtime_error( "Failed to find KeyStoreHelper class" );
        }

        key_store_helper_class_ = static_cast<jclass>( env->NewGlobalRef( local_class ) );
        env->DeleteLocalRef( local_class );

        // Get STATIC method IDs (note the "Static" in the function names)
        load_method_   = env->GetStaticMethodID( key_store_helper_class_, "load", "()Ljava/lang/String;" );
        save_method_   = env->GetStaticMethodID( key_store_helper_class_, "save", "(Ljava/lang/String;)Z" );
        delete_method_ = env->GetStaticMethodID( key_store_helper_class_, "delete", "(Ljava/lang/String;)Z" );

        if ( load_method_ == nullptr || save_method_ == nullptr || delete_method_ == nullptr )
        {
            env->ExceptionDescribe();
            env->ExceptionClear();
            throw std::runtime_error( "Failed to find KeyStoreHelper methods" );
        }

        LOGI( "AndroidSecureStorage initialized successfully" );
    }

    AndroidSecureStorage::~AndroidSecureStorage()
    {
        auto env = GetJNIEnv();

        if ( env != nullptr )
        {
            if ( key_store_helper_class_ != nullptr )
            {
                env->DeleteGlobalRef( key_store_helper_class_ );
            }
        }
    }

    outcome::result<rapidjson::Document> AndroidSecureStorage::LoadJSON() const
    {
        auto *env = GetJNIEnv();
        if ( env == nullptr )
        {
            return outcome::failure( std::errc::not_connected );
        }

        auto result = static_cast<jstring>( env->CallStaticObjectMethod( key_store_helper_class_, load_method_ ) );

        if ( env->ExceptionCheck() != 0U )
        {
            LOGE( "Failed to call load method from KeyStore" );
            env->ExceptionDescribe();
            env->ExceptionClear();
            return outcome::failure( std::errc::connection_aborted );
        }

        if ( result == nullptr )
        {
            return rj::Document( rj::Type::kObjectType );
        }

        const char *data = env->GetStringUTFChars( result, nullptr );
        if ( data == nullptr )
        {
            LOGE( "Failed to get message from KeyStore's response" );
            env->DeleteLocalRef( result );
            return outcome::failure( std::errc::bad_message );
        }

        rj::Document d;
        d.Parse( data );

        env->ReleaseStringUTFChars( result, data );
        env->DeleteLocalRef( result );

        if ( d.HasParseError() || ( !d.IsObject() && !d.Empty() ) )
        {
            LOGE( "Failed to parse JSON document" );
            return outcome::failure( std::errc::bad_message );
        }

        return d;
    }

    outcome::result<void> AndroidSecureStorage::SaveJSON( rapidjson::Document document )
    {
        JNIEnv *env = GetJNIEnv();
        if ( env == nullptr )
        {
            return outcome::failure( std::errc::not_connected );
        }

        rj::StringBuffer             buffer;
        rj::Writer<rj::StringBuffer> writer( buffer );
        document.Accept( writer );

        jstring jdata = env->NewStringUTF( buffer.GetString() );
        if ( jdata == nullptr )
        {
            LOGE( "Failed to create jstring to store JSON" );
            return outcome::failure( std::errc::bad_message );
        }

        // Call STATIC method instead of instance method
        auto success = env->CallStaticBooleanMethod( key_store_helper_class_, save_method_, jdata );

        env->DeleteLocalRef( jdata );

        if ( env->ExceptionCheck() != 0U )
        {
            LOGE( "Exception occurred while saving to KeyStore" );
            env->ExceptionDescribe();
            env->ExceptionClear();
            return outcome::failure( std::errc::bad_message );
        }

        if ( success == 0U )
        {
            LOGE( "Failed to save to KeyStore" );
            return outcome::failure( std::errc::connection_aborted );
        }

        return outcome::success();
    }

    JNIEnv *AndroidSecureStorage::GetJNIEnv() const
    {
        JNIEnv *env    = nullptr;
        jint    result = g_jvm->GetEnv( reinterpret_cast<void **>( &env ), JNI_VERSION_1_6 );

        if ( result == JNI_EDETACHED )
        {
            result = g_jvm->AttachCurrentThread( &env, nullptr );
            if ( result != JNI_OK )
            {
                LOGE( "Failed to attach current thread" );
                return nullptr;
            }
        }
        else if ( result != JNI_OK )
        {
            LOGE( "Failed to get JNI environment" );
            return nullptr;
        }

        return env;
    }
}

Updated on 2026-03-04 at 13:10:44 -0800