diff options
Diffstat (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java')
-rw-r--r-- | OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java | 176 |
1 files changed, 148 insertions, 28 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java index 28f230f71..c4ecfdec5 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java @@ -18,6 +18,7 @@ package org.sufficientlysecure.keychain.service; import android.app.AlarmManager; +import android.app.Notification; import android.app.PendingIntent; import android.app.Service; import android.content.BroadcastReceiver; @@ -25,6 +26,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Binder; +import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; @@ -32,11 +34,15 @@ import android.os.IBinder; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; + import android.support.v4.util.LongSparseArray; +import android.support.v4.app.NotificationCompat; import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.helper.Preferences; -import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing; +import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing; +import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.util.Log; @@ -54,6 +60,8 @@ public class PassphraseCacheService extends Service { + "PASSPHRASE_CACHE_ADD"; public static final String ACTION_PASSPHRASE_CACHE_GET = Constants.INTENT_PREFIX + "PASSPHRASE_CACHE_GET"; + public static final String ACTION_PASSPHRASE_CACHE_CLEAR = Constants.INTENT_PREFIX + + "PASSPHRASE_CACHE_CLEAR"; public static final String BROADCAST_ACTION_PASSPHRASE_CACHE_SERVICE = Constants.INTENT_PREFIX + "PASSPHRASE_CACHE_BROADCAST"; @@ -62,13 +70,16 @@ public class PassphraseCacheService extends Service { public static final String EXTRA_KEY_ID = "key_id"; public static final String EXTRA_PASSPHRASE = "passphrase"; public static final String EXTRA_MESSENGER = "messenger"; + public static final String EXTRA_USER_ID = "user_id"; private static final int REQUEST_ID = 0; private static final long DEFAULT_TTL = 15; + private static final int NOTIFICATION_ID = 1; + private BroadcastReceiver mIntentReceiver; - private LongSparseArray<String> mPassphraseCache = new LongSparseArray<String>(); + private LongSparseArray<CachedPassphrase> mPassphraseCache = new LongSparseArray<CachedPassphrase>(); Context mContext; @@ -81,14 +92,17 @@ public class PassphraseCacheService extends Service { * @param keyId * @param passphrase */ - public static void addCachedPassphrase(Context context, long keyId, String passphrase) { + public static void addCachedPassphrase(Context context, long keyId, String passphrase, + String primaryUserId) { Log.d(Constants.TAG, "PassphraseCacheService.cacheNewPassphrase() for " + keyId); Intent intent = new Intent(context, PassphraseCacheService.class); intent.setAction(ACTION_PASSPHRASE_CACHE_ADD); + intent.putExtra(EXTRA_TTL, Preferences.getPreferences(context).getPassphraseCacheTtl()); intent.putExtra(EXTRA_PASSPHRASE, passphrase); intent.putExtra(EXTRA_KEY_ID, keyId); + intent.putExtra(EXTRA_USER_ID, primaryUserId); context.startService(intent); } @@ -159,42 +173,46 @@ public class PassphraseCacheService extends Service { // passphrase for symmetric encryption? if (keyId == Constants.key.symmetric) { Log.d(Constants.TAG, "PassphraseCacheService.getCachedPassphraseImpl() for symmetric encryption"); - String cachedPassphrase = mPassphraseCache.get(Constants.key.symmetric); + String cachedPassphrase = mPassphraseCache.get(Constants.key.symmetric).getPassphrase(); if (cachedPassphrase == null) { return null; } - addCachedPassphrase(this, Constants.key.symmetric, cachedPassphrase); + addCachedPassphrase(this, Constants.key.symmetric, cachedPassphrase, getString(R.string.passp_cache_notif_pwd)); return cachedPassphrase; } // try to get master key id which is used as an identifier for cached passphrases try { Log.d(Constants.TAG, "PassphraseCacheService.getCachedPassphraseImpl() for masterKeyId " + keyId); - WrappedSecretKeyRing key = new ProviderHelper(this).getWrappedSecretKeyRing( + CanonicalizedSecretKeyRing key = new ProviderHelper(this).getCanonicalizedSecretKeyRing( KeychainContract.KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(keyId)); // no passphrase needed? just add empty string and return it, then if (!key.hasPassphrase()) { Log.d(Constants.TAG, "Key has no passphrase! Caches and returns empty passphrase!"); - addCachedPassphrase(this, keyId, ""); + try { + addCachedPassphrase(this, keyId, "", key.getPrimaryUserIdWithFallback()); + } catch (PgpGeneralException e) { + Log.d(Constants.TAG, "PgpGeneralException occured"); + } return ""; } // get cached passphrase - String cachedPassphrase = mPassphraseCache.get(keyId); + CachedPassphrase cachedPassphrase = mPassphraseCache.get(keyId); if (cachedPassphrase == null) { - Log.d(Constants.TAG, "PassphraseCacheService Passphrase not (yet) cached, returning null"); + Log.d(Constants.TAG, "PassphraseCacheService: Passphrase not (yet) cached, returning null"); // not really an error, just means the passphrase is not cached but not empty either return null; } // set it again to reset the cache life cycle - Log.d(Constants.TAG, "PassphraseCacheService Cache passphrase again when getting it!"); - addCachedPassphrase(this, keyId, cachedPassphrase); - return cachedPassphrase; + Log.d(Constants.TAG, "PassphraseCacheService: Cache passphrase again when getting it!"); + addCachedPassphrase(this, keyId, cachedPassphrase.getPassphrase(), cachedPassphrase.getPrimaryUserID()); + return cachedPassphrase.getPassphrase(); } catch (ProviderHelper.NotFoundException e) { - Log.e(Constants.TAG, "PassphraseCacheService Passphrase for unknown key was requested!"); + Log.e(Constants.TAG, "PassphraseCacheService: Passphrase for unknown key was requested!"); return null; } } @@ -211,7 +229,7 @@ public class PassphraseCacheService extends Service { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); - Log.d(Constants.TAG, "PassphraseCacheService Received broadcast..."); + Log.d(Constants.TAG, "PassphraseCacheService: Received broadcast..."); if (action.equals(BROADCAST_ACTION_PASSPHRASE_CACHE_SERVICE)) { long keyId = intent.getLongExtra(EXTRA_KEY_ID, -1); @@ -236,10 +254,8 @@ public class PassphraseCacheService extends Service { private static PendingIntent buildIntent(Context context, long keyId) { Intent intent = new Intent(BROADCAST_ACTION_PASSPHRASE_CACHE_SERVICE); intent.putExtra(EXTRA_KEY_ID, keyId); - PendingIntent sender = PendingIntent.getBroadcast(context, REQUEST_ID, intent, + return PendingIntent.getBroadcast(context, REQUEST_ID, intent, PendingIntent.FLAG_CANCEL_CURRENT); - - return sender; } /** @@ -256,14 +272,17 @@ public class PassphraseCacheService extends Service { if (ACTION_PASSPHRASE_CACHE_ADD.equals(intent.getAction())) { long ttl = intent.getLongExtra(EXTRA_TTL, DEFAULT_TTL); long keyId = intent.getLongExtra(EXTRA_KEY_ID, -1); + String passphrase = intent.getStringExtra(EXTRA_PASSPHRASE); + String primaryUserID = intent.getStringExtra(EXTRA_USER_ID); Log.d(Constants.TAG, - "PassphraseCacheService Received ACTION_PASSPHRASE_CACHE_ADD intent in onStartCommand() with keyId: " - + keyId + ", ttl: " + ttl); + "PassphraseCacheService: Received ACTION_PASSPHRASE_CACHE_ADD intent in onStartCommand() with keyId: " + + keyId + ", ttl: " + ttl + ", usrId: " + primaryUserID + ); - // add keyId and passphrase to memory - mPassphraseCache.put(keyId, passphrase); + // add keyId, passphrase and primary user id to memory + mPassphraseCache.put(keyId, new CachedPassphrase(passphrase, primaryUserID)); if (ttl > 0) { // register new alarm with keyId for this passphrase @@ -271,6 +290,8 @@ public class PassphraseCacheService extends Service { AlarmManager am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE); am.set(AlarmManager.RTC_WAKEUP, triggerTime, buildIntent(this, keyId)); } + + updateService(); } else if (ACTION_PASSPHRASE_CACHE_GET.equals(intent.getAction())) { long keyId = intent.getLongExtra(EXTRA_KEY_ID, -1); Messenger messenger = intent.getParcelableExtra(EXTRA_MESSENGER); @@ -284,10 +305,21 @@ public class PassphraseCacheService extends Service { try { messenger.send(msg); } 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())) { + AlarmManager am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE); + + // Stop all ttl alarms + for (int i = 0; i < mPassphraseCache.size(); i++) { + am.cancel(buildIntent(this, mPassphraseCache.keyAt(i))); + } + + mPassphraseCache.clear(); + + updateService(); } else { - Log.e(Constants.TAG, "PassphraseCacheService Intent or Intent Action not supported!"); + Log.e(Constants.TAG, "PassphraseCacheService: Intent or Intent Action not supported!"); } } @@ -306,11 +338,74 @@ public class PassphraseCacheService extends Service { Log.d(Constants.TAG, "PassphraseCacheService Timeout of keyId " + keyId + ", removed from memory!"); - // stop whole service if no cached passphrases remaining - if (mPassphraseCache.size() == 0) { - Log.d(Constants.TAG, "PassphraseCacheServic No passphrases remaining in memory, stopping service!"); - stopSelf(); + updateService(); + } + + private void updateService() { + if (mPassphraseCache.size() > 0) { + startForeground(NOTIFICATION_ID, getNotification()); + } else { + // stop whole service if no cached passphrases remaining + Log.d(Constants.TAG, "PassphraseCacheService: No passphrases remaining in memory, stopping service!"); + stopForeground(true); + } + } + + private Notification getNotification() { + NotificationCompat.Builder builder = new NotificationCompat.Builder(this); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + builder.setSmallIcon(R.drawable.ic_launcher) + .setContentTitle(getString(R.string.app_name)) + .setContentText(String.format(getString(R.string.passp_cache_notif_n_keys), + mPassphraseCache.size())); + + NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle(); + + inboxStyle.setBigContentTitle(getString(R.string.passp_cache_notif_keys)); + + // Moves events into the big view + for (int i = 0; i < mPassphraseCache.size(); i++) { + inboxStyle.addLine(mPassphraseCache.valueAt(i).getPrimaryUserID()); + } + + // Moves the big view style object into the notification object. + builder.setStyle(inboxStyle); + + // Add purging action + Intent intent = new Intent(getApplicationContext(), PassphraseCacheService.class); + intent.setAction(ACTION_PASSPHRASE_CACHE_CLEAR); + builder.addAction( + R.drawable.abc_ic_clear_normal, + getString(R.string.passp_cache_notif_clear), + PendingIntent.getService( + getApplicationContext(), + 0, + intent, + PendingIntent.FLAG_UPDATE_CURRENT + ) + ); + } else { + // Fallback, since expandable notifications weren't available back then + builder.setSmallIcon(R.drawable.ic_launcher) + .setContentTitle(String.format(getString(R.string.passp_cache_notif_n_keys, + mPassphraseCache.size()))) + .setContentText(getString(R.string.passp_cache_notif_click_to_clear)); + + Intent intent = new Intent(getApplicationContext(), PassphraseCacheService.class); + intent.setAction(ACTION_PASSPHRASE_CACHE_CLEAR); + + builder.setContentIntent( + PendingIntent.getService( + getApplicationContext(), + 0, + intent, + PendingIntent.FLAG_UPDATE_CURRENT + ) + ); } + + return builder.build(); } @Override @@ -341,4 +436,29 @@ public class PassphraseCacheService extends Service { private final IBinder mBinder = new PassphraseCacheBinder(); -} + public class CachedPassphrase { + private String primaryUserID; + private String passphrase; + + public CachedPassphrase(String passphrase, String primaryUserID) { + setPassphrase(passphrase); + setPrimaryUserID(primaryUserID); + } + + public String getPrimaryUserID() { + return primaryUserID; + } + + public String getPassphrase() { + return passphrase; + } + + public void setPrimaryUserID(String primaryUserID) { + this.primaryUserID = primaryUserID; + } + + public void setPassphrase(String passphrase) { + this.passphrase = passphrase; + } + } +}
\ No newline at end of file |