diff --git a/PgpConnect.jar b/PgpConnect.jar index 91a741f..a82fcfe 100644 Binary files a/PgpConnect.jar and b/PgpConnect.jar differ diff --git a/PgpConnect.java b/PgpConnect.java index 552be58..65b3bf0 100644 --- a/PgpConnect.java +++ b/PgpConnect.java @@ -5,14 +5,17 @@ import java.lang.String; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import android.os.Handler; +import android.os.IBinder; import java.io.InputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.UnsupportedEncodingException; +import org.openintents.openpgp.IOpenPgpService2; import org.openintents.openpgp.OpenPgpError; import org.openintents.openpgp.OpenPgpSignatureResult; import org.openintents.openpgp.util.OpenPgpApi; -import org.openintents.openpgp.util.OpenPgpServiceConnection; +import android.content.ComponentName; +import android.content.ServiceConnection; import android.content.Context; import android.content.ClipboardManager; import android.content.ClipData; @@ -36,9 +39,68 @@ public class PgpConnect extends Fragment { Context ctx; Handler handler; ClipboardManager cb; - OpenPgpServiceConnection mServiceConnection; + IOpenPgpService2 mService; final int PERMISSIONS_REQUEST = 1; + // Keychain >= 11 (and the standalone "Keychain" 5.x/6.x releases) expose + // the OpenPGP API service under org.openintents.openpgp.IOpenPgpService; + // older releases used org.openintents.openpgp.api.OpenPgpService (the + // action hardcoded in OpenPgpServiceConnection). Try the new action + // first and fall back to the legacy one. + static final String[] SERVICE_ACTIONS = { + "org.openintents.openpgp.IOpenPgpService", + "org.openintents.openpgp.api.OpenPgpService", + }; + + final ServiceConnection mConnection = new ServiceConnection() { + @Override public void onServiceConnected(ComponentName name, IBinder service) { + mService = IOpenPgpService2.Stub.asInterface(service); + Log.d("gio", "OpenPGP service connected: " + name); + } + @Override public void onServiceDisconnected(ComponentName name) { + mService = null; + Log.d("gio", "OpenPGP service disconnected"); + } + }; + + private void bindOpenPgp() { + Intent bindIntent = null; + for (String action : SERVICE_ACTIONS) { + Intent i = new Intent(action); + i.setPackage("org.sufficientlysecure.keychain"); + if (ctx.getPackageManager().resolveService(i, 0) != null) { + bindIntent = i; + break; + } + } + if (bindIntent == null) { + Log.e("gio", "No OpenPGP service found in org.sufficientlysecure.keychain"); + return; + } + Log.d("gio", "Binding OpenPGP service: " + bindIntent.getAction()); + ctx.bindService(bindIntent, mConnection, Context.BIND_AUTO_CREATE); + } + + // executeApi needs a live service; the bind is asynchronous, so if the + // service is not connected yet, retry on the main looper (up to ~10 s) + // instead of calling into a null service. + private void runWhenConnected(final int chint, final Runnable task, final int attempt) { + if (mService != null) { + task.run(); + return; + } + if (attempt > 33) { + Log.e("gio", "OpenPGP service never connected"); + stringResult(chint, null); + return; + } + handler.postDelayed(new Runnable() { + public void run() { + runWhenConnected(chint, task, attempt + 1); + } + }, 300); + } + public PgpConnect(View view) { Log.d("gio", "PgpConnect()"); this.ctx = view.getContext(); @@ -60,8 +122,7 @@ public class PgpConnect extends Fragment { if (ctx instanceof Activity) { Log.d("gio", "It's an Activity!"); } - mServiceConnection = new OpenPgpServiceConnection(ctx, "org.sufficientlysecure.keychain"); - mServiceConnection.bindToService(); + bindOpenPgp(); cb = (ClipboardManager) ctx.getSystemService(Context.CLIPBOARD_SERVICE); if (ctx.checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || ctx.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSIONS_REQUEST); @@ -156,9 +217,15 @@ public class PgpConnect extends Fragment { private void _getid(Intent data, int chint) { Log.d("gio","_getid"); + runWhenConnected(chint, new Runnable() { + public void run() { doGetid(data, chint); } + }, 0); + } + + private void doGetid(Intent data, int chint) { InputStream is = null; ByteArrayOutputStream os = new ByteArrayOutputStream(); - OpenPgpApi api = new OpenPgpApi(this.ctx, mServiceConnection.getService()); + OpenPgpApi api = new OpenPgpApi(this.ctx, mService); Intent result = api.executeApi(data, is, os); switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) { case OpenPgpApi.RESULT_CODE_SUCCESS: { @@ -193,10 +260,16 @@ public class PgpConnect extends Fragment { } private void _decrypt(Intent data, int chint) { Log.d("gio","_decrypt"); + runWhenConnected(chint, new Runnable() { + public void run() { doDecrypt(data, chint); } + }, 0); + } + + private void doDecrypt(Intent data, int chint) { byte []dat = data.getByteArrayExtra("DATA"); InputStream is = new ByteArrayInputStream(dat); ByteArrayOutputStream os = new ByteArrayOutputStream(); - OpenPgpApi api = new OpenPgpApi(this.ctx, mServiceConnection.getService()); + OpenPgpApi api = new OpenPgpApi(this.ctx, mService); Intent result = api.executeApi(data, is, os); switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) { case OpenPgpApi.RESULT_CODE_SUCCESS: { @@ -246,10 +319,16 @@ public class PgpConnect extends Fragment { private void _encrypt(Intent data, int chint) { Log.d("gio","_encrypt"); + runWhenConnected(chint, new Runnable() { + public void run() { doEncrypt(data, chint); } + }, 0); + } + + private void doEncrypt(Intent data, int chint) { byte []dat = data.getByteArrayExtra("DATA"); InputStream is = new ByteArrayInputStream(dat); ByteArrayOutputStream os = new ByteArrayOutputStream(); - OpenPgpApi api = new OpenPgpApi(this.ctx, mServiceConnection.getService()); + OpenPgpApi api = new OpenPgpApi(this.ctx, mService); Intent result = api.executeApi(data, is, os); switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) { case OpenPgpApi.RESULT_CODE_SUCCESS: { diff --git a/impl_android.go b/impl_android.go index e4bf256..8b5f635 100644 --- a/impl_android.go +++ b/impl_android.go @@ -1,10 +1,10 @@ //go:build android // +build android -//go:generate mkdir -p classes -//go:generate javac -nowarn -classpath $ANDROID_HOME/platforms/android-35/android.jar:openpgp-api.jar -d classes PgpConnect.java Permissions.java -//go:generate jar cf PgpConnect.jar -C classes . -//go:generate rm -rf classes +// 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