From 90467bf8bf2f8e4a46cb1db563154df4035bf746 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Fri, 27 May 2016 16:52:54 +0200 Subject: [PATCH] InstalledAppProvider: store APK hash and last update time The APK hash is useful for comparing whether something is exactly the same file as something else. For example, to compare whether the installed APK matches something that f-droid.org hosts. The "last update time" is a fast way to check whether the information is current. --- .../mock/MockInstallablePackageManager.java | 1 + .../fdroid/data/InstalledAppProviderTest.java | 38 +++++++++++++++++++ .../java/org/fdroid/fdroid/data/DBHelper.java | 32 ++++++++-------- .../fdroid/data/InstalledAppProvider.java | 8 +++- .../data/InstalledAppProviderService.java | 6 +++ 5 files changed, 69 insertions(+), 16 deletions(-) diff --git a/app/src/androidTest/java/mock/MockInstallablePackageManager.java b/app/src/androidTest/java/mock/MockInstallablePackageManager.java index 4b95dcf60..1fcef1e86 100644 --- a/app/src/androidTest/java/mock/MockInstallablePackageManager.java +++ b/app/src/androidTest/java/mock/MockInstallablePackageManager.java @@ -38,6 +38,7 @@ public class MockInstallablePackageManager extends MockPackageManager { p.versionCode = version; p.versionName = versionName; p.applicationInfo = new MockApplicationInfo(p); + p.lastUpdateTime = System.currentTimeMillis(); info.add(p); } } diff --git a/app/src/androidTest/java/org/fdroid/fdroid/data/InstalledAppProviderTest.java b/app/src/androidTest/java/org/fdroid/fdroid/data/InstalledAppProviderTest.java index e06f7531d..7c96e66e2 100644 --- a/app/src/androidTest/java/org/fdroid/fdroid/data/InstalledAppProviderTest.java +++ b/app/src/androidTest/java/org/fdroid/fdroid/data/InstalledAppProviderTest.java @@ -2,6 +2,8 @@ package org.fdroid.fdroid.data; import android.content.ContentValues; import android.content.pm.PackageInfo; +import android.database.Cursor; +import android.net.Uri; import mock.MockContextSwappableComponents; import mock.MockInstallablePackageManager; @@ -81,6 +83,39 @@ public class InstalledAppProviderTest extends FDroidProviderTest 0); + assertTrue(lastUpdateTime < System.currentTimeMillis()); + cursor.close(); + + insertInstalledApp(packageName, 11, "1.1"); + cursor = getMockContentResolver().query(uri, projection, null, null, null); + assertNotNull(cursor); + assertEquals("App \"" + packageName + "\" not installed", 1, cursor.getCount()); + cursor.moveToFirst(); + assertTrue(lastUpdateTime < cursor.getLong(cursor.getColumnIndex(InstalledAppProvider.DataColumns.LAST_UPDATE_TIME))); + cursor.close(); + } + public void testDelete() { insertInstalledApp("com.example.app1", 10, "1.0"); @@ -156,6 +191,9 @@ public class InstalledAppProviderTest extends FDroidProviderTest= 51) { + /** + * If any column was added or removed, just drop the table, create it again + * and let the cache be filled from scratch by {@link InstalledAppProviderService} + * For DB versions older than 43, this will create the {@link InstalledAppProvider} + * table for the first time. + */ + private void recreateInstalledAppTable(SQLiteDatabase db, int oldVersion) { + if (oldVersion >= 57) { return; } + Utils.debugLog(TAG, "(re)creating 'installed app' database table."); db.execSQL(DROP_TABLE_INSTALLED_APP); - createInstalledApp(db); + db.execSQL(CREATE_TABLE_INSTALLED_APP); } private static boolean columnExists(SQLiteDatabase db, diff --git a/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProvider.java b/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProvider.java index 414e528f5..9b3d01877 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProvider.java +++ b/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProvider.java @@ -64,10 +64,13 @@ public class InstalledAppProvider extends FDroidProvider { String VERSION_NAME = "versionName"; String APPLICATION_LABEL = "applicationLabel"; String SIGNATURE = "sig"; + String LAST_UPDATE_TIME = "lastUpdateTime"; + String HASH_TYPE = "hashType"; + String HASH = "hash"; String[] ALL = { _ID, PACKAGE_NAME, VERSION_CODE, VERSION_NAME, APPLICATION_LABEL, - SIGNATURE, + SIGNATURE, LAST_UPDATE_TIME, HASH_TYPE, HASH, }; } @@ -89,6 +92,9 @@ public class InstalledAppProvider extends FDroidProvider { return Uri.parse("content://" + getAuthority()); } + /** + * @return the {@link Uri} that points to a specific installed app + */ public static Uri getAppUri(String packageName) { return Uri.withAppendedPath(getContentUri(), packageName); } diff --git a/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProviderService.java b/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProviderService.java index 175a78976..acd5a8d45 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProviderService.java +++ b/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProviderService.java @@ -155,6 +155,12 @@ public class InstalledAppProviderService extends IntentService { InstalledAppProvider.getApplicationLabel(context, packageInfo.packageName)); contentValues.put(InstalledAppProvider.DataColumns.SIGNATURE, InstalledAppProvider.getPackageSig(packageInfo)); + contentValues.put(InstalledAppProvider.DataColumns.LAST_UPDATE_TIME, packageInfo.lastUpdateTime); + + String hashType = "sha256"; + String hash = Utils.getBinaryHash(new File(packageInfo.applicationInfo.publicSourceDir), hashType); + contentValues.put(InstalledAppProvider.DataColumns.HASH_TYPE, hashType); + contentValues.put(InstalledAppProvider.DataColumns.HASH, hash); context.getContentResolver().insert(uri, contentValues); }