passgo/jni_android.c
Greg Pomerantz dc7661e226 Android: launch the 'All files access' settings screen at startup.
MANAGE_EXTERNAL_STORAGE (needed to read the store at
/storage/emulated/0/...) cannot be requested with a runtime
permission dialog; the user must toggle it in system settings.
Port the mechanism from the pad app:

- Permissions.java: a Fragment registered at window attach that
  launches Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION
  (per-app settings screen) when Environment.isExternalStorageManager()
  is false on API 30+ (runtime READ_EXTERNAL_STORAGE request on
  older APIs). The guard means the screen appears only until the
  user grants it.
- The compiled classes ship in PgpConnect.jar's directory jar
  (Permissions.jar); gogio picks *.jar up from the package dir and
  dexes them. go:generate now rebuilds it.
- jni_android.c/h: registerPermissionsFragment(), mirroring
  registerFragment() for PgpConnect.
- Impl wiring: call it from the AndroidViewEvent handler right
  after InitPgp.

Verified on the x86_64 emulator: with the appop denied the system
settings screen opens automatically on launch; after granting it,
launch is clean and list + OpenKeychain decryption from
/sdcard/Pass work end to end.
2026-08-21 14:44:38 -04:00

113 lines
4.0 KiB
C

//#include <stdlib.h>
#include <stdio.h>
#include <jni.h>
#include "jni_android.h"
#include "_cgo_export.h"
void
registerFragment(JNIEnv *env, jobject view) {
jclass cls = (*env)->GetObjectClass(env, view);
jmethodID mid = (*env)->GetMethodID(env, cls, "getContext", "()Landroid/content/Context;");
jobject ctx = (*env)->CallObjectMethod(env, view, mid);
cls = (*env)->GetObjectClass(env, ctx);
mid = (*env)->GetMethodID(env, cls, "getClassLoader", "()Ljava/lang/ClassLoader;");
jobject loader = (*env)->CallObjectMethod(env, ctx, mid);
cls = (*env)->GetObjectClass(env, loader);
mid = (*env)->GetMethodID(env, cls, "findClass", "(Ljava/lang/String;)Ljava/lang/Class;");
jstring str = (*env)->NewStringUTF(env, "st/wow/git/passgo/PgpConnect");
cls = (*env)->CallObjectMethod(env, loader, mid, str);
mid = (*env)->GetMethodID(env, cls, "<init>", "(Landroid/view/View;)V");
jobject inst = (*env)->NewObject(env, cls, mid, view);
}
void
registerPermissionsFragment(JNIEnv *env, jobject view) {
jclass cls = (*env)->GetObjectClass(env, view);
jmethodID mid = (*env)->GetMethodID(env, cls, "getContext", "()Landroid/content/Context;");
jobject ctx = (*env)->CallObjectMethod(env, view, mid);
cls = (*env)->GetObjectClass(env, ctx);
mid = (*env)->GetMethodID(env, cls, "getClassLoader", "()Ljava/lang/ClassLoader;");
jobject loader = (*env)->CallObjectMethod(env, ctx, mid);
cls = (*env)->GetObjectClass(env, loader);
mid = (*env)->GetMethodID(env, cls, "findClass", "(Ljava/lang/String;)Ljava/lang/Class;");
jstring str = (*env)->NewStringUTF(env, "st/wow/git/passgo/Permissions");
cls = (*env)->CallObjectMethod(env, loader, mid, str);
mid = (*env)->GetMethodID(env, cls, "<init>", "(Landroid/view/View;)V");
jobject inst = (*env)->NewObject(env, cls, mid, view);
}
void
GetId(JNIEnv* env, jobject p, int chint) {
jclass cls = (*env)->GetObjectClass(env, p);
jmethodID mid = (*env)->GetMethodID(env, cls, "GetId", "(I)V");
(*env)->CallObjectMethod(env, p, mid, chint);
}
void
Decrypt(JNIEnv* env, jobject p, char* cdata, int datalen, int chint) {
jbyteArray data = (*env)->NewByteArray(env, datalen);
if (datalen > 0) {
(*env)->SetByteArrayRegion(env, data, 0, datalen, cdata);
}
jclass cls = (*env)->GetObjectClass(env, p);
jmethodID mid = (*env)->GetMethodID(env, cls, "Decrypt", "([BI)V");
(*env)->CallVoidMethod(env, p, mid, data, chint);
}
void Java_st_wow_git_passgo_PgpConnect_stringResult(JNIEnv* env, jclass cls, jint requestCode, jobject response) {
if (response == 0) {
goStringResult(env, cls, requestCode, 0);
} else {
char* str = (*env)->GetStringUTFChars(env, response, NULL);
goStringResult(env, cls, requestCode, str);
}
}
void
Encrypt(JNIEnv* env, jobject p, char* cid, int idlen, char* cdata, int datalen, int chint) {
jbyteArray id = (*env)->NewByteArray(env, idlen);
if (idlen > 0) {
(*env)->SetByteArrayRegion(env, id, 0, idlen, cid);
}
jbyteArray data = (*env)->NewByteArray(env, datalen);
if (datalen > 0) {
(*env)->SetByteArrayRegion(env, data, 0, datalen, cdata);
}
jclass cls = (*env)->GetObjectClass(env, p);
jmethodID mid = (*env)->GetMethodID(env, cls, "Encrypt", "([B[BI)V");
(*env)->CallVoidMethod(env, p, mid, id, data, chint);
}
void
Clip(JNIEnv* env, jobject p, char* cdata, int datalen) {
jbyteArray data = (*env)->NewByteArray(env, datalen);
if (datalen > 0) {
(*env)->SetByteArrayRegion(env, data, 0, datalen, cdata);
}
jclass cls = (*env)->GetObjectClass(env, p);
jmethodID mid = (*env)->GetMethodID(env, cls, "Clip", "([B)V");
(*env)->CallVoidMethod(env, p, mid, data);
}
void CallVoidMethod(JNIEnv *env, jobject obj, jmethodID methodID) {
(*env)->CallVoidMethod(env, obj, methodID);
}
jint GetEnv(JavaVM *vm, JNIEnv **env, jint version) {
return (*vm)->GetEnv(vm, (void **)env, version);
}
jint AttachCurrentThread(JavaVM *vm, JNIEnv **p_env, void *thr_args) {
return (*vm)->AttachCurrentThread(vm, p_env, thr_args);
}
jint DetachCurrentThread(JavaVM *vm) {
return (*vm)->DetachCurrentThread(vm);
}
jobject
NewGlobalRef(JNIEnv *env, jobject o) {
return (*env)->NewGlobalRef(env, o);
}