Skip to content

Commit

Permalink
Merge pull request #952 from m2049r/feature/sidekick_v
Browse files Browse the repository at this point in the history
Support for Sidekick device
  • Loading branch information
m2049r authored Sep 5, 2024
2 parents 48577e4 + 059438a commit ac9e24e
Show file tree
Hide file tree
Showing 129 changed files with 3,740 additions and 381 deletions.
8 changes: 2 additions & 6 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,8 @@
.DS_Store
/app/build
/app/release
/app/alpha
/app/prod
/app/alphaMainnet
/app/prodMainnet
/app/alphaStagenet
/app/prodStagenet
/app/alpha*
/app/prod*
/app/.cxx
/monerujo.id
/external-libs/VERSION
Expand Down
16 changes: 8 additions & 8 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ android {
buildToolsVersion = '34.0.0'
compileSdk 34
minSdkVersion 21
targetSdkVersion 33
versionCode 3311
versionName "3.3.11 'Argentina'"
targetSdkVersion 35
versionCode 4005
versionName "4.0.5 'Sidekick'"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
Expand All @@ -24,7 +24,7 @@ android {
}
}

flavorDimensions 'type', 'net'
flavorDimensions = ['type', 'net']
productFlavors {
mainnet {
dimension 'net'
Expand Down Expand Up @@ -132,18 +132,18 @@ static def getId(name) {
}

dependencies {
implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.8.0"))
implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.8.22"))

implementation 'androidx.core:core:1.12.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.core:core:1.13.1'
implementation 'androidx.appcompat:appcompat:1.7.0'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.recyclerview:recyclerview:1.3.2'
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.preference:preference:1.2.1'

implementation 'com.google.android.material:material:1.11.0'
implementation 'com.google.android.material:material:1.12.0'

implementation 'me.dm7.barcodescanner:zxing:1.9.8'
implementation "com.squareup.okhttp3:okhttp:4.12.0"
Expand Down
13 changes: 12 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,19 @@
android:name="android.hardware.camera"
android:required="false" />

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />

<queries>
<intent>
Expand Down Expand Up @@ -98,7 +104,12 @@
android:name=".service.WalletService"
android:description="@string/service_description"
android:exported="false"
android:label="Monero Wallet Service" />
android:foregroundServiceType="specialUse"
android:label="Monero Wallet Service">
<property
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
android:value="Keeps app in sync with the blockchain" />
</service>

<provider
android:name="androidx.core.content.FileProvider"
Expand Down
92 changes: 78 additions & 14 deletions app/src/main/cpp/monerujo.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2017 m2049r
* Copyright (c) 2017-2024 m2049r
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -18,8 +18,6 @@
#include "monerujo.h"
#include "wallet2_api.h"

//TODO explicit casting jlong, jint, jboolean to avoid warnings

#ifdef __cplusplus
extern "C"
{
Expand All @@ -34,25 +32,18 @@ extern "C"
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR , LOG_TAG,__VA_ARGS__)

static JavaVM *cachedJVM;
static jclass class_String;
static jclass class_ArrayList;
static jclass class_WalletListener;
static jclass class_CoinsInfo;
static jclass class_TransactionInfo;
static jclass class_Transfer;
static jclass class_Ledger;
static jclass class_WalletStatus;
static jclass class_BluetoothService;
static jclass class_SidekickService;

std::mutex _listenerMutex;

//void jstringToString(JNIEnv *env, std::string &str, jstring jstr) {
// if (!jstr) return;
// const int len = env->GetStringUTFLength(jstr);
// const char *chars = env->GetStringUTFChars(jstr, nullptr);
// str.assign(chars, len);
// env->ReleaseStringUTFChars(jstr, chars);
//}

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
cachedJVM = jvm;
LOGI("JNI_OnLoad");
Expand All @@ -62,8 +53,6 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
}
//LOGI("JNI_OnLoad ok");

class_String = static_cast<jclass>(jenv->NewGlobalRef(
jenv->FindClass("java/lang/String")));
class_ArrayList = static_cast<jclass>(jenv->NewGlobalRef(
jenv->FindClass("java/util/ArrayList")));
class_CoinsInfo = static_cast<jclass>(jenv->NewGlobalRef(
Expand All @@ -78,6 +67,8 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
jenv->FindClass("com/m2049r/xmrwallet/ledger/Ledger")));
class_WalletStatus = static_cast<jclass>(jenv->NewGlobalRef(
jenv->FindClass("com/m2049r/xmrwallet/model/Wallet$Status")));
class_BluetoothService = static_cast<jclass>(jenv->NewGlobalRef(
jenv->FindClass("com/m2049r/xmrwallet/service/BluetoothService")));
return JNI_VERSION_1_6;
}
#ifdef __cplusplus
Expand Down Expand Up @@ -1686,6 +1677,79 @@ int LedgerFind(char *buffer, size_t len) {
return ret;
}

//
// SidekickWallet Stuff
//

/**
* @brief BtExchange - exchange data with Monerujo Device
* @param request - buffer for data to send
* @param request_len - length of data to send
* @param response - buffer for received data
* @param max_resp_len - size of receive buffer
*
* @return length of received data in response or -1 if error, -2 if response buffer too small
*/
int BtExchange(
unsigned char *request,
unsigned int request_len,
unsigned char *response,
unsigned int max_resp_len) {
JNIEnv *jenv;
int envStat = attachJVM(&jenv);
if (envStat == JNI_ERR) return -16;

jmethodID exchangeMethod = jenv->GetStaticMethodID(class_BluetoothService, "Exchange",
"([B)[B");

auto reqLen = static_cast<jsize>(request_len);
jbyteArray reqData = jenv->NewByteArray(reqLen);
jenv->SetByteArrayRegion(reqData, 0, reqLen, (jbyte *) request);
LOGD("BtExchange cmd: 0x%02x with %u bytes", request[0], reqLen);
auto dataRecv = (jbyteArray)
jenv->CallStaticObjectMethod(class_BluetoothService, exchangeMethod, reqData);
jenv->DeleteLocalRef(reqData);
if (dataRecv == nullptr) {
detachJVM(jenv, envStat);
LOGD("BtExchange: error reading");
return -1;
}
jsize respLen = jenv->GetArrayLength(dataRecv);
LOGD("BtExchange response is %u bytes", respLen);
if (respLen <= max_resp_len) {
jenv->GetByteArrayRegion(dataRecv, 0, respLen, (jbyte *) response);
jenv->DeleteLocalRef(dataRecv);
detachJVM(jenv, envStat);
return static_cast<int>(respLen);;
} else {
jenv->DeleteLocalRef(dataRecv);
detachJVM(jenv, envStat);
LOGE("BtExchange response buffer too small: %u < %u", respLen, max_resp_len);
return -2;
}
}

/**
* @brief ConfirmTransfers
* @param transfers - string of "fee (':' address ':' amount)+"
*
* @return true on accept, false on reject
*/
bool ConfirmTransfers(const char *transfers) {
JNIEnv *jenv;
int envStat = attachJVM(&jenv);
if (envStat == JNI_ERR) return -16;

jmethodID confirmMethod = jenv->GetStaticMethodID(class_SidekickService, "ConfirmTransfers",
"(Ljava/lang/String;)Z");

jstring _transfers = jenv->NewStringUTF(transfers);
auto confirmed =
jenv->CallStaticBooleanMethod(class_SidekickService, confirmMethod, _transfers);
jenv->DeleteLocalRef(_transfers);
return confirmed;
}

#ifdef __cplusplus
}
#endif
48 changes: 42 additions & 6 deletions app/src/main/cpp/monerujo.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2017 m2049r
* Copyright (c) 2017-2024 m2049r
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -19,6 +19,8 @@

