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.
This commit is contained in:
parent
349445219b
commit
b4c2e19988
BIN
PgpConnect.jar
BIN
PgpConnect.jar
Binary file not shown.
|
|
@ -5,14 +5,17 @@ import java.lang.String;
|
||||||
import java.util.concurrent.BlockingQueue;
|
import java.util.concurrent.BlockingQueue;
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
|
import android.os.IBinder;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import org.openintents.openpgp.IOpenPgpService2;
|
||||||
import org.openintents.openpgp.OpenPgpError;
|
import org.openintents.openpgp.OpenPgpError;
|
||||||
import org.openintents.openpgp.OpenPgpSignatureResult;
|
import org.openintents.openpgp.OpenPgpSignatureResult;
|
||||||
import org.openintents.openpgp.util.OpenPgpApi;
|
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.Context;
|
||||||
import android.content.ClipboardManager;
|
import android.content.ClipboardManager;
|
||||||
import android.content.ClipData;
|
import android.content.ClipData;
|
||||||
|
|
@ -36,9 +39,68 @@ public class PgpConnect extends Fragment {
|
||||||
Context ctx;
|
Context ctx;
|
||||||
Handler handler;
|
Handler handler;
|
||||||
ClipboardManager cb;
|
ClipboardManager cb;
|
||||||
OpenPgpServiceConnection mServiceConnection;
|
IOpenPgpService2 mService;
|
||||||
final int PERMISSIONS_REQUEST = 1;
|
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) {
|
public PgpConnect(View view) {
|
||||||
Log.d("gio", "PgpConnect()");
|
Log.d("gio", "PgpConnect()");
|
||||||
this.ctx = view.getContext();
|
this.ctx = view.getContext();
|
||||||
|
|
@ -60,8 +122,7 @@ public class PgpConnect extends Fragment {
|
||||||
if (ctx instanceof Activity) {
|
if (ctx instanceof Activity) {
|
||||||
Log.d("gio", "It's an Activity!");
|
Log.d("gio", "It's an Activity!");
|
||||||
}
|
}
|
||||||
mServiceConnection = new OpenPgpServiceConnection(ctx, "org.sufficientlysecure.keychain");
|
bindOpenPgp();
|
||||||
mServiceConnection.bindToService();
|
|
||||||
cb = (ClipboardManager) ctx.getSystemService(Context.CLIPBOARD_SERVICE);
|
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) {
|
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);
|
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) {
|
private void _getid(Intent data, int chint) {
|
||||||
Log.d("gio","_getid");
|
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;
|
InputStream is = null;
|
||||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
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);
|
Intent result = api.executeApi(data, is, os);
|
||||||
switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) {
|
switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) {
|
||||||
case OpenPgpApi.RESULT_CODE_SUCCESS: {
|
case OpenPgpApi.RESULT_CODE_SUCCESS: {
|
||||||
|
|
@ -193,10 +260,16 @@ public class PgpConnect extends Fragment {
|
||||||
}
|
}
|
||||||
private void _decrypt(Intent data, int chint) {
|
private void _decrypt(Intent data, int chint) {
|
||||||
Log.d("gio","_decrypt");
|
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");
|
byte []dat = data.getByteArrayExtra("DATA");
|
||||||
InputStream is = new ByteArrayInputStream(dat);
|
InputStream is = new ByteArrayInputStream(dat);
|
||||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
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);
|
Intent result = api.executeApi(data, is, os);
|
||||||
switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) {
|
switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) {
|
||||||
case OpenPgpApi.RESULT_CODE_SUCCESS: {
|
case OpenPgpApi.RESULT_CODE_SUCCESS: {
|
||||||
|
|
@ -246,10 +319,16 @@ public class PgpConnect extends Fragment {
|
||||||
|
|
||||||
private void _encrypt(Intent data, int chint) {
|
private void _encrypt(Intent data, int chint) {
|
||||||
Log.d("gio","_encrypt");
|
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");
|
byte []dat = data.getByteArrayExtra("DATA");
|
||||||
InputStream is = new ByteArrayInputStream(dat);
|
InputStream is = new ByteArrayInputStream(dat);
|
||||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
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);
|
Intent result = api.executeApi(data, is, os);
|
||||||
switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) {
|
switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) {
|
||||||
case OpenPgpApi.RESULT_CODE_SUCCESS: {
|
case OpenPgpApi.RESULT_CODE_SUCCESS: {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
//go:build android
|
//go:build android
|
||||||
// +build android
|
// +build android
|
||||||
|
|
||||||
//go:generate mkdir -p classes
|
// Regenerate PgpConnect.jar and Permissions.jar (gogio dexes every *.jar in
|
||||||
//go:generate javac -nowarn -classpath $ANDROID_HOME/platforms/android-35/android.jar:openpgp-api.jar -d classes PgpConnect.java Permissions.java
|
// the repo root; the two classes must stay in separate jars). ANDROID_HOME
|
||||||
//go:generate jar cf PgpConnect.jar -C classes .
|
// must be set. Note: go generate skips this file on non-android hosts.
|
||||||
//go:generate rm -rf classes
|
//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
|
package passgo
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user