Don't implement 'update' for installed apps, use replace (Fixes #14)
There were some weird edge cases that couldn't quite be pinned down, whereby installing an app would result in a unique key violation being hit. One example was when somebody was installing an apk from a file manager. It seems that this doesn't trigger a PACKAGE_CHANGED, but rather a PACKAGE_INSTALLED. The end result is that it attempts to insert a record that already exists in the installed apps table. Because we have a unique key constraing on the appId, it breaks. This commit changes the way that we insert installed app details. Instead of inserting some times, and updating other times, we always insert. If we hit a unique key violation, the row is deleted, and then the new values are reinserted.
This commit is contained in:
parent
d573bac5b0
commit
71db322b6d
@ -39,11 +39,12 @@ public class PackageUpgradedReceiver extends PackageReceiver {
|
||||
|
||||
Log.d("FDroid", "Updating installed app info for '" + appId + "' to v" + info.versionCode + " (" + info.versionName + ")");
|
||||
|
||||
Uri uri = InstalledAppProvider.getAppUri(appId);
|
||||
Uri uri = InstalledAppProvider.getContentUri();
|
||||
ContentValues values = new ContentValues(1);
|
||||
values.put(InstalledAppProvider.DataColumns.APP_ID, info.packageName);
|
||||
values.put(InstalledAppProvider.DataColumns.VERSION_CODE, info.versionCode);
|
||||
values.put(InstalledAppProvider.DataColumns.VERSION_NAME, info.versionName);
|
||||
context.getContentResolver().update(uri, values, null, null);
|
||||
context.getContentResolver().insert(uri, values);
|
||||
}
|
||||
|
||||
}
|
@ -27,7 +27,6 @@ public class InstalledAppCacheUpdater {
|
||||
private Context context;
|
||||
|
||||
private List<PackageInfo> toInsert = new ArrayList<PackageInfo>();
|
||||
private List<PackageInfo> toUpdate = new ArrayList<PackageInfo>();
|
||||
private List<String> toDelete = new ArrayList<String>();
|
||||
|
||||
protected InstalledAppCacheUpdater(Context context) {
|
||||
@ -85,14 +84,13 @@ public class InstalledAppCacheUpdater {
|
||||
* then the cache has changed.
|
||||
*/
|
||||
private boolean hasChanged() {
|
||||
return toInsert.size() > 0 || toUpdate.size() > 0 || toDelete.size() > 0;
|
||||
return toInsert.size() > 0 || toDelete.size() > 0;
|
||||
}
|
||||
|
||||
private void updateCache() {
|
||||
|
||||
ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
|
||||
ops.addAll(deleteFromCache(toDelete));
|
||||
ops.addAll(updateCachedValues(toUpdate));
|
||||
ops.addAll(insertIntoCache(toInsert));
|
||||
|
||||
if (ops.size() > 0) {
|
||||
@ -114,12 +112,8 @@ public class InstalledAppCacheUpdater {
|
||||
|
||||
List<PackageInfo> installedPackages = context.getPackageManager().getInstalledPackages(0);
|
||||
for (PackageInfo appInfo : installedPackages) {
|
||||
if (!cachedInfo.containsKey(appInfo.packageName)) {
|
||||
toInsert.add(appInfo);
|
||||
} else {
|
||||
if (cachedInfo.get(appInfo.packageName) < appInfo.versionCode) {
|
||||
toUpdate.add(appInfo);
|
||||
}
|
||||
toInsert.add(appInfo);
|
||||
if (cachedInfo.containsKey(appInfo.packageName)) {
|
||||
cachedInfo.remove(appInfo.packageName);
|
||||
}
|
||||
}
|
||||
@ -148,22 +142,6 @@ public class InstalledAppCacheUpdater {
|
||||
return ops;
|
||||
}
|
||||
|
||||
private List<ContentProviderOperation> updateCachedValues(List<PackageInfo> appsToUpdate) {
|
||||
List<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(appsToUpdate.size());
|
||||
if (appsToUpdate.size() > 0) {
|
||||
Log.d(TAG, "Preparing to update installed app cache for " + appsToUpdate.size() + " apps.");
|
||||
for (PackageInfo info : appsToUpdate) {
|
||||
Uri uri = InstalledAppProvider.getAppUri(info.packageName);
|
||||
ContentProviderOperation op = ContentProviderOperation.newUpdate(uri)
|
||||
.withValue(InstalledAppProvider.DataColumns.VERSION_CODE, info.versionCode)
|
||||
.withValue(InstalledAppProvider.DataColumns.VERSION_NAME, info.versionName)
|
||||
.build();
|
||||
ops.add(op);
|
||||
}
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
private List<ContentProviderOperation> deleteFromCache(List<String> appIds) {
|
||||
List<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(appIds.size());
|
||||
if (appIds.size() > 0) {
|
||||
|
@ -140,7 +140,7 @@ public class InstalledAppProvider extends FDroidProvider {
|
||||
}
|
||||
|
||||
verifyVersionNameNotNull(values);
|
||||
write().insertOrThrow(getTableName(), null, values);
|
||||
write().replaceOrThrow(getTableName(), null, values);
|
||||
if (!isApplyingBatch()) {
|
||||
getContext().getContentResolver().notifyChange(uri, null);
|
||||
}
|
||||
@ -149,20 +149,7 @@ public class InstalledAppProvider extends FDroidProvider {
|
||||
|
||||
@Override
|
||||
public int update(Uri uri, ContentValues values, String where, String[] whereArgs) {
|
||||
|
||||
if (matcher.match(uri) != CODE_SINGLE) {
|
||||
throw new UnsupportedOperationException("Update not supported for " + uri + ".");
|
||||
}
|
||||
|
||||
QuerySelection query = new QuerySelection(where, whereArgs);
|
||||
query = query.add(queryApp(uri.getLastPathSegment()));
|
||||
|
||||
verifyVersionNameNotNull(values);
|
||||
int count = write().update(getTableName(), values, query.getSelection(), query.getArgs());
|
||||
if (!isApplyingBatch()) {
|
||||
getContext().getContentResolver().notifyChange(uri, null);
|
||||
}
|
||||
return count;
|
||||
throw new UnsupportedOperationException("\"Update' not supported for installed appp provider. Instead, you should insert, and it will overwrite the relevant rows if one exists.");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -60,10 +60,20 @@ public class InstalledAppProviderTest extends FDroidProviderTest<InstalledAppPro
|
||||
assertResultCount(2, InstalledAppProvider.getContentUri());
|
||||
assertIsInstalledVersionInDb("com.example.app2", 10, "1.0");
|
||||
|
||||
getMockContentResolver().update(
|
||||
InstalledAppProvider.getAppUri("com.example.app2"),
|
||||
createContentValues(11, "1.1"),
|
||||
null, null
|
||||
try {
|
||||
getMockContentResolver().update(
|
||||
InstalledAppProvider.getAppUri("com.example.app2"),
|
||||
createContentValues(11, "1.1"),
|
||||
null, null
|
||||
);
|
||||
fail();
|
||||
} catch (UnsupportedOperationException e) {
|
||||
// We expect this to happen, because we should be using insert() instead.
|
||||
}
|
||||
|
||||
getMockContentResolver().insert(
|
||||
InstalledAppProvider.getContentUri(),
|
||||
createContentValues("com.example.app2", 11, "1.1")
|
||||
);
|
||||
|
||||
assertResultCount(2, InstalledAppProvider.getContentUri());
|
||||
|
Loading…
x
Reference in New Issue
Block a user