aboutsummaryrefslogtreecommitdiffstats
path: root/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java
diff options
context:
space:
mode:
authorAdithya Abraham Philip <adithyaphilip@gmail.com>2015-07-27 14:10:26 +0530
committerAdithya Abraham Philip <adithyaphilip@gmail.com>2015-08-20 21:02:29 +0530
commit1ef6f883e34a08dc916ad7dfabd7f892964aff54 (patch)
tree93abe6f44f6266f8c57e9b1cb1311d7e1af0068a /OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java
parente643e098e2ad1a762e40f4ecbe6f97f729b11e3c (diff)
downloadopen-keychain-1ef6f883e34a08dc916ad7dfabd7f892964aff54.tar.gz
open-keychain-1ef6f883e34a08dc916ad7dfabd7f892964aff54.tar.bz2
open-keychain-1ef6f883e34a08dc916ad7dfabd7f892964aff54.zip
introduced keyserver sync adapter
Diffstat (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java')
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java234
1 files changed, 234 insertions, 0 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java
new file mode 100644
index 000000000..4a7e2942e
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java
@@ -0,0 +1,234 @@
+package org.sufficientlysecure.keychain.service;
+
+import android.accounts.Account;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.AbstractThreadedSyncAdapter;
+import android.content.ContentProviderClient;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SyncResult;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.support.v4.app.NotificationCompat;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
+import org.sufficientlysecure.keychain.operations.ImportOperation;
+import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
+import org.sufficientlysecure.keychain.ui.MainActivity;
+import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
+import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.ParcelableProxy;
+import org.sufficientlysecure.keychain.util.Preferences;
+
+import java.util.ArrayList;
+import java.util.GregorianCalendar;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class KeyserverSyncAdapterService extends Service {
+
+ private static final String ACTION_IGNORE_TOR = "ignore_tor";
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ Log.e("PHILIP", "Sync adapter service starting");
+ switch (intent.getAction()) {
+ case ACTION_IGNORE_TOR: {
+ updateKeysFromKeyserver(this,
+ new CryptoInputParcel(ParcelableProxy.getForNoProxy()));
+ break;
+ }
+ }
+ // TODO: correct flag?
+ return START_NOT_STICKY;
+ }
+
+ private static AtomicBoolean sCancelled = new AtomicBoolean(false);
+
+ private class KeyserverSyncAdapter extends AbstractThreadedSyncAdapter {
+
+ public KeyserverSyncAdapter() {
+ super(KeyserverSyncAdapterService.this, true);
+ }
+
+ @Override
+ public void onPerformSync(Account account, Bundle extras, String authority,
+ ContentProviderClient provider, SyncResult syncResult) {
+ Log.d(Constants.TAG, "Performing a keyserver sync!");
+
+ updateKeysFromKeyserver(KeyserverSyncAdapterService.this, new CryptoInputParcel());
+ }
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return new KeyserverSyncAdapter().getSyncAdapterBinder();
+ }
+
+ public static void updateKeysFromKeyserver(Context context,
+ CryptoInputParcel cryptoInputParcel) {
+ /**
+ * 1. Get keys which have been updated recently and therefore do not need to
+ * be updated now
+ * 2. Get list of all keys and filter out ones that don't need to be updated
+ * 3. Update the remaining keys.
+ * At any time, if the operation is to be cancelled, the sCancelled AtomicBoolean may be set
+ */
+
+ // 1. Get keys which have been updated recently and don't need to updated now
+ final int INDEX_UPDATED_KEYS_MASTER_KEY_ID = 0;
+ final int INDEX_LAST_UPDATED = 1;
+
+ final long TIME_DAY = TimeUnit.DAYS.toSeconds(1);
+ final long CURRENT_TIME = GregorianCalendar.getInstance().get(GregorianCalendar.SECOND);
+ Log.e("PHILIP", "day: " + TIME_DAY);
+ Cursor updatedKeysCursor = context.getContentResolver().query(
+ KeychainContract.UpdatedKeys.CONTENT_URI,
+ new String[]{
+ KeychainContract.UpdatedKeys.MASTER_KEY_ID,
+ KeychainContract.UpdatedKeys.LAST_UPDATED
+ },
+ "? - " + KeychainContract.UpdatedKeys.LAST_UPDATED + " < " + TIME_DAY,
+ new String[]{"" + CURRENT_TIME},
+ null
+ );
+
+ ArrayList<Long> ignoreMasterKeyIds = new ArrayList<>();
+ while (updatedKeysCursor.moveToNext()) {
+ long masterKeyId = updatedKeysCursor.getLong(INDEX_UPDATED_KEYS_MASTER_KEY_ID);
+ Log.d(Constants.TAG, "Keyserver sync: {" + masterKeyId + "} last updated at {"
+ + updatedKeysCursor.getLong(INDEX_LAST_UPDATED) + "}s");
+ ignoreMasterKeyIds.add(masterKeyId);
+ }
+ updatedKeysCursor.close();
+
+ // 2. Make a list of public keys which should be updated
+ final int INDEX_MASTER_KEY_ID = 0;
+ final int INDEX_FINGERPRINT = 1;
+ Cursor keyCursor = context.getContentResolver().query(
+ KeychainContract.KeyRings.buildUnifiedKeyRingsUri(),
+ new String[]{
+ KeychainContract.KeyRings.MASTER_KEY_ID,
+ KeychainContract.KeyRings.FINGERPRINT
+ },
+ null,
+ null,
+ null
+ );
+
+ if (keyCursor == null) {
+ return;
+ }
+
+ ArrayList<ParcelableKeyRing> keyList = new ArrayList<>();
+ while (keyCursor.moveToNext()) {
+ long keyId = keyCursor.getLong(INDEX_MASTER_KEY_ID);
+ if (ignoreMasterKeyIds.contains(keyId)) {
+ continue;
+ }
+ String fingerprint = KeyFormattingUtils
+ .convertFingerprintToHex(keyCursor.getBlob(INDEX_FINGERPRINT));
+ String hexKeyId = KeyFormattingUtils
+ .convertKeyIdToHex(keyId);
+ // we aren't updating from keybase as of now
+ keyList.add(new ParcelableKeyRing(fingerprint, hexKeyId, null));
+ }
+ keyCursor.close();
+
+ if (sCancelled.get()) {
+ // if we've already been cancelled
+ return;
+ }
+
+ // 3. Actually update the keys
+ Log.e("PHILIP", keyList.toString());
+ ImportOperation importOp = new ImportOperation(context, new ProviderHelper(context), null);
+ ImportKeyResult result = importOp.execute(
+ new ImportKeyringParcel(keyList,
+ Preferences.getPreferences(context).getPreferredKeyserver()),
+ cryptoInputParcel
+ );
+ if (result.isPending()) {
+ NotificationManager manager =
+ (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
+ manager.notify(Constants.Notification.KEYSERVER_SYNC_FAIL_ORBOT,
+ getOrbotNoification(context));
+
+ Log.d(Constants.TAG, "Keyserver sync failed due to pending result " +
+ result.getRequiredInputParcel().mType);
+ } else {
+ Log.d(Constants.TAG, "Background keyserver sync completed: new " + result.mNewKeys
+ + " updated " + result.mUpdatedKeys + " bad " + result.mBadKeys);
+ }
+ }
+
+ public static void preventAndCancelUpdates() {
+ // TODO: PHILIP uncomment!
+ // sCancelled.set(true);
+ }
+
+ public static void allowUpdates() {
+ sCancelled.set(false);
+ }
+
+ private static Notification getOrbotNoification(Context context) {
+ // TODO: PHILIP work in progress
+ NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
+ builder.setSmallIcon(R.drawable.ic_stat_notify_24dp)
+ .setLargeIcon(getBitmap(R.drawable.ic_launcher, context))
+ .setContentTitle(context.getString(R.string.keyserver_sync_orbot_notif_title))
+ .setContentText(context.getString(R.string.keyserver_sync_orbot_notif_msg));
+
+ // In case the user decides to not use tor
+ Intent ignoreTorIntent = new Intent(context, KeyserverSyncAdapterService.class);
+ ignoreTorIntent.setAction(ACTION_IGNORE_TOR);
+ PendingIntent ignoreTorPi = PendingIntent.getService(
+ context,
+ Constants.Notification.KEYSERVER_SYNC_FAIL_ORBOT,
+ ignoreTorIntent,
+ PendingIntent.FLAG_CANCEL_CURRENT
+ );
+
+ builder.addAction(R.drawable.abc_ic_clear_mtrl_alpha,
+ context.getString(R.string.keyserver_sync_orbot_notif_ignore),
+ ignoreTorPi);
+
+ return builder.build();
+ }
+
+ // from de.azapps.mirakel.helper.Helpers from https://github.com/MirakelX/mirakel-android
+ private static Bitmap getBitmap(int resId, Context context) {
+ int mLargeIconWidth = (int) context.getResources().getDimension(
+ android.R.dimen.notification_large_icon_width);
+ int mLargeIconHeight = (int) context.getResources().getDimension(
+ android.R.dimen.notification_large_icon_height);
+ Drawable d;
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
+ // noinspection deprecation (can't help it at this api level)
+ d = context.getResources().getDrawable(resId);
+ } else {
+ d = context.getDrawable(resId);
+ }
+ if (d == null) {
+ return null;
+ }
+ Bitmap b = Bitmap.createBitmap(mLargeIconWidth, mLargeIconHeight, Bitmap.Config.ARGB_8888);
+ Canvas c = new Canvas(b);
+ d.setBounds(0, 0, mLargeIconWidth, mLargeIconHeight);
+ d.draw(c);
+ return b;
+ }
+}