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 37a6a07d6..5da337996 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProvider.java +++ b/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProvider.java @@ -18,6 +18,7 @@ import org.fdroid.fdroid.data.Schema.InstalledAppTable; import org.fdroid.fdroid.data.Schema.InstalledAppTable.Cols; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; public class InstalledAppProvider extends FDroidProvider { @@ -81,6 +82,20 @@ public class InstalledAppProvider extends FDroidProvider { private static final UriMatcher MATCHER = new UriMatcher(-1); + /** + * Built-in apps that are signed by the various Android ROM keys. + * + * @see Certificates and private keys + */ + private static final String[] SYSTEM_PACKAGES = { + "android", // platform key + "com.android.email", // test/release key + "com.android.contacts", // shared key + "com.android.providers.downloads", // media key + }; + + private static String[] systemSignatures; + static { MATCHER.addURI(getAuthority(), null, CODE_LIST); MATCHER.addURI(getAuthority(), PATH_SEARCH + "/*", CODE_SEARCH); @@ -117,6 +132,36 @@ public class InstalledAppProvider extends FDroidProvider { return packageName; // all else fails, return packageName } + /** + * Add SQL selection statement to exclude {@link InstalledApp}s that were + * signed by the platform/shared/media/testkey keys. + * + * @see Certificates and private keys + */ + private QuerySelection selectNotSystemSignature(QuerySelection selection) { + if (systemSignatures == null) { + Log.i(TAG, "selectNotSystemSignature: systemSignature == null, querying for it"); + HashSet signatures = new HashSet<>(); + for (String packageName : SYSTEM_PACKAGES) { + Cursor cursor = query(InstalledAppProvider.getAppUri(packageName), new String[]{Cols.SIGNATURE}, + null, null, null); + if (cursor != null) { + if (cursor.moveToFirst()) { + signatures.add(cursor.getString(cursor.getColumnIndex(Cols.SIGNATURE))); + } + cursor.close(); + } + } + systemSignatures = signatures.toArray(new String[signatures.size()]); + } + + Log.i(TAG, "excluding InstalledApps signed by system signatures"); + for (String systemSignature : systemSignatures) { + selection = selection.add("NOT " + Cols.SIGNATURE + " IN (?)", new String[]{systemSignature}); + } + return selection; + } + @Override protected String getTableName() { return InstalledAppTable.NAME; @@ -185,6 +230,7 @@ public class InstalledAppProvider extends FDroidProvider { QuerySelection selection = new QuerySelection(customSelection, selectionArgs); switch (MATCHER.match(uri)) { case CODE_LIST: + selection = selectNotSystemSignature(selection); break; case CODE_SINGLE: 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 1b531d205..2df49e74d 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProviderService.java +++ b/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProviderService.java @@ -23,6 +23,8 @@ import rx.subjects.PublishSubject; import java.io.File; import java.io.FilenameFilter; import java.security.NoSuchAlgorithmException; +import java.util.Collections; +import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -146,7 +148,10 @@ public class InstalledAppProviderService extends IntentService { * Make sure that {@link InstalledAppProvider}, our database of installed apps, * is in sync with what the {@link PackageManager} tells us is installed. Once * completed, the relevant {@link android.content.ContentProvider}s will be - * notified of any changes to installed statuses. + * notified of any changes to installed statuses. The packages are processed + * in alphabetically order so that "{@code android}" is processed first. That + * is always present and signed by the system key, so it is the source of the + * system key for comparing all packages. *

* The installed app cache could get out of sync, e.g. if F-Droid crashed/ or * ran out of battery half way through responding to {@link Intent#ACTION_PACKAGE_ADDED}. @@ -169,6 +174,12 @@ public class InstalledAppProviderService extends IntentService { List packageInfoList = context.getPackageManager() .getInstalledPackages(PackageManager.GET_SIGNATURES); + Collections.sort(packageInfoList, new Comparator() { + @Override + public int compare(PackageInfo o1, PackageInfo o2) { + return o1.packageName.compareTo(o2.packageName); + } + }); for (PackageInfo packageInfo : packageInfoList) { if (cachedInfo.containsKey(packageInfo.packageName)) { if (packageInfo.lastUpdateTime < 1262300400000L // 2010-01-01 00:00 @@ -314,6 +325,11 @@ public class InstalledAppProviderService extends IntentService { context.getContentResolver().delete(uri, null, null); } + /** + * Get the fingerprint used to represent an APK signing key in F-Droid. + * This is a custom fingerprint algorithm that was kind of accidentally + * created, but is still in use. + */ private static String getPackageSig(PackageInfo info) { if (info == null || info.signatures == null || info.signatures.length < 1) { return "";