#include <jni.h>

#include <string>

/*
#include <android/log.h>
Expand All @@ -28,15 +30,27 @@
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
*/

void ThrowException(JNIEnv *jenv, const char* type, const char* msg) {
jenv->ThrowNew(jenv->FindClass(type), msg);
}

jfieldID getHandleField(JNIEnv *env, jobject obj, const char *fieldName = "handle") {
jclass c = env->GetObjectClass(obj);
return env->GetFieldID(c, fieldName, "J"); // of type long
}

template<typename T>
T *getHandle(JNIEnv *env, jobject obj, const char *fieldName = "handle") {
return reinterpret_cast<T *>(env->GetLongField(obj, getHandleField(env, obj, fieldName)));
}

template<typename T>
void destroyNativeObject(JNIEnv *env, T nativeObjectHandle, jobject obj, const char *fieldName = "handle") {
jlong handle = env->GetLongField(obj, getHandleField(env, obj, fieldName));
return reinterpret_cast<T *>(handle);
if (handle != 0) {
ThrowException(env, "java/lang/IllegalStateException", "invalid handle (destroy)");
}
delete reinterpret_cast<T *>(nativeObjectHandle);
}

void setHandleFromLong(JNIEnv *env, jobject obj, jlong handle) {
Expand All @@ -54,26 +68,48 @@ extern "C"
{
#endif

extern const char* const MONERO_VERSION; // the actual monero core version
extern const char *const MONERO_VERSION; // the actual monero core version

// from monero-core crypto/hash-ops.h - avoid #including monero code here
enum {
HASH_SIZE = 32,
HASH_DATA_AREA = 136
};

void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed, uint64_t height);
void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed,
uint64_t height);

inline void slow_hash(const void *data, const size_t length, char *hash) {
cn_slow_hash(data, length, hash, 0 /*variant*/, 0 /*prehashed*/, 0 /*height*/);
}

inline void slow_hash_broken(const void *data, char *hash, int variant) {
cn_slow_hash(data, 200 /*sizeof(union hash_state)*/, hash, variant, 1 /*prehashed*/, 0 /*height*/);
cn_slow_hash(data, 200 /*sizeof(union hash_state)*/, hash, variant, 1 /*prehashed*/,
0 /*height*/);
}

#ifdef __cplusplus
}
#endif

namespace Monerujo {
class SidekickWallet {
public:
enum Status {
Status_Ok,
Status_Error,
Status_Critical
};

SidekickWallet(uint8_t networkType, std::string a, std::string b);

~SidekickWallet();

std::string call(int commandId, const std::string &request);

void reset();

Status status() const;

};
}
#endif //XMRWALLET_WALLET_LIB_H
Binary file added app/src/main/ic_launcher-playstore.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit ac9e24e

Please sign in to comment.