diff --git a/F-Droid/AndroidManifest.xml b/F-Droid/AndroidManifest.xml
index 3fffd013c..b9c86c37b 100644
--- a/F-Droid/AndroidManifest.xml
+++ b/F-Droid/AndroidManifest.xml
@@ -88,6 +88,11 @@
android:name="org.fdroid.fdroid.data.TempApkProvider"
android:exported="false"/>
+
+
0 || appsToSave.size() > 0) {
+ Log.d(TAG, "Flushing details of up to " + MAX_APP_BUFFER + " apps and their packages to the database.");
+ flushAppsToDbInBatch();
+ flushApksToDbInBatch();
+ apksToSave.clear();
+ appsToSave.clear();
+ }
}
private void flushApksToDbInBatch() throws UpdateException {
@@ -236,7 +239,7 @@ public class RepoUpdater {
ArrayList appOperations = insertOrUpdateApps(appsToSave);
try {
- context.getContentResolver().applyBatch(AppProvider.getAuthority(), appOperations);
+ context.getContentResolver().applyBatch(TempAppProvider.getAuthority(), appOperations);
} catch (RemoteException|OperationApplicationException e) {
Log.e(TAG, "Error updating apps", e);
throw new UpdateException(repo, "Error updating apps: " + e.getMessage(), e);
@@ -290,7 +293,7 @@ public class RepoUpdater {
* Does not do any checks to see if the app already exists or not.
*/
private ContentProviderOperation updateExistingApp(App app) {
- Uri uri = AppProvider.getContentUri(app);
+ Uri uri = TempAppProvider.getAppUri(app);
ContentValues values = app.toContentValues();
for (final String toIgnore : APP_FIELDS_TO_IGNORE) {
if (values.containsKey(toIgnore)) {
@@ -306,7 +309,7 @@ public class RepoUpdater {
*/
private ContentProviderOperation insertNewApp(App app) {
ContentValues values = app.toContentValues();
- Uri uri = AppProvider.getContentUri();
+ Uri uri = TempAppProvider.getContentUri();
return ContentProviderOperation.newInsert(uri).withValues(values).build();
}
@@ -415,6 +418,7 @@ public class RepoUpdater {
// the index was signed with until we've finished reading it - and we don't
// want to put stuff in the real database until we are sure it is from a
// trusted source.
+ TempAppProvider.Helper.init(context);
TempApkProvider.Helper.init(context);
// Due to a bug in Android 5.0 Lollipop, the inclusion of spongycastle causes
@@ -433,12 +437,17 @@ public class RepoUpdater {
final RepoXMLHandler repoXMLHandler = new RepoXMLHandler(repo, createIndexReceiver());
reader.setContentHandler(repoXMLHandler);
reader.parse(new InputSource(indexInputStream));
+
+ flushBufferToDb();
+
signingCertFromJar = getSigningCertFromJar(indexEntry);
// JarEntry can only read certificates after the file represented by that JarEntry
// has been read completely, so verification cannot run until now...
assertSigningCertFromXmlCorrect();
+ Log.i(TAG, "Repo signature verified, saving app metadata to database.");
+ TempAppProvider.Helper.commit(context);
TempApkProvider.Helper.commit(context);
RepoProvider.Helper.update(context, repo, repoDetailsToSave);
diff --git a/F-Droid/src/org/fdroid/fdroid/data/TempApkProvider.java b/F-Droid/src/org/fdroid/fdroid/data/TempApkProvider.java
index a8bc1dbbc..e2806fd49 100644
--- a/F-Droid/src/org/fdroid/fdroid/data/TempApkProvider.java
+++ b/F-Droid/src/org/fdroid/fdroid/data/TempApkProvider.java
@@ -31,14 +31,12 @@ public class TempApkProvider extends ApkProvider {
private static final int CODE_INIT = 10000;
private static final int CODE_COMMIT = CODE_INIT + 1;
- private static final int CODE_ROLLBACK = CODE_INIT + 2;
private static final UriMatcher matcher = new UriMatcher(-1);
static {
matcher.addURI(getAuthority(), PATH_INIT, CODE_INIT);
matcher.addURI(getAuthority(), PATH_COMMIT, CODE_COMMIT);
- matcher.addURI(getAuthority(), PATH_ROLLBACK, CODE_ROLLBACK);
matcher.addURI(getAuthority(), PATH_APK + "/#/*", CODE_SINGLE);
matcher.addURI(getAuthority(), PATH_REPO_APK + "/#/*", CODE_REPO_APK);
}
@@ -94,14 +92,6 @@ public class TempApkProvider extends ApkProvider {
context.getContentResolver().insert(uri, new ContentValues());
}
- /**
- * Not sure that this is strictly necessary, but this will remove the temp table.
- */
- public static void rollback(Context context) {
- Uri uri = Uri.withAppendedPath(getContentUri(), PATH_ROLLBACK);
- context.getContentResolver().insert(uri, new ContentValues());
- }
-
}
@Override
@@ -114,16 +104,13 @@ public class TempApkProvider extends ApkProvider {
} else if (code == CODE_COMMIT) {
commitTable();
return null;
- } else if (code == CODE_ROLLBACK) {
- removeTable();
- return null;
} else {
return super.insert(uri, values);
}
}
private void initTable() {
- removeTable();
+ write().execSQL("DROP TABLE IF EXISTS " + getTableName());
write().execSQL("CREATE TEMPORARY TABLE " + getTableName() + " AS SELECT * FROM " + DBHelper.TABLE_APK);
}
diff --git a/F-Droid/src/org/fdroid/fdroid/data/TempAppProvider.java b/F-Droid/src/org/fdroid/fdroid/data/TempAppProvider.java
new file mode 100644
index 000000000..b4c9fb7eb
--- /dev/null
+++ b/F-Droid/src/org/fdroid/fdroid/data/TempAppProvider.java
@@ -0,0 +1,103 @@
+package org.fdroid.fdroid.data;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.UriMatcher;
+import android.net.Uri;
+import android.util.Log;
+
+import java.util.List;
+
+/**
+ * This class does all of its operations in a temporary sqlite table.
+ */
+public class TempAppProvider extends AppProvider {
+
+ private static final String TAG = "TempAppProvider";
+
+ private static final String PROVIDER_NAME = "TempAppProvider";
+
+ private static final String PATH_INIT = "init";
+ private static final String PATH_COMMIT = "commit";
+ private static final String PATH_ROLLBACK = "rollback";
+
+ private static final int CODE_INIT = 10000;
+ private static final int CODE_COMMIT = CODE_INIT + 1;
+
+ private static final UriMatcher matcher = new UriMatcher(-1);
+
+ static {
+ matcher.addURI(getAuthority(), PATH_INIT, CODE_INIT);
+ matcher.addURI(getAuthority(), PATH_COMMIT, CODE_COMMIT);
+ matcher.addURI(getAuthority(), "*", CODE_SINGLE);
+ }
+
+ @Override
+ protected String getTableName() {
+ return "temp_" + super.getTableName();
+ }
+
+ public static String getAuthority() {
+ return AUTHORITY + "." + PROVIDER_NAME;
+ }
+
+ public static Uri getContentUri() {
+ return Uri.parse("content://" + getAuthority());
+ }
+
+ public static Uri getAppUri(App app) {
+ return Uri.withAppendedPath(getContentUri(), app.id);
+ }
+
+ public static class Helper {
+
+ /**
+ * Deletes the old temporary table (if it exists). Then creates a new temporary apk provider
+ * table and populates it with all the data from the real apk provider table.
+ */
+ public static void init(Context context) {
+ Uri uri = Uri.withAppendedPath(getContentUri(), PATH_INIT);
+ context.getContentResolver().insert(uri, new ContentValues());
+ }
+
+ /**
+ * Saves data from the temp table to the apk table, by removing _EVERYTHING_ from the real
+ * apk table and inserting all of the records from here. The temporary table is then removed.
+ */
+ public static void commit(Context context) {
+ Uri uri = Uri.withAppendedPath(getContentUri(), PATH_COMMIT);
+ context.getContentResolver().insert(uri, new ContentValues());
+ }
+
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ int code = matcher.match(uri);
+
+ if (code == CODE_INIT) {
+ initTable();
+ return null;
+ } else if (code == CODE_COMMIT) {
+ commitTable();
+ return null;
+ } else {
+ return super.insert(uri, values);
+ }
+ }
+
+ private void initTable() {
+ write().execSQL("DROP TABLE IF EXISTS " + getTableName());
+ write().execSQL("CREATE TEMPORARY TABLE " + getTableName() + " AS SELECT * FROM " + DBHelper.TABLE_APP);
+ }
+
+ private void commitTable() {
+ Log.d(TAG, "Deleting all apks from " + DBHelper.TABLE_APP + " so they can be copied from " + getTableName());
+ write().execSQL("DELETE FROM " + DBHelper.TABLE_APP);
+ write().execSQL("INSERT INTO " + DBHelper.TABLE_APP + " SELECT * FROM " + getTableName());
+ }
+
+ private void removeTable() {
+
+ }
+}