IMplement CryptoInputParcelCacheService

This commit is contained in:
Dominik Schürmann 2015-04-13 23:29:35 +02:00
parent 9fc001c9b9
commit 256d644d03
7 changed files with 325 additions and 100 deletions

View File

@ -696,6 +696,10 @@
android:name=".service.PassphraseCacheService" android:name=".service.PassphraseCacheService"
android:exported="false" android:exported="false"
android:process=":passphrase_cache" /> android:process=":passphrase_cache" />
<service
android:name=".remote.CryptoInputParcelCacheService"
android:exported="false"
android:process=":remote_api" />
<service <service
android:name=".service.KeychainIntentService" android:name=".service.KeychainIntentService"
android:exported="false" /> android:exported="false" />

View File

@ -0,0 +1,246 @@
/*
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.remote;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import org.openintents.openpgp.util.OpenPgpApi;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.util.Log;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public class CryptoInputParcelCacheService extends Service {
public static final String ACTION_ADD = Constants.INTENT_PREFIX + "ADD";
public static final String ACTION_GET = Constants.INTENT_PREFIX + "GET";
public static final String EXTRA_CRYPTO_INPUT_PARCEL = "crypto_input_parcel";
public static final String EXTRA_UUID1 = "uuid1";
public static final String EXTRA_UUID2 = "uuid2";
public static final String EXTRA_MESSENGER = "messenger";
private static final int MSG_GET_OKAY = 1;
private static final int MSG_GET_NOT_FOUND = 2;
Context mContext;
private static final UUID NULL_UUID = new UUID(0, 0);
private ConcurrentHashMap<UUID, CryptoInputParcel> mCache = new ConcurrentHashMap<>();
public static class InputParcelNotFound extends Exception {
public InputParcelNotFound() {
}
public InputParcelNotFound(String name) {
super(name);
}
}
public static void addCryptoInputParcel(Context context, Intent data, CryptoInputParcel inputParcel) {
UUID mTicket = addCryptoInputParcel(context, inputParcel);
// And write out the UUID most and least significant bits.
data.putExtra(OpenPgpApi.EXTRA_CALL_UUID1, mTicket.getMostSignificantBits());
data.putExtra(OpenPgpApi.EXTRA_CALL_UUID2, mTicket.getLeastSignificantBits());
}
public static CryptoInputParcel getCryptoInputParcel(Context context, Intent data) {
if (!data.getExtras().containsKey(OpenPgpApi.EXTRA_CALL_UUID1)
|| !data.getExtras().containsKey(OpenPgpApi.EXTRA_CALL_UUID2)) {
return null;
}
long mostSig = data.getLongExtra(OpenPgpApi.EXTRA_CALL_UUID1, 0);
long leastSig = data.getLongExtra(OpenPgpApi.EXTRA_CALL_UUID2, 0);
UUID uuid = new UUID(mostSig, leastSig);
try {
return getCryptoInputParcel(context, uuid);
} catch (InputParcelNotFound inputParcelNotFound) {
return null;
}
}
private static UUID addCryptoInputParcel(Context context, CryptoInputParcel inputParcel) {
UUID uuid = UUID.randomUUID();
Intent intent = new Intent(context, CryptoInputParcelCacheService.class);
intent.setAction(ACTION_ADD);
intent.putExtra(EXTRA_CRYPTO_INPUT_PARCEL, inputParcel);
intent.putExtra(EXTRA_UUID1, uuid.getMostSignificantBits());
intent.putExtra(EXTRA_UUID2, uuid.getLeastSignificantBits());
context.startService(intent);
return uuid;
}
private static CryptoInputParcel getCryptoInputParcel(Context context, UUID uuid) throws InputParcelNotFound {
Intent intent = new Intent(context, CryptoInputParcelCacheService.class);
intent.setAction(ACTION_GET);
final Object mutex = new Object();
final Message returnMessage = Message.obtain();
HandlerThread handlerThread = new HandlerThread("getParcelableThread");
handlerThread.start();
Handler returnHandler = new Handler(handlerThread.getLooper()) {
@Override
public void handleMessage(Message message) {
// copy over result to handle after mutex.wait
returnMessage.what = message.what;
returnMessage.copyFrom(message);
synchronized (mutex) {
mutex.notify();
}
// quit handlerThread
getLooper().quit();
}
};
// Create a new Messenger for the communication back
Messenger messenger = new Messenger(returnHandler);
intent.putExtra(EXTRA_UUID1, uuid.getMostSignificantBits());
intent.putExtra(EXTRA_UUID2, uuid.getLeastSignificantBits());
intent.putExtra(EXTRA_MESSENGER, messenger);
// send intent to this service
context.startService(intent);
// Wait on mutex until parcelable is returned to handlerThread. Note that this local
// variable is used in the handler closure above, so it does make sense here!
// noinspection SynchronizationOnLocalVariableOrMethodParameter
synchronized (mutex) {
try {
mutex.wait(3000);
} catch (InterruptedException e) {
// don't care
}
}
switch (returnMessage.what) {
case MSG_GET_OKAY:
Bundle returnData = returnMessage.getData();
returnData.setClassLoader(context.getClassLoader());
return returnData.getParcelable(EXTRA_CRYPTO_INPUT_PARCEL);
case MSG_GET_NOT_FOUND:
throw new InputParcelNotFound();
default:
Log.e(Constants.TAG, "timeout!");
throw new InputParcelNotFound("should not happen!");
}
}
/**
* Executed when service is started by intent
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent == null || intent.getAction() == null) {
return START_NOT_STICKY;
}
String action = intent.getAction();
switch (action) {
case ACTION_ADD: {
long uuid1 = intent.getLongExtra(EXTRA_UUID1, 0);
long uuid2 = intent.getLongExtra(EXTRA_UUID2, 0);
UUID uuid = new UUID(uuid1, uuid2);
CryptoInputParcel inputParcel = intent.getParcelableExtra(EXTRA_CRYPTO_INPUT_PARCEL);
mCache.put(uuid, inputParcel);
break;
}
case ACTION_GET: {
long uuid1 = intent.getLongExtra(EXTRA_UUID1, 0);
long uuid2 = intent.getLongExtra(EXTRA_UUID2, 0);
UUID uuid = new UUID(uuid1, uuid2);
Messenger messenger = intent.getParcelableExtra(EXTRA_MESSENGER);
Message msg = Message.obtain();
// UUID.equals isn't well documented; we use compareTo instead.
if (NULL_UUID.compareTo(uuid) == 0) {
msg.what = MSG_GET_NOT_FOUND;
} else {
CryptoInputParcel inputParcel = mCache.get(uuid);
mCache.remove(uuid);
msg.what = MSG_GET_OKAY;
Bundle bundle = new Bundle();
bundle.putParcelable(EXTRA_CRYPTO_INPUT_PARCEL, inputParcel);
msg.setData(bundle);
}
try {
messenger.send(msg);
} catch (RemoteException e) {
Log.e(Constants.TAG, "CryptoInputParcelCacheService: Sending message failed", e);
}
break;
}
default: {
Log.e(Constants.TAG, "CryptoInputParcelCacheService: Intent or Intent Action not supported!");
break;
}
}
if (mCache.size() <= 0) {
// stop whole service if cache is empty
Log.d(Constants.TAG, "CryptoInputParcelCacheService: No passphrases remaining in memory, stopping service!");
stopSelf();
}
return START_NOT_STICKY;
}
@Override
public void onCreate() {
super.onCreate();
mContext = this;
Log.d(Constants.TAG, "CryptoInputParcelCacheService, onCreate()");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(Constants.TAG, "CryptoInputParcelCacheService, onDestroy()");
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public class CryptoInputParcelCacheServiceBinder extends Binder {
public CryptoInputParcelCacheService getService() {
return CryptoInputParcelCacheService.this;
}
}
private final IBinder mBinder = new CryptoInputParcelCacheServiceBinder();
}

View File

@ -36,7 +36,6 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult; import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogEntryParcel; import org.sufficientlysecure.keychain.operations.results.OperationResult.LogEntryParcel;
import org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult; import org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult;
import org.sufficientlysecure.keychain.operations.results.SignEncryptResult;
import org.sufficientlysecure.keychain.pgp.PgpConstants; import org.sufficientlysecure.keychain.pgp.PgpConstants;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify; import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
import org.sufficientlysecure.keychain.pgp.PgpSignEncryptInputParcel; import org.sufficientlysecure.keychain.pgp.PgpSignEncryptInputParcel;
@ -57,7 +56,6 @@ import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity;
import org.sufficientlysecure.keychain.ui.ViewKeyActivity; import org.sufficientlysecure.keychain.ui.ViewKeyActivity;
import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ParcelableCache;
import org.sufficientlysecure.keychain.util.Passphrase; import org.sufficientlysecure.keychain.util.Passphrase;
import java.io.IOException; import java.io.IOException;
@ -68,25 +66,6 @@ import java.util.Set;
public class OpenPgpService extends RemoteService { public class OpenPgpService extends RemoteService {
/**
* Instead of parceling the CryptoInputParcel, they are cached on our side to prevent
* leakage of passphrases, symmetric keys, an yubikey related pass-through values
*/
private static ParcelableCache<CryptoInputParcel> inputParcelCache;
static {
inputParcelCache = new ParcelableCache<>();
}
public static void cacheCryptoInputParcel(Intent data, CryptoInputParcel inputParcel) {
inputParcelCache.cacheAndWriteToIntent(inputParcel, data,
OpenPgpApi.EXTRA_CALL_UUID1, OpenPgpApi.EXTRA_CALL_UUID2);
}
public static CryptoInputParcel getCryptoInputParcel(Intent data) {
return inputParcelCache.readFromIntentAndGetFromCache(data,
OpenPgpApi.EXTRA_CALL_UUID1, OpenPgpApi.EXTRA_CALL_UUID2);
}
static final String[] EMAIL_SEARCH_PROJECTION = new String[]{ static final String[] EMAIL_SEARCH_PROJECTION = new String[]{
KeyRings._ID, KeyRings._ID,
KeyRings.MASTER_KEY_ID, KeyRings.MASTER_KEY_ID,
@ -283,7 +262,7 @@ public class OpenPgpService extends RemoteService {
long inputLength = is.available(); long inputLength = is.available();
InputData inputData = new InputData(is, inputLength); InputData inputData = new InputData(is, inputLength);
CryptoInputParcel inputParcel = getCryptoInputParcel(data); CryptoInputParcel inputParcel = CryptoInputParcelCacheService.getCryptoInputParcel(this, data);
if (inputParcel == null) { if (inputParcel == null) {
inputParcel = new CryptoInputParcel(); inputParcel = new CryptoInputParcel();
} }
@ -424,7 +403,7 @@ public class OpenPgpService extends RemoteService {
.setAdditionalEncryptId(signKeyId); // add sign key for encryption .setAdditionalEncryptId(signKeyId); // add sign key for encryption
} }
CryptoInputParcel inputParcel = getCryptoInputParcel(data); CryptoInputParcel inputParcel = CryptoInputParcelCacheService.getCryptoInputParcel(this, data);
if (inputParcel == null) { if (inputParcel == null) {
inputParcel = new CryptoInputParcel(); inputParcel = new CryptoInputParcel();
} }
@ -513,7 +492,7 @@ public class OpenPgpService extends RemoteService {
this, new ProviderHelper(getContext()), null, inputData, os this, new ProviderHelper(getContext()), null, inputData, os
); );
CryptoInputParcel inputParcel = getCryptoInputParcel(data); CryptoInputParcel inputParcel = CryptoInputParcelCacheService.getCryptoInputParcel(this, data);
if (inputParcel == null) { if (inputParcel == null) {
inputParcel = new CryptoInputParcel(); inputParcel = new CryptoInputParcel();
} }
@ -768,9 +747,7 @@ public class OpenPgpService extends RemoteService {
return null; return null;
} }
// TODO: multi-threading
private final IOpenPgpService.Stub mBinder = new IOpenPgpService.Stub() { private final IOpenPgpService.Stub mBinder = new IOpenPgpService.Stub() {
@Override @Override
public Intent execute(Intent data, ParcelFileDescriptor input, ParcelFileDescriptor output) { public Intent execute(Intent data, ParcelFileDescriptor input, ParcelFileDescriptor output) {
try { try {
@ -780,31 +757,43 @@ public class OpenPgpService extends RemoteService {
} }
String action = data.getAction(); String action = data.getAction();
if (OpenPgpApi.ACTION_CLEARTEXT_SIGN.equals(action)) { switch (action) {
case OpenPgpApi.ACTION_CLEARTEXT_SIGN: {
return signImpl(data, input, output, true); return signImpl(data, input, output, true);
} else if (OpenPgpApi.ACTION_SIGN.equals(action)) { }
case OpenPgpApi.ACTION_SIGN: {
// DEPRECATED: same as ACTION_CLEARTEXT_SIGN // DEPRECATED: same as ACTION_CLEARTEXT_SIGN
Log.w(Constants.TAG, "You are using a deprecated API call, please use ACTION_CLEARTEXT_SIGN instead of ACTION_SIGN!"); Log.w(Constants.TAG, "You are using a deprecated API call, please use ACTION_CLEARTEXT_SIGN instead of ACTION_SIGN!");
return signImpl(data, input, output, true); return signImpl(data, input, output, true);
} else if (OpenPgpApi.ACTION_DETACHED_SIGN.equals(action)) { }
case OpenPgpApi.ACTION_DETACHED_SIGN: {
return signImpl(data, input, output, false); return signImpl(data, input, output, false);
} else if (OpenPgpApi.ACTION_ENCRYPT.equals(action)) { }
case OpenPgpApi.ACTION_ENCRYPT: {
return encryptAndSignImpl(data, input, output, false); return encryptAndSignImpl(data, input, output, false);
} else if (OpenPgpApi.ACTION_SIGN_AND_ENCRYPT.equals(action)) { }
case OpenPgpApi.ACTION_SIGN_AND_ENCRYPT: {
return encryptAndSignImpl(data, input, output, true); return encryptAndSignImpl(data, input, output, true);
} else if (OpenPgpApi.ACTION_DECRYPT_VERIFY.equals(action)) { }
case OpenPgpApi.ACTION_DECRYPT_VERIFY: {
return decryptAndVerifyImpl(data, input, output, false); return decryptAndVerifyImpl(data, input, output, false);
} else if (OpenPgpApi.ACTION_DECRYPT_METADATA.equals(action)) { }
case OpenPgpApi.ACTION_DECRYPT_METADATA: {
return decryptAndVerifyImpl(data, input, output, true); return decryptAndVerifyImpl(data, input, output, true);
} else if (OpenPgpApi.ACTION_GET_SIGN_KEY_ID.equals(action)) { }
case OpenPgpApi.ACTION_GET_SIGN_KEY_ID: {
return getSignKeyIdImpl(data); return getSignKeyIdImpl(data);
} else if (OpenPgpApi.ACTION_GET_KEY_IDS.equals(action)) { }
case OpenPgpApi.ACTION_GET_KEY_IDS: {
return getKeyIdsImpl(data); return getKeyIdsImpl(data);
} else if (OpenPgpApi.ACTION_GET_KEY.equals(action)) { }
case OpenPgpApi.ACTION_GET_KEY: {
return getKeyImpl(data); return getKeyImpl(data);
} else { }
default: {
return null; return null;
} }
}
} finally { } finally {
// always close input and output file descriptors even in error cases // always close input and output file descriptors even in error cases
if (input != null) { if (input != null) {

View File

@ -337,11 +337,17 @@ public class PassphraseCacheService extends Service {
public int onStartCommand(Intent intent, int flags, int startId) { public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(Constants.TAG, "PassphraseCacheService.onStartCommand()"); Log.d(Constants.TAG, "PassphraseCacheService.onStartCommand()");
if (intent == null || intent.getAction() == null) {
updateService();
return START_STICKY;
}
// register broadcastreceiver // register broadcastreceiver
registerReceiver(); registerReceiver();
if (intent != null && intent.getAction() != null) { String action = intent.getAction();
if (ACTION_PASSPHRASE_CACHE_ADD.equals(intent.getAction())) { switch (action) {
case ACTION_PASSPHRASE_CACHE_ADD: {
long ttl = intent.getLongExtra(EXTRA_TTL, DEFAULT_TTL); long ttl = intent.getLongExtra(EXTRA_TTL, DEFAULT_TTL);
long masterKeyId = intent.getLongExtra(EXTRA_KEY_ID, -1); long masterKeyId = intent.getLongExtra(EXTRA_KEY_ID, -1);
long subKeyId = intent.getLongExtra(EXTRA_SUBKEY_ID, -1); long subKeyId = intent.getLongExtra(EXTRA_SUBKEY_ID, -1);
@ -365,9 +371,9 @@ public class PassphraseCacheService extends Service {
AlarmManager am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE); AlarmManager am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP, triggerTime, buildIntent(this, referenceKeyId)); am.set(AlarmManager.RTC_WAKEUP, triggerTime, buildIntent(this, referenceKeyId));
} }
break;
updateService(); }
} else if (ACTION_PASSPHRASE_CACHE_GET.equals(intent.getAction())) { case ACTION_PASSPHRASE_CACHE_GET: {
long masterKeyId = intent.getLongExtra(EXTRA_KEY_ID, Constants.key.symmetric); long masterKeyId = intent.getLongExtra(EXTRA_KEY_ID, Constants.key.symmetric);
long subKeyId = intent.getLongExtra(EXTRA_SUBKEY_ID, Constants.key.symmetric); long subKeyId = intent.getLongExtra(EXTRA_SUBKEY_ID, Constants.key.symmetric);
Messenger messenger = intent.getParcelableExtra(EXTRA_MESSENGER); Messenger messenger = intent.getParcelableExtra(EXTRA_MESSENGER);
@ -395,7 +401,9 @@ public class PassphraseCacheService extends Service {
} catch (RemoteException e) { } catch (RemoteException e) {
Log.e(Constants.TAG, "PassphraseCacheService: Sending message failed", e); Log.e(Constants.TAG, "PassphraseCacheService: Sending message failed", e);
} }
} else if (ACTION_PASSPHRASE_CACHE_CLEAR.equals(intent.getAction())) { break;
}
case ACTION_PASSPHRASE_CACHE_CLEAR: {
AlarmManager am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE); AlarmManager am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
if (intent.hasExtra(EXTRA_SUBKEY_ID) && intent.hasExtra(EXTRA_KEY_ID)) { if (intent.hasExtra(EXTRA_SUBKEY_ID) && intent.hasExtra(EXTRA_KEY_ID)) {
@ -419,12 +427,15 @@ public class PassphraseCacheService extends Service {
mPassphraseCache.clear(); mPassphraseCache.clear();
} }
break;
}
default: {
Log.e(Constants.TAG, "PassphraseCacheService: Intent or Intent Action not supported!");
break;
}
}
updateService(); updateService();
} else {
Log.e(Constants.TAG, "PassphraseCacheService: Intent or Intent Action not supported!");
}
}
return START_STICKY; return START_STICKY;
} }

View File

@ -10,13 +10,11 @@ import android.annotation.TargetApi;
import android.content.Intent; import android.content.Intent;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Parcelable;
import android.view.WindowManager; import android.view.WindowManager;
import org.openintents.openpgp.util.OpenPgpApi;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.remote.OpenPgpService; import org.sufficientlysecure.keychain.remote.CryptoInputParcelCacheService;
import org.sufficientlysecure.keychain.service.PassphraseCacheService; import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel; import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
@ -93,7 +91,7 @@ public class NfcOperationActivity extends BaseNfcActivity {
} }
if (mServiceIntent != null) { if (mServiceIntent != null) {
OpenPgpService.cacheCryptoInputParcel(mServiceIntent, inputParcel); CryptoInputParcelCacheService.addCryptoInputParcel(this, mServiceIntent, inputParcel);
setResult(RESULT_OK, mServiceIntent); setResult(RESULT_OK, mServiceIntent);
} else { } else {
Intent result = new Intent(); Intent result = new Intent();

View File

@ -41,7 +41,6 @@ import android.widget.EditText;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import org.openintents.openpgp.util.OpenPgpApi;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround; import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
@ -53,11 +52,10 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.remote.OpenPgpService; import org.sufficientlysecure.keychain.remote.CryptoInputParcelCacheService;
import org.sufficientlysecure.keychain.service.PassphraseCacheService; import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel; import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.RequiredInputType;
import org.sufficientlysecure.keychain.ui.dialog.CustomAlertDialogBuilder; import org.sufficientlysecure.keychain.ui.dialog.CustomAlertDialogBuilder;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Passphrase; import org.sufficientlysecure.keychain.util.Passphrase;
@ -428,7 +426,7 @@ public class PassphraseDialogActivity extends FragmentActivity {
CryptoInputParcel inputParcel = new CryptoInputParcel(null, passphrase); CryptoInputParcel inputParcel = new CryptoInputParcel(null, passphrase);
if (mServiceIntent != null) { if (mServiceIntent != null) {
OpenPgpService.cacheCryptoInputParcel(mServiceIntent, inputParcel); CryptoInputParcelCacheService.addCryptoInputParcel(getActivity(), mServiceIntent, inputParcel);
getActivity().setResult(RESULT_OK, mServiceIntent); getActivity().setResult(RESULT_OK, mServiceIntent);
} else { } else {
// also return passphrase back to activity // also return passphrase back to activity

View File

@ -17,8 +17,6 @@
package org.sufficientlysecure.keychain.util; package org.sufficientlysecure.keychain.util;
import android.content.Intent;
import android.os.Bundle;
import android.os.Parcel; import android.os.Parcel;
import java.util.UUID; import java.util.UUID;
@ -30,7 +28,7 @@ import java.util.concurrent.ConcurrentHashMap;
* To overcome this issue this class allows to cache Parcelables, mapped by unique UUIDs, * To overcome this issue this class allows to cache Parcelables, mapped by unique UUIDs,
* which are written to the parcel instead of the whole Parcelable. * which are written to the parcel instead of the whole Parcelable.
*/ */
public class ParcelableCache<E extends Object> { public class ParcelableCache<E> {
private static final UUID NULL_UUID = new UUID(0, 0); private static final UUID NULL_UUID = new UUID(0, 0);
@ -39,7 +37,7 @@ public class ParcelableCache<E extends Object> {
* This is used such that when we become parceled, we are * This is used such that when we become parceled, we are
* well below the 1 MB boundary that is specified. * well below the 1 MB boundary that is specified.
*/ */
private ConcurrentHashMap<UUID, E> dehydratedLogs = new ConcurrentHashMap<>(); private ConcurrentHashMap<UUID, E> objectCache = new ConcurrentHashMap<>();
/** /**
* Dehydrate a Parcelable (such that it is available after deparcelization) * Dehydrate a Parcelable (such that it is available after deparcelization)
@ -52,9 +50,9 @@ public class ParcelableCache<E extends Object> {
if (parcelable == null) { if (parcelable == null) {
return NULL_UUID; return NULL_UUID;
} else { } else {
UUID ticket = UUID.randomUUID(); UUID uuid = UUID.randomUUID();
dehydratedLogs.put(ticket, parcelable); objectCache.put(uuid, parcelable);
return ticket; return uuid;
} }
} }
@ -63,53 +61,34 @@ public class ParcelableCache<E extends Object> {
* invalidating its place in the dehydration pool. * invalidating its place in the dehydration pool.
* This is used such that when parcelized, the Parcelable is no larger than 1 MB. * This is used such that when parcelized, the Parcelable is no larger than 1 MB.
* *
* @param ticket A UUID ticket that identifies the log in question. * @param uuid A UUID ticket that identifies the log in question.
* @return An OperationLog. * @return An OperationLog.
*/ */
private E rehydrateParcelable(UUID ticket) { private E rehydrateParcelable(UUID uuid) {
// UUID.equals isn't well documented; we use compareTo instead. // UUID.equals isn't well documented; we use compareTo instead.
if (NULL_UUID.compareTo(ticket) == 0) { if (NULL_UUID.compareTo(uuid) == 0) {
return null; return null;
} else { } else {
E parcelable = dehydratedLogs.get(ticket); E parcelable = objectCache.get(uuid);
dehydratedLogs.remove(ticket); objectCache.remove(uuid);
return parcelable; return parcelable;
} }
} }
public E readFromIntentAndGetFromCache(Intent data, String key1, String key2) {
if (!data.getExtras().containsKey(key1) || !data.getExtras().containsKey(key2)) {
return null;
}
long mostSig = data.getLongExtra(key1, 0);
long leastSig = data.getLongExtra(key2, 0);
UUID mTicket = new UUID(mostSig, leastSig);
// fetch the dehydrated log out of storage (this removes it from the dehydration pool)
return rehydrateParcelable(mTicket);
}
public E readFromParcelAndGetFromCache(Parcel source) { public E readFromParcelAndGetFromCache(Parcel source) {
long mostSig = source.readLong(); long mostSig = source.readLong();
long leastSig = source.readLong(); long leastSig = source.readLong();
UUID mTicket = new UUID(mostSig, leastSig); UUID mTicket = new UUID(mostSig, leastSig);
// fetch the dehydrated log out of storage (this removes it from the dehydration pool) // fetch the dehydrated parcelable out of storage (this removes it from the dehydration pool)
return rehydrateParcelable(mTicket); return rehydrateParcelable(mTicket);
} }
public void cacheAndWriteToParcel(E parcelable, Parcel dest) { public void cacheAndWriteToParcel(E parcelable, Parcel dest) {
// Get a ticket for our log. // Get a ticket for our parcelable.
UUID mTicket = dehydrateParcelable(parcelable); UUID mTicket = dehydrateParcelable(parcelable);
// And write out the UUID most and least significant bits. // And write out the UUID most and least significant bits.
dest.writeLong(mTicket.getMostSignificantBits()); dest.writeLong(mTicket.getMostSignificantBits());
dest.writeLong(mTicket.getLeastSignificantBits()); dest.writeLong(mTicket.getLeastSignificantBits());
} }
public void cacheAndWriteToIntent(E parcelable, Intent data, String key1, String key2) {
// Get a ticket for our log.
UUID mTicket = dehydrateParcelable(parcelable);
// And write out the UUID most and least significant bits.
data.putExtra(key1, mTicket.getMostSignificantBits());
data.putExtra(key2, mTicket.getLeastSignificantBits());
}
} }