Save app details to temp table, then flush after update verified.
This commit is contained in:
parent
9a2d390279
commit
cc0adcc5ad
@ -88,6 +88,11 @@
|
|||||||
android:name="org.fdroid.fdroid.data.TempApkProvider"
|
android:name="org.fdroid.fdroid.data.TempApkProvider"
|
||||||
android:exported="false"/>
|
android:exported="false"/>
|
||||||
|
|
||||||
|
<provider
|
||||||
|
android:authorities="org.fdroid.fdroid.data.TempAppProvider"
|
||||||
|
android:name="org.fdroid.fdroid.data.TempAppProvider"
|
||||||
|
android:exported="false"/>
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:authorities="org.fdroid.fdroid.data.InstalledAppProvider"
|
android:authorities="org.fdroid.fdroid.data.InstalledAppProvider"
|
||||||
android:name="org.fdroid.fdroid.data.InstalledAppProvider"
|
android:name="org.fdroid.fdroid.data.InstalledAppProvider"
|
||||||
|
@ -18,6 +18,7 @@ import org.fdroid.fdroid.data.AppProvider;
|
|||||||
import org.fdroid.fdroid.data.Repo;
|
import org.fdroid.fdroid.data.Repo;
|
||||||
import org.fdroid.fdroid.data.RepoProvider;
|
import org.fdroid.fdroid.data.RepoProvider;
|
||||||
import org.fdroid.fdroid.data.TempApkProvider;
|
import org.fdroid.fdroid.data.TempApkProvider;
|
||||||
|
import org.fdroid.fdroid.data.TempAppProvider;
|
||||||
import org.fdroid.fdroid.net.Downloader;
|
import org.fdroid.fdroid.net.Downloader;
|
||||||
import org.fdroid.fdroid.net.DownloaderFactory;
|
import org.fdroid.fdroid.net.DownloaderFactory;
|
||||||
import org.xml.sax.InputSource;
|
import org.xml.sax.InputSource;
|
||||||
@ -203,11 +204,13 @@ public class RepoUpdater {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void flushBufferToDb() throws UpdateException {
|
private void flushBufferToDb() throws UpdateException {
|
||||||
Log.d(TAG, "Flushing details of " + MAX_APP_BUFFER + " and their packages to the database.");
|
if (apksToSave.size() > 0 || appsToSave.size() > 0) {
|
||||||
flushAppsToDbInBatch();
|
Log.d(TAG, "Flushing details of up to " + MAX_APP_BUFFER + " apps and their packages to the database.");
|
||||||
flushApksToDbInBatch();
|
flushAppsToDbInBatch();
|
||||||
apksToSave.clear();
|
flushApksToDbInBatch();
|
||||||
appsToSave.clear();
|
apksToSave.clear();
|
||||||
|
appsToSave.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void flushApksToDbInBatch() throws UpdateException {
|
private void flushApksToDbInBatch() throws UpdateException {
|
||||||
@ -236,7 +239,7 @@ public class RepoUpdater {
|
|||||||
ArrayList<ContentProviderOperation> appOperations = insertOrUpdateApps(appsToSave);
|
ArrayList<ContentProviderOperation> appOperations = insertOrUpdateApps(appsToSave);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
context.getContentResolver().applyBatch(AppProvider.getAuthority(), appOperations);
|
context.getContentResolver().applyBatch(TempAppProvider.getAuthority(), appOperations);
|
||||||
} catch (RemoteException|OperationApplicationException e) {
|
} catch (RemoteException|OperationApplicationException e) {
|
||||||
Log.e(TAG, "Error updating apps", e);
|
Log.e(TAG, "Error updating apps", e);
|
||||||
throw new UpdateException(repo, "Error updating apps: " + e.getMessage(), e);
|
throw new UpdateException(repo, "Error updating apps: " + e.getMessage(), e);
|
||||||
@ -290,7 +293,7 @@ public class RepoUpdater {
|
|||||||
* <strong>Does not do any checks to see if the app already exists or not.</strong>
|
* <strong>Does not do any checks to see if the app already exists or not.</strong>
|
||||||
*/
|
*/
|
||||||
private ContentProviderOperation updateExistingApp(App app) {
|
private ContentProviderOperation updateExistingApp(App app) {
|
||||||
Uri uri = AppProvider.getContentUri(app);
|
Uri uri = TempAppProvider.getAppUri(app);
|
||||||
ContentValues values = app.toContentValues();
|
ContentValues values = app.toContentValues();
|
||||||
for (final String toIgnore : APP_FIELDS_TO_IGNORE) {
|
for (final String toIgnore : APP_FIELDS_TO_IGNORE) {
|
||||||
if (values.containsKey(toIgnore)) {
|
if (values.containsKey(toIgnore)) {
|
||||||
@ -306,7 +309,7 @@ public class RepoUpdater {
|
|||||||
*/
|
*/
|
||||||
private ContentProviderOperation insertNewApp(App app) {
|
private ContentProviderOperation insertNewApp(App app) {
|
||||||
ContentValues values = app.toContentValues();
|
ContentValues values = app.toContentValues();
|
||||||
Uri uri = AppProvider.getContentUri();
|
Uri uri = TempAppProvider.getContentUri();
|
||||||
return ContentProviderOperation.newInsert(uri).withValues(values).build();
|
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
|
// 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
|
// want to put stuff in the real database until we are sure it is from a
|
||||||
// trusted source.
|
// trusted source.
|
||||||
|
TempAppProvider.Helper.init(context);
|
||||||
TempApkProvider.Helper.init(context);
|
TempApkProvider.Helper.init(context);
|
||||||
|
|
||||||
// Due to a bug in Android 5.0 Lollipop, the inclusion of spongycastle causes
|
// 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());
|
final RepoXMLHandler repoXMLHandler = new RepoXMLHandler(repo, createIndexReceiver());
|
||||||
reader.setContentHandler(repoXMLHandler);
|
reader.setContentHandler(repoXMLHandler);
|
||||||
reader.parse(new InputSource(indexInputStream));
|
reader.parse(new InputSource(indexInputStream));
|
||||||
|
|
||||||
|
flushBufferToDb();
|
||||||
|
|
||||||
signingCertFromJar = getSigningCertFromJar(indexEntry);
|
signingCertFromJar = getSigningCertFromJar(indexEntry);
|
||||||
|
|
||||||
// JarEntry can only read certificates after the file represented by that JarEntry
|
// JarEntry can only read certificates after the file represented by that JarEntry
|
||||||
// has been read completely, so verification cannot run until now...
|
// has been read completely, so verification cannot run until now...
|
||||||
assertSigningCertFromXmlCorrect();
|
assertSigningCertFromXmlCorrect();
|
||||||
|
|
||||||
|
Log.i(TAG, "Repo signature verified, saving app metadata to database.");
|
||||||
|
TempAppProvider.Helper.commit(context);
|
||||||
TempApkProvider.Helper.commit(context);
|
TempApkProvider.Helper.commit(context);
|
||||||
RepoProvider.Helper.update(context, repo, repoDetailsToSave);
|
RepoProvider.Helper.update(context, repo, repoDetailsToSave);
|
||||||
|
|
||||||
|
@ -31,14 +31,12 @@ public class TempApkProvider extends ApkProvider {
|
|||||||
|
|
||||||
private static final int CODE_INIT = 10000;
|
private static final int CODE_INIT = 10000;
|
||||||
private static final int CODE_COMMIT = CODE_INIT + 1;
|
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);
|
private static final UriMatcher matcher = new UriMatcher(-1);
|
||||||
|
|
||||||
static {
|
static {
|
||||||
matcher.addURI(getAuthority(), PATH_INIT, CODE_INIT);
|
matcher.addURI(getAuthority(), PATH_INIT, CODE_INIT);
|
||||||
matcher.addURI(getAuthority(), PATH_COMMIT, CODE_COMMIT);
|
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_APK + "/#/*", CODE_SINGLE);
|
||||||
matcher.addURI(getAuthority(), PATH_REPO_APK + "/#/*", CODE_REPO_APK);
|
matcher.addURI(getAuthority(), PATH_REPO_APK + "/#/*", CODE_REPO_APK);
|
||||||
}
|
}
|
||||||
@ -94,14 +92,6 @@ public class TempApkProvider extends ApkProvider {
|
|||||||
context.getContentResolver().insert(uri, new ContentValues());
|
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
|
@Override
|
||||||
@ -114,16 +104,13 @@ public class TempApkProvider extends ApkProvider {
|
|||||||
} else if (code == CODE_COMMIT) {
|
} else if (code == CODE_COMMIT) {
|
||||||
commitTable();
|
commitTable();
|
||||||
return null;
|
return null;
|
||||||
} else if (code == CODE_ROLLBACK) {
|
|
||||||
removeTable();
|
|
||||||
return null;
|
|
||||||
} else {
|
} else {
|
||||||
return super.insert(uri, values);
|
return super.insert(uri, values);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initTable() {
|
private void initTable() {
|
||||||
removeTable();
|
write().execSQL("DROP TABLE IF EXISTS " + getTableName());
|
||||||
write().execSQL("CREATE TEMPORARY TABLE " + getTableName() + " AS SELECT * FROM " + DBHelper.TABLE_APK);
|
write().execSQL("CREATE TEMPORARY TABLE " + getTableName() + " AS SELECT * FROM " + DBHelper.TABLE_APK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
103
F-Droid/src/org/fdroid/fdroid/data/TempAppProvider.java
Normal file
103
F-Droid/src/org/fdroid/fdroid/data/TempAppProvider.java
Normal file
@ -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() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user