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.
This commit is contained in:
parent
906a26414a
commit
90467bf8bf
@ -38,6 +38,7 @@ public class MockInstallablePackageManager extends MockPackageManager {
|
|||||||
p.versionCode = version;
|
p.versionCode = version;
|
||||||
p.versionName = versionName;
|
p.versionName = versionName;
|
||||||
p.applicationInfo = new MockApplicationInfo(p);
|
p.applicationInfo = new MockApplicationInfo(p);
|
||||||
|
p.lastUpdateTime = System.currentTimeMillis();
|
||||||
info.add(p);
|
info.add(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,8 @@ package org.fdroid.fdroid.data;
|
|||||||
|
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.content.pm.PackageInfo;
|
import android.content.pm.PackageInfo;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
|
||||||
import mock.MockContextSwappableComponents;
|
import mock.MockContextSwappableComponents;
|
||||||
import mock.MockInstallablePackageManager;
|
import mock.MockInstallablePackageManager;
|
||||||
@ -81,6 +83,39 @@ public class InstalledAppProviderTest extends FDroidProviderTest<InstalledAppPro
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testLastUpdateTime() {
|
||||||
|
String packageName = "com.example.app";
|
||||||
|
|
||||||
|
insertInstalledApp(packageName, 10, "1.0");
|
||||||
|
assertResultCount(1, InstalledAppProvider.getContentUri());
|
||||||
|
assertIsInstalledVersionInDb(packageName, 10, "1.0");
|
||||||
|
|
||||||
|
Uri uri = InstalledAppProvider.getAppUri(packageName);
|
||||||
|
|
||||||
|
String[] projection = {
|
||||||
|
InstalledAppProvider.DataColumns.PACKAGE_NAME,
|
||||||
|
InstalledAppProvider.DataColumns.LAST_UPDATE_TIME,
|
||||||
|
};
|
||||||
|
|
||||||
|
Cursor cursor = getMockContentResolver().query(uri, projection, null, null, null);
|
||||||
|
assertNotNull(cursor);
|
||||||
|
assertEquals("App \"" + packageName + "\" not installed", 1, cursor.getCount());
|
||||||
|
cursor.moveToFirst();
|
||||||
|
assertEquals(packageName, cursor.getString(cursor.getColumnIndex(InstalledAppProvider.DataColumns.PACKAGE_NAME)));
|
||||||
|
long lastUpdateTime = cursor.getLong(cursor.getColumnIndex(InstalledAppProvider.DataColumns.LAST_UPDATE_TIME));
|
||||||
|
assertTrue(lastUpdateTime > 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() {
|
public void testDelete() {
|
||||||
|
|
||||||
insertInstalledApp("com.example.app1", 10, "1.0");
|
insertInstalledApp("com.example.app1", 10, "1.0");
|
||||||
@ -156,6 +191,9 @@ public class InstalledAppProviderTest extends FDroidProviderTest<InstalledAppPro
|
|||||||
values.put(InstalledAppProvider.DataColumns.VERSION_CODE, versionCode);
|
values.put(InstalledAppProvider.DataColumns.VERSION_CODE, versionCode);
|
||||||
values.put(InstalledAppProvider.DataColumns.VERSION_NAME, versionNumber);
|
values.put(InstalledAppProvider.DataColumns.VERSION_NAME, versionNumber);
|
||||||
values.put(InstalledAppProvider.DataColumns.SIGNATURE, "");
|
values.put(InstalledAppProvider.DataColumns.SIGNATURE, "");
|
||||||
|
values.put(InstalledAppProvider.DataColumns.LAST_UPDATE_TIME, System.currentTimeMillis());
|
||||||
|
values.put(InstalledAppProvider.DataColumns.HASH_TYPE, "sha256");
|
||||||
|
values.put(InstalledAppProvider.DataColumns.HASH, "cafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafe");
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,11 +103,14 @@ class DBHelper extends SQLiteOpenHelper {
|
|||||||
+ InstalledAppProvider.DataColumns.VERSION_CODE + " INT NOT NULL, "
|
+ InstalledAppProvider.DataColumns.VERSION_CODE + " INT NOT NULL, "
|
||||||
+ InstalledAppProvider.DataColumns.VERSION_NAME + " TEXT NOT NULL, "
|
+ InstalledAppProvider.DataColumns.VERSION_NAME + " TEXT NOT NULL, "
|
||||||
+ InstalledAppProvider.DataColumns.APPLICATION_LABEL + " TEXT NOT NULL, "
|
+ InstalledAppProvider.DataColumns.APPLICATION_LABEL + " TEXT NOT NULL, "
|
||||||
+ InstalledAppProvider.DataColumns.SIGNATURE + " TEXT NOT NULL "
|
+ InstalledAppProvider.DataColumns.SIGNATURE + " TEXT NOT NULL, "
|
||||||
|
+ InstalledAppProvider.DataColumns.LAST_UPDATE_TIME + " INTEGER NOT NULL DEFAULT 0, "
|
||||||
|
+ InstalledAppProvider.DataColumns.HASH_TYPE + " TEXT NOT NULL, "
|
||||||
|
+ InstalledAppProvider.DataColumns.HASH + " TEXT NOT NULL"
|
||||||
+ " );";
|
+ " );";
|
||||||
private static final String DROP_TABLE_INSTALLED_APP = "DROP TABLE " + TABLE_INSTALLED_APP + ";";
|
private static final String DROP_TABLE_INSTALLED_APP = "DROP TABLE " + TABLE_INSTALLED_APP + ";";
|
||||||
|
|
||||||
private static final int DB_VERSION = 55;
|
private static final int DB_VERSION = 56;
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
|
|
||||||
@ -199,7 +202,7 @@ class DBHelper extends SQLiteOpenHelper {
|
|||||||
public void onCreate(SQLiteDatabase db) {
|
public void onCreate(SQLiteDatabase db) {
|
||||||
|
|
||||||
createAppApk(db);
|
createAppApk(db);
|
||||||
createInstalledApp(db);
|
db.execSQL(CREATE_TABLE_INSTALLED_APP);
|
||||||
db.execSQL(CREATE_TABLE_REPO);
|
db.execSQL(CREATE_TABLE_REPO);
|
||||||
|
|
||||||
insertRepo(
|
insertRepo(
|
||||||
@ -287,16 +290,15 @@ class DBHelper extends SQLiteOpenHelper {
|
|||||||
addLastUpdatedToRepo(db, oldVersion);
|
addLastUpdatedToRepo(db, oldVersion);
|
||||||
renameRepoId(db, oldVersion);
|
renameRepoId(db, oldVersion);
|
||||||
populateRepoNames(db, oldVersion);
|
populateRepoNames(db, oldVersion);
|
||||||
if (oldVersion < 43) createInstalledApp(db);
|
|
||||||
addIsSwapToRepo(db, oldVersion);
|
addIsSwapToRepo(db, oldVersion);
|
||||||
addChangelogToApp(db, oldVersion);
|
addChangelogToApp(db, oldVersion);
|
||||||
addIconUrlLargeToApp(db, oldVersion);
|
addIconUrlLargeToApp(db, oldVersion);
|
||||||
updateIconUrlLarge(db, oldVersion);
|
updateIconUrlLarge(db, oldVersion);
|
||||||
recreateInstalledCache(db, oldVersion);
|
|
||||||
addCredentialsToRepo(db, oldVersion);
|
addCredentialsToRepo(db, oldVersion);
|
||||||
addAuthorToApp(db, oldVersion);
|
addAuthorToApp(db, oldVersion);
|
||||||
useMaxValueInMaxSdkVersion(db, oldVersion);
|
useMaxValueInMaxSdkVersion(db, oldVersion);
|
||||||
requireTimestampInRepos(db, oldVersion);
|
requireTimestampInRepos(db, oldVersion);
|
||||||
|
recreateInstalledAppTable(db, oldVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -555,19 +557,19 @@ class DBHelper extends SQLiteOpenHelper {
|
|||||||
db.execSQL("create index apk_id on " + TABLE_APK + " (id);");
|
db.execSQL("create index apk_id on " + TABLE_APK + " (id);");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createInstalledApp(SQLiteDatabase db) {
|
/**
|
||||||
Utils.debugLog(TAG, "Creating 'installed app' database table.");
|
* If any column was added or removed, just drop the table, create it again
|
||||||
db.execSQL(CREATE_TABLE_INSTALLED_APP);
|
* 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.
|
||||||
// If any column was added or removed, just drop the table, create it
|
*/
|
||||||
// again and let the cache be filled from scratch again.
|
private void recreateInstalledAppTable(SQLiteDatabase db, int oldVersion) {
|
||||||
private void recreateInstalledCache(SQLiteDatabase db, int oldVersion) {
|
if (oldVersion >= 57) {
|
||||||
if (oldVersion >= 51) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
Utils.debugLog(TAG, "(re)creating 'installed app' database table.");
|
||||||
db.execSQL(DROP_TABLE_INSTALLED_APP);
|
db.execSQL(DROP_TABLE_INSTALLED_APP);
|
||||||
createInstalledApp(db);
|
db.execSQL(CREATE_TABLE_INSTALLED_APP);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean columnExists(SQLiteDatabase db,
|
private static boolean columnExists(SQLiteDatabase db,
|
||||||
|
@ -64,10 +64,13 @@ public class InstalledAppProvider extends FDroidProvider {
|
|||||||
String VERSION_NAME = "versionName";
|
String VERSION_NAME = "versionName";
|
||||||
String APPLICATION_LABEL = "applicationLabel";
|
String APPLICATION_LABEL = "applicationLabel";
|
||||||
String SIGNATURE = "sig";
|
String SIGNATURE = "sig";
|
||||||
|
String LAST_UPDATE_TIME = "lastUpdateTime";
|
||||||
|
String HASH_TYPE = "hashType";
|
||||||
|
String HASH = "hash";
|
||||||
|
|
||||||
String[] ALL = {
|
String[] ALL = {
|
||||||
_ID, PACKAGE_NAME, VERSION_CODE, VERSION_NAME, APPLICATION_LABEL,
|
_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 Uri.parse("content://" + getAuthority());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the {@link Uri} that points to a specific installed app
|
||||||
|
*/
|
||||||
public static Uri getAppUri(String packageName) {
|
public static Uri getAppUri(String packageName) {
|
||||||
return Uri.withAppendedPath(getContentUri(), packageName);
|
return Uri.withAppendedPath(getContentUri(), packageName);
|
||||||
}
|
}
|
||||||
|
@ -155,6 +155,12 @@ public class InstalledAppProviderService extends IntentService {
|
|||||||
InstalledAppProvider.getApplicationLabel(context, packageInfo.packageName));
|
InstalledAppProvider.getApplicationLabel(context, packageInfo.packageName));
|
||||||
contentValues.put(InstalledAppProvider.DataColumns.SIGNATURE,
|
contentValues.put(InstalledAppProvider.DataColumns.SIGNATURE,
|
||||||
InstalledAppProvider.getPackageSig(packageInfo));
|
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);
|
context.getContentResolver().insert(uri, contentValues);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user