2012-03-12 14:28:35 +01:00
|
|
|
/*
|
2012-09-11 19:56:54 +02:00
|
|
|
* Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de>
|
|
|
|
*
|
2012-03-12 14:28:35 +01:00
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2012-06-20 20:06:17 +03:00
|
|
|
package org.thialfihar.android.apg.service;
|
2010-07-16 20:13:12 +00:00
|
|
|
|
2012-09-12 11:50:30 +02:00
|
|
|
import java.util.Date;
|
2012-09-11 19:56:54 +02:00
|
|
|
import java.util.HashMap;
|
|
|
|
|
|
|
|
import org.spongycastle.openpgp.PGPSecretKey;
|
|
|
|
import org.spongycastle.openpgp.PGPSecretKeyRing;
|
|
|
|
import org.thialfihar.android.apg.Constants;
|
|
|
|
import org.thialfihar.android.apg.Id;
|
|
|
|
import org.thialfihar.android.apg.helper.PGPHelper;
|
2012-06-20 15:27:45 +03:00
|
|
|
import org.thialfihar.android.apg.helper.Preferences;
|
2012-09-20 17:13:45 +02:00
|
|
|
import org.thialfihar.android.apg.provider.ProviderHelper;
|
2012-04-25 18:59:51 +02:00
|
|
|
|
2012-09-11 19:56:54 +02:00
|
|
|
import android.app.AlarmManager;
|
|
|
|
import android.app.PendingIntent;
|
2012-04-25 18:59:51 +02:00
|
|
|
import android.app.Service;
|
2012-09-11 19:56:54 +02:00
|
|
|
import android.content.BroadcastReceiver;
|
2012-06-09 03:46:30 +03:00
|
|
|
import android.content.Context;
|
2010-07-16 20:13:12 +00:00
|
|
|
import android.content.Intent;
|
2012-09-11 19:56:54 +02:00
|
|
|
import android.content.IntentFilter;
|
2010-07-16 20:13:12 +00:00
|
|
|
import android.os.Binder;
|
|
|
|
import android.os.IBinder;
|
2012-09-11 19:56:54 +02:00
|
|
|
import android.util.Log;
|
2010-07-16 20:13:12 +00:00
|
|
|
|
2012-04-25 18:59:51 +02:00
|
|
|
public class PassphraseCacheService extends Service {
|
2012-09-12 11:50:30 +02:00
|
|
|
public static final String TAG = Constants.TAG + ": PassphraseCacheService";
|
|
|
|
|
2012-09-11 19:56:54 +02:00
|
|
|
public static final String BROADCAST_ACTION_PASSPHRASE_CACHE_SERVICE = Constants.INTENT_PREFIX
|
|
|
|
+ "PASSPHRASE_CACHE_SERVICE";
|
2010-07-16 20:13:12 +00:00
|
|
|
|
|
|
|
public static final String EXTRA_TTL = "ttl";
|
2012-09-11 19:56:54 +02:00
|
|
|
public static final String EXTRA_KEY_ID = "keyId";
|
|
|
|
public static final String EXTRA_PASSPHRASE = "passphrase";
|
|
|
|
|
|
|
|
private static final int REQUEST_ID = 0;
|
|
|
|
private static final long DEFAULT_TTL = 15;
|
|
|
|
|
|
|
|
private BroadcastReceiver mIntentReceiver;
|
|
|
|
|
2012-09-12 13:51:48 +02:00
|
|
|
// This is static to be easily retrieved by getCachedPassphrase() without the need of callback
|
|
|
|
// functions
|
2012-09-12 11:50:30 +02:00
|
|
|
private static HashMap<Long, String> mPassphraseCache = new HashMap<Long, String>();
|
2012-09-11 19:56:54 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* This caches a new passphrase by sending a new command to the service. An android service is
|
2012-09-12 13:51:48 +02:00
|
|
|
* only run once. Thus, when the service is already started, new commands just add new events to
|
|
|
|
* the alarm manager for new passphrases to let them timeout in the future.
|
2012-09-11 19:56:54 +02:00
|
|
|
*
|
|
|
|
* @param context
|
|
|
|
* @param keyId
|
|
|
|
* @param passphrase
|
|
|
|
*/
|
|
|
|
public static void addCachedPassphrase(Context context, long keyId, String passphrase) {
|
2012-09-12 11:50:30 +02:00
|
|
|
Log.d(TAG, "cacheNewPassphrase() for " + keyId);
|
2010-07-16 20:13:12 +00:00
|
|
|
|
2012-06-09 03:46:30 +03:00
|
|
|
Intent intent = new Intent(context, PassphraseCacheService.class);
|
2012-09-11 19:56:54 +02:00
|
|
|
intent.putExtra(EXTRA_TTL, Preferences.getPreferences(context).getPassPhraseCacheTtl());
|
|
|
|
intent.putExtra(EXTRA_PASSPHRASE, passphrase);
|
|
|
|
intent.putExtra(EXTRA_KEY_ID, keyId);
|
|
|
|
|
2012-06-09 03:46:30 +03:00
|
|
|
context.startService(intent);
|
|
|
|
}
|
|
|
|
|
2012-09-11 19:56:54 +02:00
|
|
|
/**
|
|
|
|
* Gets a cached passphrase from memory
|
|
|
|
*
|
|
|
|
* @param context
|
|
|
|
* @param keyId
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
public static String getCachedPassphrase(Context context, long keyId) {
|
2012-11-14 16:02:11 +01:00
|
|
|
// try to get master key id which is used as an identifier for cached passphrases
|
|
|
|
long masterKeyId = keyId;
|
|
|
|
if (masterKeyId != Id.key.symmetric) {
|
|
|
|
PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(context, keyId);
|
2012-09-11 19:56:54 +02:00
|
|
|
if (keyRing == null) {
|
|
|
|
return null;
|
2010-07-16 20:13:12 +00:00
|
|
|
}
|
2012-09-11 19:56:54 +02:00
|
|
|
PGPSecretKey masterKey = PGPHelper.getMasterKey(keyRing);
|
|
|
|
if (masterKey == null) {
|
|
|
|
return null;
|
2010-07-16 20:13:12 +00:00
|
|
|
}
|
2012-11-14 16:02:11 +01:00
|
|
|
masterKeyId = masterKey.getKeyID();
|
2012-09-11 19:56:54 +02:00
|
|
|
}
|
2010-07-16 20:13:12 +00:00
|
|
|
|
2012-09-11 19:56:54 +02:00
|
|
|
// get cached passphrase
|
2012-11-14 16:02:11 +01:00
|
|
|
String cachedPassphrase = mPassphraseCache.get(masterKeyId);
|
2012-09-12 11:50:30 +02:00
|
|
|
if (cachedPassphrase == null) {
|
2012-09-11 19:56:54 +02:00
|
|
|
return null;
|
2010-07-16 20:13:12 +00:00
|
|
|
}
|
2012-09-11 19:56:54 +02:00
|
|
|
// set it again to reset the cache life cycle
|
2012-09-12 11:50:30 +02:00
|
|
|
Log.d(TAG, "Cache passphrase again when getting it!");
|
2012-11-14 16:02:11 +01:00
|
|
|
addCachedPassphrase(context, masterKeyId, cachedPassphrase);
|
2010-07-16 20:13:12 +00:00
|
|
|
|
2012-09-12 11:50:30 +02:00
|
|
|
return cachedPassphrase;
|
2012-09-11 19:56:54 +02:00
|
|
|
}
|
2010-07-16 20:13:12 +00:00
|
|
|
|
2012-09-11 19:56:54 +02:00
|
|
|
/**
|
|
|
|
* Register BroadcastReceiver that is unregistered when service is destroyed. This
|
2012-09-12 13:51:48 +02:00
|
|
|
* BroadcastReceiver hears on intents with ACTION_PASSPHRASE_CACHE_SERVICE to then timeout
|
|
|
|
* specific passphrases in memory.
|
2012-09-11 19:56:54 +02:00
|
|
|
*/
|
|
|
|
private void registerReceiver() {
|
|
|
|
if (mIntentReceiver == null) {
|
|
|
|
mIntentReceiver = new BroadcastReceiver() {
|
|
|
|
@Override
|
|
|
|
public void onReceive(Context context, Intent intent) {
|
|
|
|
String action = intent.getAction();
|
|
|
|
|
2012-09-12 11:50:30 +02:00
|
|
|
Log.d(TAG, "Received broadcast...");
|
|
|
|
|
2012-09-11 19:56:54 +02:00
|
|
|
if (action.equals(BROADCAST_ACTION_PASSPHRASE_CACHE_SERVICE)) {
|
|
|
|
long keyId = intent.getLongExtra(EXTRA_KEY_ID, -1);
|
|
|
|
timeout(context, keyId);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
IntentFilter filter = new IntentFilter();
|
|
|
|
filter.addAction(BROADCAST_ACTION_PASSPHRASE_CACHE_SERVICE);
|
2012-09-12 11:50:30 +02:00
|
|
|
registerReceiver(mIntentReceiver, filter);
|
2012-09-11 19:56:54 +02:00
|
|
|
}
|
|
|
|
}
|
2010-07-16 20:13:12 +00:00
|
|
|
|
2012-09-11 19:56:54 +02:00
|
|
|
/**
|
2012-09-12 13:51:48 +02:00
|
|
|
* Build pending intent that is executed by alarm manager to time out a specific passphrase
|
2012-09-11 19:56:54 +02:00
|
|
|
*
|
|
|
|
* @param context
|
|
|
|
* @param keyId
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
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,
|
|
|
|
PendingIntent.FLAG_CANCEL_CURRENT);
|
|
|
|
|
|
|
|
return sender;
|
2010-07-16 20:13:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2012-09-11 19:56:54 +02:00
|
|
|
public void onCreate() {
|
2012-09-12 11:50:30 +02:00
|
|
|
Log.d(TAG, "onCreate()");
|
2010-07-16 20:13:12 +00:00
|
|
|
}
|
|
|
|
|
2012-09-11 19:56:54 +02:00
|
|
|
/**
|
|
|
|
* Executed when service is started by intent
|
|
|
|
*/
|
2010-07-16 20:13:12 +00:00
|
|
|
@Override
|
2012-09-11 19:56:54 +02:00
|
|
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
2012-09-12 11:50:30 +02:00
|
|
|
Log.d(TAG, "onStartCommand()");
|
2012-09-11 19:56:54 +02:00
|
|
|
|
|
|
|
// register broadcastreceiver
|
|
|
|
registerReceiver();
|
2010-07-16 20:13:12 +00:00
|
|
|
|
|
|
|
if (intent != null) {
|
2012-09-11 19:56:54 +02:00
|
|
|
long ttl = intent.getLongExtra(EXTRA_TTL, DEFAULT_TTL);
|
|
|
|
long keyId = intent.getLongExtra(EXTRA_KEY_ID, -1);
|
|
|
|
String passphrase = intent.getStringExtra(EXTRA_PASSPHRASE);
|
|
|
|
|
2012-09-12 11:50:30 +02:00
|
|
|
Log.d(TAG, "Received intent in onStartCommand() with keyId: " + keyId + ", ttl: " + ttl);
|
2012-09-11 19:56:54 +02:00
|
|
|
|
|
|
|
// add keyId and passphrase to memory
|
2012-09-12 11:50:30 +02:00
|
|
|
mPassphraseCache.put(keyId, passphrase);
|
2012-09-11 19:56:54 +02:00
|
|
|
|
|
|
|
// register new alarm with keyId for this passphrase
|
2012-09-12 11:50:30 +02:00
|
|
|
long triggerTime = new Date().getTime() + (ttl * 1000);
|
2012-09-11 19:56:54 +02:00
|
|
|
AlarmManager am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
|
|
|
|
am.set(AlarmManager.RTC_WAKEUP, triggerTime, buildIntent(this, keyId));
|
2010-07-16 20:13:12 +00:00
|
|
|
}
|
2012-09-11 19:56:54 +02:00
|
|
|
|
|
|
|
return START_STICKY;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Called when one specific passphrase for keyId timed out
|
|
|
|
*
|
|
|
|
* @param context
|
|
|
|
* @param keyId
|
|
|
|
*/
|
|
|
|
private void timeout(Context context, long keyId) {
|
|
|
|
// remove passphrase corresponding to keyId from memory
|
|
|
|
mPassphraseCache.remove(keyId);
|
|
|
|
|
2012-09-12 11:50:30 +02:00
|
|
|
Log.d(TAG, "Timeout of " + keyId + ", removed from memory!");
|
|
|
|
|
2012-09-11 19:56:54 +02:00
|
|
|
// stop whole service if no cached passphrases remaining
|
|
|
|
if (mPassphraseCache.isEmpty()) {
|
2012-09-12 11:50:30 +02:00
|
|
|
Log.d(TAG, "No passphrases remaining in memory, stopping service!");
|
2012-09-11 19:56:54 +02:00
|
|
|
stopSelf();
|
2010-07-16 20:13:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-11 19:56:54 +02:00
|
|
|
@Override
|
|
|
|
public void onDestroy() {
|
2012-09-12 11:50:30 +02:00
|
|
|
Log.d(TAG, "onDestroy()");
|
2012-09-11 19:56:54 +02:00
|
|
|
|
2012-09-12 11:50:30 +02:00
|
|
|
unregisterReceiver(mIntentReceiver);
|
2010-07-16 20:13:12 +00:00
|
|
|
}
|
|
|
|
|
2012-09-11 19:56:54 +02:00
|
|
|
public class PassphraseCacheBinder extends Binder {
|
|
|
|
public PassphraseCacheService getService() {
|
2012-04-25 18:59:51 +02:00
|
|
|
return PassphraseCacheService.this;
|
2010-07-16 20:13:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-11 19:56:54 +02:00
|
|
|
private final IBinder mBinder = new PassphraseCacheBinder();
|
|
|
|
|
2010-07-16 20:13:12 +00:00
|
|
|
@Override
|
|
|
|
public IBinder onBind(Intent intent) {
|
|
|
|
return mBinder;
|
|
|
|
}
|
2012-09-11 19:56:54 +02:00
|
|
|
|
|
|
|
}
|