passgo/impl_android.go
Greg Pomerantz b4c2e19988 Android: fix OpenPGP service bind for modern Keychain.
Keychain 6.0.4 no longer exports the legacy
org.openintents.openpgp.api.OpenPgpService action, so the OpenPgpServiceConnection
from openpgp-api.jar bound nothing and every encrypt/decrypt died with a
NullPointerException in OpenPgpApi.executeApi.

Bind our own ServiceConnection against org.openintents.openpgp.IOpenPgpService
(falling back to the legacy action for older Keychain releases), and queue
API calls until the service is actually connected instead of racing the
async bind.

Also fix the //go:generate recipe: it built one jar containing both
PgpConnect and Permissions, which collides with Permissions.jar when gogio
dexes every *.jar in the repo root.
2026-08-26 18:11:10 -04:00

116 lines
2.8 KiB
Go

//go:build android
// +build android
// Regenerate PgpConnect.jar and Permissions.jar (gogio dexes every *.jar in
// the repo root; the two classes must stay in separate jars). ANDROID_HOME
// must be set. Note: go generate skips this file on non-android hosts.
//go:generate sh -c 'mkdir -p classes && javac -nowarn -classpath "$ANDROID_HOME/platforms/android-35/android.jar:openpgp-api.jar" -d classes PgpConnect.java Permissions.java && (cd classes && jar cf ../../PgpConnect.jar st/wow/git/passgo/PgpConnect* && jar cf ../../Permissions.jar st/wow/git/passgo/Permissions*) && rm -rf classes'
package passgo
/*
#import <jni.h>
#import "jni_android.h"
*/
import "C"
import (
"fmt"
"io/ioutil"
"log"
"path"
"strings"
"sync"
"unsafe"
"gioui.org/app"
_ "gioui.org/app/permission/storage"
//"git.wow.st/gmp/jni"
)
var (
jvm uintptr
waitch chan struct{}
//w *app.Window
pgp PGP
installCompleteOnce sync.Once
)
func init() {
waitch = make(chan struct{})
}
//export Java_st_wow_git_passgo_PgpConnect_installComplete
func Java_st_wow_git_passgo_PgpConnect_installComplete(env *C.JNIEnv, class C.jclass, p C.jobject) {
log.Printf("InstallComplete()")
pgp = PGP(C.NewGlobalRef(env, p))
installCompleteOnce.Do(func() {
close(waitch)
})
}
func InitPgp(view uintptr) {
log.Printf("InitPgp()")
if view == 0 {
return // detach signal; nothing to register
}
jvm = app.JavaVM()
SetJVM(jvm) // why?
RunInJVM(func(env *JNIEnv) {
C.registerFragment(env, (C.jobject)(unsafe.Pointer(view)))
})
}
// InitPermissions registers the Permissions fragment, which requests the
// storage permissions (launching the system "All files access" settings
// screen on Android 11+) so the app can read the store in shared storage.
func InitPermissions(view uintptr) {
log.Printf("InitPermissions()")
if view == 0 {
return // detach signal; nothing to register
}
RunInJVM(func(env *JNIEnv) {
C.registerPermissionsFragment(env, (C.jobject)(unsafe.Pointer(view)))
})
}
func stopPgp() {
waitch = make(chan struct{})
}
func connect() {
<-waitch
}
// Clip copies a string to the clipboard
func Clip(x string) {
pgp.Clip(x)
}
func (s *Store) nativeDecrypt(name string) (string, error) {
connect()
data, err := ioutil.ReadFile(path.Join(s.Dir, strings.Join([]string{name, ".gpg"}, "")))
if err != nil {
return "", fmt.Errorf("Error reading file: %s", err)
}
ret, err := pgp.Decrypt(string(data))
return ret, err
}
func (s *Store) nativeEncrypt(pw string) ([]byte, error) {
connect()
ret, err := pgp.Encrypt(s.Id, pw)
return []byte(ret), err
}
func nativeIdentities() ([]string, error) {
//NOT IMPLEMENTED ON ANDROID
return []string{}, nil
log.Printf("nativeIdentities()")
connect()
log.Printf("calling pgp.GetId()")
id, err := pgp.GetId()
log.Printf("pgp.GetId() returned")
return []string{id}, err
}