Don't accidentally return Apk instances from the wrong repo

There may be multiple apk files with the same hash. Although it is not a
security issue to install one or the other (they are exactly the same
binary), they may have different metadata to display in the client.
Thus, it may result in weirdness if one has a different
name/description/summary etc).

This change takes each of the matching Apk objects from the database,
then asks them where they expect to be downloaded. It matches this
against the File that we are looking at and only returns if they match.
This commit is contained in:
Peter Serwylo 2017-04-28 08:33:58 +10:00
parent ccdd8a168c
commit 0d1e00b6cf

View File

@ -5,6 +5,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.support.annotation.Nullable;
import android.util.Log;
@ -68,33 +69,20 @@ public class AppUpdateStatusService extends IntentService {
return null;
}
// NOTE: This presumes SHA256 is the only supported hash. It seems like that is an assumption
// in more than one place in the F-Droid client. If this becomes a problem in the future, we
// can query the Apk table for `SELECT DISTINCT hashType FROM fdroid_apk` and then we can just
// try each of the hash types that have been specified in the metadata. Seems a bit overkill
// at the time of writing though.
Utils.debugLog(TAG, "Found package for " + downloadedInfo.packageName + ", checking its hash to see if it downloaded correctly.");
String hash = Utils.getBinaryHash(apkPath, "sha256");
List<Apk> apksMatchingHash = ApkProvider.Helper.findApksByHash(this, hash);
Utils.debugLog(TAG, "Found " + apksMatchingHash.size() + " apk(s) matching the hash " + hash);
if (apksMatchingHash.size() == 0) {
Apk downloadedApk = findApkMatchingHash(apkPath);
if (downloadedApk == null) {
Utils.debugLog(TAG, "Either the apk wasn't downloaded fully, or the repo it came from has been disabled. Either way, not notifying the user about it.");
return null;
}
// It makes zero difference which apk we get from this list. By definition they all have
// the exact same hash, and are thus the same binary.
Apk downloadedApk = apksMatchingHash.get(0);
PackageInfo installedInfo = null;
try {
installedInfo = getPackageManager().getPackageInfo(downloadedApk.packageName, PackageManager.GET_META_DATA);
} catch (PackageManager.NameNotFoundException ignored) { }
if (installedInfo == null) {
if (AppUpdateStatusManager.getInstance(this).isPendingInstall(hash)) {
if (AppUpdateStatusManager.getInstance(this).isPendingInstall(downloadedApk.hash)) {
Utils.debugLog(TAG, downloadedApk.packageName + " is not installed, so presuming we need to notify the user about installing it.");
return downloadedApk;
} else {
@ -112,4 +100,35 @@ public class AppUpdateStatusService extends IntentService {
return downloadedApk;
}
/**
* There could be multiple apks with the same hash, provided by different repositories.
* This method looks for all matching records in the database. It then asks each of these
* {@link Apk} instances where they expect to be downloaded. If they expect to be downloaded
* to {@param apkPath} then that instance is returned.
*
* If no files have a matching hash, or only those which don't belong to the correct repo, then
* this will return null.
*/
@Nullable
private Apk findApkMatchingHash(File apkPath) {
// NOTE: This presumes SHA256 is the only supported hash. It seems like that is an assumption
// in more than one place in the F-Droid client. If this becomes a problem in the future, we
// can query the Apk table for `SELECT DISTINCT hashType FROM fdroid_apk` and then we can just
// try each of the hash types that have been specified in the metadata. Seems a bit overkill
// at the time of writing though.
String hash = Utils.getBinaryHash(apkPath, "sha256");
List<Apk> apksMatchingHash = ApkProvider.Helper.findApksByHash(this, hash);
Utils.debugLog(TAG, "Found " + apksMatchingHash.size() + " apk(s) matching the hash " + hash);
for (Apk apk : apksMatchingHash) {
if (apkPath.equals(ApkCache.getApkDownloadPath(this, Uri.parse(apk.getUrl())))) {
return apk;
}
}
return null;
}
}