Android: bind the IOpenPgpService2 service; declare <queries> in manifest.

The v1 action (org.openintents.openpgp.IOpenPgpService) resolves in
Keychain 6.0.4 but its binder does not implement the IOpenPgpService2
AIDL descriptor that OpenPgpApi calls through, so every call died with
SecurityException: Binder invocation to an incorrect interface. Bind
the IOpenPgpService2 service first (v1 and the very old
api.OpenPgpService actions remain as fallbacks).

Android 11+ package visibility also hid Keychain's services entirely:
resolveService/bindService found nothing from the app even with
MANAGE_EXTERNAL_STORAGE granted, so the build now injects a <queries>
element (package + intent) into the decoded manifest.
This commit is contained in:
Greg Pomerantz 2026-08-26 20:47:13 -04:00
parent b4c2e19988
commit 45c1c34982
3 changed files with 22 additions and 6 deletions

Binary file not shown.

View File

@ -42,12 +42,15 @@ public class PgpConnect extends Fragment {
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.
// The OpenPgpApi in openpgp-api.jar calls through the IOpenPgpService2
// AIDL interface, so we must bind the service that implements that
// descriptor: org.openintents.openpgp.IOpenPgpService2 (Keychain >= 11 /
// standalone 5.x-6.x). The v1 action (IOpenPgpService) does not implement
// the v2 descriptor and dies with a SecurityException; the very old
// org.openintents.openpgp.api.OpenPgpService action (used by
// OpenPgpServiceConnection) no longer exists. Try in that order.
static final String[] SERVICE_ACTIONS = {
"org.openintents.openpgp.IOpenPgpService2",
"org.openintents.openpgp.IOpenPgpService",
"org.openintents.openpgp.api.OpenPgpService",
};

View File

@ -74,8 +74,21 @@ if perm not in t:
t = t.replace(" <application", " " + perm + "\n <application", 1)
if perm not in t:
sys.exit("ERROR: failed to inject MANAGE_EXTERNAL_STORAGE")
# Android 11+ package visibility: without <queries> the app cannot even
# resolve (let alone bind) Keychain's OpenPGP service.
queries = """ <queries>
<package android:name="org.sufficientlysecure.keychain"/>
<intent>
<action android:name="org.openintents.openpgp.IOpenPgpService2"/>
</intent>
</queries>
"""
if "<queries>" not in t:
t = t.replace(" <application", queries + " <application", 1)
if "<queries>" not in t:
sys.exit("ERROR: failed to inject <queries>")
p.write_text(t)
print("manifest: MANAGE_EXTERNAL_STORAGE present")
print("manifest: MANAGE_EXTERNAL_STORAGE + <queries> present")
PYEOF
echo "=== apktool rebuild ==="