From 7c67f7a7154cc4da2c53aeb338d9bff2cbc3985a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Tue, 9 Sep 2014 09:26:03 +0200 Subject: Consolidate on database upgrade --- .../keychain/KeychainApplication.java | 2 +- .../keychain/provider/KeychainDatabase.java | 22 ++- .../keychain/provider/ProviderHelper.java | 18 +- .../keychain/service/KeychainIntentService.java | 6 +- .../keychain/ui/ConsolidateDialogActivity.java | 10 +- .../keychain/ui/ImportKeysActivity.java | 8 +- .../keychain/util/FileImportCache.java | 200 -------------------- .../keychain/util/ParcelableFileCache.java | 207 +++++++++++++++++++++ 8 files changed, 249 insertions(+), 224 deletions(-) delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FileImportCache.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ParcelableFileCache.java (limited to 'OpenKeychain/src/main/java/org') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java index b435b886d..67b172200 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java @@ -104,8 +104,8 @@ public class KeychainApplication extends Application { */ public void checkConsolidateRecovery() { if (Preferences.getPreferences(this).getCachedConsolidate()) { - // do something which calls ProviderHelper.consolidateDatabaseStep2 with a progressable Intent consolidateIntent = new Intent(this, ConsolidateDialogActivity.class); + consolidateIntent.putExtra(ConsolidateDialogActivity.EXTRA_CONSOLIDATE_RECOVERY, true); consolidateIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(consolidateIntent); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java index 0bb43d47f..4d62f67a9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java @@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.provider; import android.content.Context; +import android.content.Intent; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; @@ -33,6 +34,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.CertsColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingsColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.KeysColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.UserIdsColumns; +import org.sufficientlysecure.keychain.ui.ConsolidateDialogActivity; import org.sufficientlysecure.keychain.util.Log; import java.io.File; @@ -52,8 +54,9 @@ import java.io.IOException; */ public class KeychainDatabase extends SQLiteOpenHelper { private static final String DATABASE_NAME = "openkeychain.db"; - private static final int DATABASE_VERSION = 3; + private static final int DATABASE_VERSION = 4; static Boolean apgHack = false; + private Context mContext; public interface Tables { String KEY_RINGS_PUBLIC = "keyrings_public"; @@ -164,6 +167,7 @@ public class KeychainDatabase extends SQLiteOpenHelper { KeychainDatabase(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); + mContext = context; // make sure this is only done once, on the first instance! boolean iAmIt = false; @@ -203,21 +207,35 @@ public class KeychainDatabase extends SQLiteOpenHelper { @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - // add has_secret for all who are upgrading from a beta version + Log.d(Constants.TAG, "Upgrading db from " + oldVersion + " to " + newVersion); + switch (oldVersion) { case 1: + // add has_secret for all who are upgrading from a beta version try { db.execSQL("ALTER TABLE keys ADD COLUMN has_secret BOOLEAN"); } catch(Exception e){ // never mind, the column probably already existed } + // fall through case 2: + // ECC support try { db.execSQL("ALTER TABLE keys ADD COLUMN " + KeysColumns.KEY_CURVE_OID + " TEXT"); } catch(Exception e){ // never mind, the column probably already existed } + // fall through + case 3: + // better s2k detection, we need consolidate + // fall through } + + // always do consolidate after upgrade + Intent consolidateIntent = new Intent(mContext.getApplicationContext(), ConsolidateDialogActivity.class); + consolidateIntent.putExtra(ConsolidateDialogActivity.EXTRA_CONSOLIDATE_RECOVERY, false); + consolidateIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + mContext.getApplicationContext().startActivity(consolidateIntent); } /** This method tries to import data from a provided database. diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index 186a0fc88..af7bdb139 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -59,7 +59,7 @@ import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType; import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog; import org.sufficientlysecure.keychain.service.OperationResults.ConsolidateResult; import org.sufficientlysecure.keychain.service.OperationResults.SaveKeyringResult; -import org.sufficientlysecure.keychain.util.FileImportCache; +import org.sufficientlysecure.keychain.util.ParcelableFileCache; import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.ProgressFixedScaler; @@ -889,8 +889,8 @@ public class ProviderHelper { Preferences.getPreferences(mContext).setCachedConsolidateNumSecrets(cursor.getCount()); - FileImportCache cache = - new FileImportCache(mContext, "consolidate_secret.pcl"); + ParcelableFileCache cache = + new ParcelableFileCache(mContext, "consolidate_secret.pcl"); cache.writeCache(new Iterator() { ParcelableKeyRing ring; @@ -951,8 +951,8 @@ public class ProviderHelper { Preferences.getPreferences(mContext).setCachedConsolidateNumPublics(cursor.getCount()); - FileImportCache cache = - new FileImportCache(mContext, "consolidate_public.pcl"); + ParcelableFileCache cache = + new ParcelableFileCache(mContext, "consolidate_public.pcl"); cache.writeCache(new Iterator() { ParcelableKeyRing ring; @@ -1041,10 +1041,10 @@ public class ProviderHelper { log(LogLevel.DEBUG, LogType.MSG_CON_DB_CLEAR); mContentResolver.delete(KeyRings.buildUnifiedKeyRingsUri(), null, null); - FileImportCache cacheSecret = - new FileImportCache(mContext, "consolidate_secret.pcl"); - FileImportCache cachePublic = - new FileImportCache(mContext, "consolidate_public.pcl"); + ParcelableFileCache cacheSecret = + new ParcelableFileCache(mContext, "consolidate_secret.pcl"); + ParcelableFileCache cachePublic = + new ParcelableFileCache(mContext, "consolidate_public.pcl"); // 3. Re-Import secret keyrings from cache if (numSecrets > 0) try { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java index b9746aee5..08f51d692 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -61,7 +61,7 @@ import org.sufficientlysecure.keychain.service.OperationResults.ConsolidateResul import org.sufficientlysecure.keychain.service.OperationResults.EditKeyResult; import org.sufficientlysecure.keychain.service.OperationResults.ImportKeyResult; import org.sufficientlysecure.keychain.service.OperationResults.SaveKeyringResult; -import org.sufficientlysecure.keychain.util.FileImportCache; +import org.sufficientlysecure.keychain.util.ParcelableFileCache; import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.ProgressScaler; @@ -512,8 +512,8 @@ public class KeychainIntentService extends IntentService implements Progressable entries = data.getParcelableArrayList(IMPORT_KEY_LIST); } else { // get entries from cached file - FileImportCache cache = - new FileImportCache(this, "key_import.pcl"); + ParcelableFileCache cache = + new ParcelableFileCache(this, "key_import.pcl"); entries = cache.readCacheIntoList(); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ConsolidateDialogActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ConsolidateDialogActivity.java index edc05e28d..0d8905c5b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ConsolidateDialogActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ConsolidateDialogActivity.java @@ -34,17 +34,20 @@ import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; */ public class ConsolidateDialogActivity extends FragmentActivity { + public static final String EXTRA_CONSOLIDATE_RECOVERY = "consolidate_recovery"; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // this activity itself has no content view (see manifest) - consolidateRecovery(); + boolean recovery = getIntent().getBooleanExtra(EXTRA_CONSOLIDATE_RECOVERY, false); + consolidateRecovery(recovery); } - private void consolidateRecovery() { + private void consolidateRecovery(boolean recovery) { // Message is received after importing is done in KeychainIntentService KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler( this, @@ -81,7 +84,7 @@ public class ConsolidateDialogActivity extends FragmentActivity { // fill values for this action Bundle data = new Bundle(); - data.putBoolean(KeychainIntentService.CONSOLIDATE_RECOVERY, true); + data.putBoolean(KeychainIntentService.CONSOLIDATE_RECOVERY, recovery); intent.putExtra(KeychainIntentService.EXTRA_DATA, data); // Create a new Messenger for the communication back @@ -93,7 +96,6 @@ public class ConsolidateDialogActivity extends FragmentActivity { // start service with intent startService(intent); - } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java index f74e4fdd6..3e949cfae 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -19,8 +19,6 @@ package org.sufficientlysecure.keychain.ui; import android.annotation.TargetApi; import android.app.ProgressDialog; -import android.content.DialogInterface; -import android.content.DialogInterface.OnCancelListener; import android.content.Intent; import android.net.Uri; import android.nfc.NdefMessage; @@ -52,7 +50,7 @@ import org.sufficientlysecure.keychain.service.OperationResultParcel; import org.sufficientlysecure.keychain.service.OperationResults.ImportKeyResult; import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter; import org.sufficientlysecure.keychain.ui.widget.SlidingTabLayout; -import org.sufficientlysecure.keychain.util.FileImportCache; +import org.sufficientlysecure.keychain.util.ParcelableFileCache; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Notify; @@ -505,8 +503,8 @@ public class ImportKeysActivity extends ActionBarActivity { // to prevent Java Binder problems on heavy imports // read FileImportCache for more info. try { - FileImportCache cache = - new FileImportCache(this, "key_import.pcl"); + ParcelableFileCache cache = + new ParcelableFileCache(this, "key_import.pcl"); cache.writeCache(selectedEntries); intent.putExtra(KeychainIntentService.EXTRA_DATA, data); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FileImportCache.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FileImportCache.java deleted file mode 100644 index 09275fc95..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FileImportCache.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright (C) 2014 Dominik Schürmann - * - * 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 . - */ - -package org.sufficientlysecure.keychain.util; - -import android.content.Context; -import android.os.Parcel; -import android.os.Parcelable; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.KeychainApplication; - -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.EOFException; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -/** - * When sending large data (over 1MB) through Androids Binder IPC you get - * JavaBinder E !!! FAILED BINDER TRANSACTION !!! - *

- * To overcome this problem, we cache large Parcelables into a file in our private cache directory - * instead of sending them through IPC. - */ -public class FileImportCache { - - private Context mContext; - - private final String mFilename; - - public FileImportCache(Context context, String filename) { - mContext = context; - mFilename = filename; - } - - public void writeCache(ArrayList selectedEntries) throws IOException { - writeCache(selectedEntries.iterator()); - } - - public void writeCache(Iterator it) throws IOException { - - File cacheDir = mContext.getCacheDir(); - if (cacheDir == null) { - // https://groups.google.com/forum/#!topic/android-developers/-694j87eXVU - throw new IOException("cache dir is null!"); - } - - File tempFile = new File(mContext.getCacheDir(), mFilename); - - DataOutputStream oos = new DataOutputStream(new FileOutputStream(tempFile)); - - while (it.hasNext()) { - Parcel p = Parcel.obtain(); // creating empty parcel object - p.writeParcelable(it.next(), 0); // saving bundle as parcel - byte[] buf = p.marshall(); - oos.writeInt(buf.length); - oos.write(buf); - p.recycle(); - } - - oos.close(); - - } - - public List readCacheIntoList() throws IOException { - ArrayList result = new ArrayList(); - Iterator it = readCache(); - while (it.hasNext()) { - result.add(it.next()); - } - return result; - } - - public Iterator readCache() throws IOException { - return readCache(true); - } - - public Iterator readCache(final boolean deleteAfterRead) throws IOException { - - File cacheDir = mContext.getCacheDir(); - if (cacheDir == null) { - // https://groups.google.com/forum/#!topic/android-developers/-694j87eXVU - throw new IOException("cache dir is null!"); - } - - final File tempFile = new File(cacheDir, mFilename); - final DataInputStream ois = new DataInputStream(new FileInputStream(tempFile)); - - return new Iterator() { - - E mRing = null; - boolean closed = false; - byte[] buf = new byte[512]; - - private void readNext() { - if (mRing != null || closed) { - return; - } - - try { - - int length = ois.readInt(); - while (buf.length < length) { - buf = new byte[buf.length * 2]; - } - ois.readFully(buf, 0, length); - - Parcel parcel = Parcel.obtain(); // creating empty parcel object - parcel.unmarshall(buf, 0, length); - parcel.setDataPosition(0); - mRing = parcel.readParcelable(KeychainApplication.class.getClassLoader()); - parcel.recycle(); - } catch (EOFException e) { - // aight - close(); - } catch (IOException e) { - Log.e(Constants.TAG, "Encountered IOException during cache read!", e); - } - - } - - @Override - public boolean hasNext() { - readNext(); - return mRing != null; - } - - @Override - public E next() { - readNext(); - try { - return mRing; - } finally { - mRing = null; - } - } - - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - - @Override - public void finalize() throws Throwable { - close(); - super.finalize(); - } - - private void close() { - if (!closed) { - try { - ois.close(); - if (deleteAfterRead) { - //noinspection ResultOfMethodCallIgnored - tempFile.delete(); - } - } catch (IOException e) { - // nvm - } - } - closed = true; - } - - - }; - } - - public boolean delete() throws IOException { - - File cacheDir = mContext.getCacheDir(); - if (cacheDir == null) { - // https://groups.google.com/forum/#!topic/android-developers/-694j87eXVU - throw new IOException("cache dir is null!"); - } - - final File tempFile = new File(cacheDir, mFilename); - return tempFile.delete(); - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ParcelableFileCache.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ParcelableFileCache.java new file mode 100644 index 000000000..111bf0124 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ParcelableFileCache.java @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * + * 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 . + */ + +package org.sufficientlysecure.keychain.util; + +import android.content.Context; +import android.os.Parcel; +import android.os.Parcelable; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.KeychainApplication; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.EOFException; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * When sending large data (over 1MB) through Androids Binder IPC you get + * JavaBinder E !!! FAILED BINDER TRANSACTION !!! + *

+ * To overcome this problem, we cache large Parcelables into a file in our private cache directory + * instead of sending them through IPC. + */ +public class ParcelableFileCache { + + private Context mContext; + + private final String mFilename; + + public ParcelableFileCache(Context context, String filename) { + mContext = context; + mFilename = filename; + } + + public void writeCache(ArrayList selectedEntries) throws IOException { + writeCache(selectedEntries.iterator()); + } + + public void writeCache(Iterator it) throws IOException { + + File cacheDir = mContext.getCacheDir(); + if (cacheDir == null) { + // https://groups.google.com/forum/#!topic/android-developers/-694j87eXVU + throw new IOException("cache dir is null!"); + } + + File tempFile = new File(mContext.getCacheDir(), mFilename); + + DataOutputStream oos = new DataOutputStream(new FileOutputStream(tempFile)); + + while (it.hasNext()) { + Parcel p = Parcel.obtain(); // creating empty parcel object + p.writeParcelable(it.next(), 0); // saving bundle as parcel + byte[] buf = p.marshall(); + oos.writeInt(buf.length); + oos.write(buf); + p.recycle(); + } + + oos.close(); + + } + + public List readCacheIntoList() throws IOException { + ArrayList result = new ArrayList(); + Iterator it = readCache(); + while (it.hasNext()) { + result.add(it.next()); + } + return result; + } + + public Iterator readCache() throws IOException { + return readCache(true); + } + + public Iterator readCache(final boolean deleteAfterRead) throws IOException { + + File cacheDir = mContext.getCacheDir(); + if (cacheDir == null) { + // https://groups.google.com/forum/#!topic/android-developers/-694j87eXVU + throw new IOException("cache dir is null!"); + } + + final File tempFile = new File(cacheDir, mFilename); + final DataInputStream ois; + try { + ois = new DataInputStream(new FileInputStream(tempFile)); + } catch (FileNotFoundException e) { + Log.e(Constants.TAG, "parcel import file not existing", e); + throw new IOException(e); + } + + return new Iterator() { + + E mRing = null; + boolean closed = false; + byte[] buf = new byte[512]; + + private void readNext() { + if (mRing != null || closed) { + return; + } + + try { + + int length = ois.readInt(); + while (buf.length < length) { + buf = new byte[buf.length * 2]; + } + ois.readFully(buf, 0, length); + + Parcel parcel = Parcel.obtain(); // creating empty parcel object + parcel.unmarshall(buf, 0, length); + parcel.setDataPosition(0); + mRing = parcel.readParcelable(KeychainApplication.class.getClassLoader()); + parcel.recycle(); + } catch (EOFException e) { + // aight + close(); + } catch (IOException e) { + Log.e(Constants.TAG, "Encountered IOException during cache read!", e); + } + + } + + @Override + public boolean hasNext() { + readNext(); + return mRing != null; + } + + @Override + public E next() { + readNext(); + try { + return mRing; + } finally { + mRing = null; + } + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + + @Override + public void finalize() throws Throwable { + close(); + super.finalize(); + } + + private void close() { + if (!closed) { + try { + ois.close(); + if (deleteAfterRead) { + //noinspection ResultOfMethodCallIgnored + tempFile.delete(); + } + } catch (IOException e) { + // nvm + } + } + closed = true; + } + + + }; + } + + public boolean delete() throws IOException { + + File cacheDir = mContext.getCacheDir(); + if (cacheDir == null) { + // https://groups.google.com/forum/#!topic/android-developers/-694j87eXVU + throw new IOException("cache dir is null!"); + } + + final File tempFile = new File(cacheDir, mFilename); + return tempFile.delete(); + } + +} -- cgit v1.2.3