added a service to handle the caching, this'll ensure the cache works while no activity is around, which is better for k9mail integration

it also is a more efficient and much smarter cache, not requiring an own timer thread, just a service that sleeps must of the time, it also is more accurate in cleaning up the entries, ensuring that the worst case of too late removal is 5 seconds
This commit is contained in:
Thialfihar 2010-06-03 16:17:55 +00:00
parent 371dc31b97
commit 600b44b9fc
5 changed files with 101 additions and 32 deletions

View File

@ -128,6 +128,8 @@
android:label="@string/title_preferences" android:label="@string/title_preferences"
android:configChanges="keyboardHidden|orientation|keyboard"/> android:configChanges="keyboardHidden|orientation|keyboard"/>
<service android:name=".Service" />
<provider <provider
android:readPermission="org.thialfihar.android.apg.permission.READ_KEY_DETAILS" android:readPermission="org.thialfihar.android.apg.permission.READ_KEY_DETAILS"
android:name="org.thialfihar.android.apg.provider.DataProvider" android:name="org.thialfihar.android.apg.provider.DataProvider"

View File

@ -225,19 +225,30 @@ public class Apg {
return cpp.passPhrase; return cpp.passPhrase;
} }
public static void cleanUpCache(int ttl) { public static int cleanUpCache(int ttl, int initialDelay) {
int delay = initialDelay;
long realTtl = ttl * 1000;
long now = new Date().getTime(); long now = new Date().getTime();
Vector<Long> oldKeys = new Vector<Long>(); Vector<Long> oldKeys = new Vector<Long>();
for (Map.Entry<Long, CachedPassPhrase> pair : mPassPhraseCache.entrySet()) { for (Map.Entry<Long, CachedPassPhrase> pair : mPassPhraseCache.entrySet()) {
if ((now - pair.getValue().timestamp) >= 1000 * ttl) { long lived = now - pair.getValue().timestamp;
if (lived >= realTtl) {
oldKeys.add(pair.getKey()); oldKeys.add(pair.getKey());
} else {
// see, whether the remaining time for this cache entry improves our
// check delay
long nextCheck = realTtl - lived + 1000;
if (nextCheck < delay) {
delay = (int)nextCheck;
}
} }
} }
for (long keyId : oldKeys) { for (long keyId : oldKeys) {
mPassPhraseCache.remove(keyId); mPassPhraseCache.remove(keyId);
} }
return delay;
} }
public static PGPSecretKey createKey(Context context, public static PGPSecretKey createKey(Context context,

View File

@ -17,8 +17,6 @@
package org.thialfihar.android.apg; package org.thialfihar.android.apg;
import java.io.File; import java.io.File;
import java.util.Timer;
import java.util.TimerTask;
import org.bouncycastle2.bcpg.HashAlgorithmTags; import org.bouncycastle2.bcpg.HashAlgorithmTags;
import org.bouncycastle2.openpgp.PGPEncryptedData; import org.bouncycastle2.openpgp.PGPEncryptedData;
@ -53,8 +51,6 @@ public class BaseActivity extends Activity
private String mDeleteFile = null; private String mDeleteFile = null;
protected static SharedPreferences mPreferences = null; protected static SharedPreferences mPreferences = null;
private static Timer mCacheTimer = new Timer();
private Handler mHandler = new Handler() { private Handler mHandler = new Handler() {
@Override @Override
public void handleMessage(Message msg) { public void handleMessage(Message msg) {
@ -80,29 +76,9 @@ public class BaseActivity extends Activity
} }
} }
if (mCacheTimer == null) { Intent intent = new Intent(this, Service.class);
setPassPhraseCacheTimer(); intent.putExtra(Service.EXTRA_TTL, getPassPhraseCacheTtl());
} startService(intent);
}
private void setPassPhraseCacheTimer() {
if (mCacheTimer != null) {
mCacheTimer.cancel();
mCacheTimer = null;
}
int ttl = getPassPhraseCacheTtl();
if (ttl == 0) {
// no timer needed
return;
}
// check every ttl/2 seconds, which shouldn't be heavy on the device (even if ttl = 15),
// and makes sure the longest a pass phrase survives int the cache is 1.5 * ttl
mCacheTimer = new Timer();
mCacheTimer.scheduleAtFixedRate(new TimerTask() {
public void run() {
Apg.cleanUpCache(BaseActivity.this.getPassPhraseCacheTtl());
}
}, 0, ttl * 1000 / 2);
} }
@Override @Override
@ -400,7 +376,9 @@ public class BaseActivity extends Activity
editor.putInt(Constants.pref.pass_phrase_cache_ttl, value); editor.putInt(Constants.pref.pass_phrase_cache_ttl, value);
editor.commit(); editor.commit();
setPassPhraseCacheTimer(); Intent intent = new Intent(this, Service.class);
intent.putExtra(Service.EXTRA_TTL, getPassPhraseCacheTtl());
startService(intent);
} }
public int getDefaultEncryptionAlgorithm() { public int getDefaultEncryptionAlgorithm() {

View File

@ -50,7 +50,6 @@ public class PreferencesActivity extends BaseActivity {
new Choice(180, getString(R.string.choice_3mins)), new Choice(180, getString(R.string.choice_3mins)),
new Choice(300, getString(R.string.choice_5mins)), new Choice(300, getString(R.string.choice_5mins)),
new Choice(600, getString(R.string.choice_10mins)), new Choice(600, getString(R.string.choice_10mins)),
new Choice(0, getString(R.string.choice_untilQuit)),
}; };
ArrayAdapter<Choice> adapter = ArrayAdapter<Choice> adapter =
new ArrayAdapter<Choice>(this, android.R.layout.simple_spinner_item, choices); new ArrayAdapter<Choice>(this, android.R.layout.simple_spinner_item, choices);

View File

@ -0,0 +1,79 @@
package org.thialfihar.android.apg;
import android.content.Intent;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
public class Service extends android.app.Service {
private final IBinder mBinder = new LocalBinder();
public static final String EXTRA_TTL = "ttl";
private int mPassPhraseCacheTtl = 15;
private Handler mCacheHandler = new Handler();
private Runnable mCacheTask = new Runnable() {
public void run() {
// TODO: I suppose we could read out the time left until the first cache entry
// expiration, then use that for the timer...
// check every ttl/2 seconds, which shouldn't be heavy on the device (even if ttl = 15),
// and makes sure the longest a pass phrase survives in the cache is 1.5 * ttl
int delay = mPassPhraseCacheTtl * 1000 / 2;
// also make sure the delay is not longer than one minute
if (delay > 60000) {
delay = 60000;
}
delay = Apg.cleanUpCache(mPassPhraseCacheTtl, delay);
// don't check too often, even if we were close
if (delay < 5000) {
delay = 5000;
}
mCacheHandler.postDelayed(this, delay);
}
};
static private boolean mIsRunning = false;
@Override
public void onCreate() {
super.onCreate();
mIsRunning = true;
}
@Override
public void onDestroy() {
super.onDestroy();
mIsRunning = false;
}
@Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
mPassPhraseCacheTtl = intent.getIntExtra(EXTRA_TTL, 15);
if (mPassPhraseCacheTtl < 15) {
mPassPhraseCacheTtl = 15;
}
mCacheHandler.removeCallbacks(mCacheTask);
mCacheHandler.postDelayed(mCacheTask, 1000);
}
static public boolean isRunning() {
return mIsRunning;
}
public class LocalBinder extends Binder {
Service getService() {
return Service.this;
}
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}