Merge branch 'fallback-install' into 'master'

Use AOSP Installer if permission check fails

If the old repo index is used and the permission check fails in ApkVerifier, this allows a fallback to the AOSP DefaultInstaller to show all permissions.
This has been discussed in https://gitlab.com/fdroid/fdroidclient/issues/704

Unfortunately, this shows our permission screen before download and then afterwards when the ``ApkVerifier`` fails the permission screen of the AOSP DefaultInstaller, i.e., the user sees two permission screen which she needs to acknowledge. This should only happen if an old repo format is used, thus I think this is okay. I don't know of any other solution to this problem.

See merge request !369
This commit is contained in:
Daniel Martí 2016-07-31 14:20:00 +00:00
commit 59c25dd998
2 changed files with 35 additions and 7 deletions

View File

@ -51,7 +51,7 @@ class ApkVerifier {
this.pm = context.getPackageManager(); this.pm = context.getPackageManager();
} }
public void verifyApk() throws ApkVerificationException { public void verifyApk() throws ApkVerificationException, ApkPermissionUnequalException {
// parse downloaded apk file locally // parse downloaded apk file locally
PackageInfo localApkInfo = pm.getPackageArchiveInfo( PackageInfo localApkInfo = pm.getPackageArchiveInfo(
localApkUri.getPath(), PackageManager.GET_PERMISSIONS); localApkUri.getPath(), PackageManager.GET_PERMISSIONS);
@ -78,9 +78,9 @@ class ApkVerifier {
// Thus, containsAll() instead of equals() is used! // Thus, containsAll() instead of equals() is used!
// See also https://gitlab.com/fdroid/fdroidclient/issues/703 // See also https://gitlab.com/fdroid/fdroidclient/issues/703
if (!expectedPermissions.containsAll(localPermissions)) { if (!expectedPermissions.containsAll(localPermissions)) {
throw new ApkVerificationException( throw new ApkPermissionUnequalException(
"Permissions of the apk file are not a true subset of the permissions listed by the repo," + "Permissions of the apk file are not a true subset of the permissions listed by the repo," +
" i.e., some permissions have not been shown to the user!"); " i.e., some permissions have not been shown to the user!");
} }
int localTargetSdkVersion = localApkInfo.applicationInfo.targetSdkVersion; int localTargetSdkVersion = localApkInfo.applicationInfo.targetSdkVersion;
@ -93,7 +93,6 @@ class ApkVerifier {
} else if (localTargetSdkVersion != expectedTargetSdkVersion) { } else if (localTargetSdkVersion != expectedTargetSdkVersion) {
throw new ApkVerificationException("TargetSdkVersion of apk file is not the expected targetSdkVersion!"); throw new ApkVerificationException("TargetSdkVersion of apk file is not the expected targetSdkVersion!");
} }
} }
private HashSet<String> getLocalPermissionsSet(PackageInfo localApkInfo) { private HashSet<String> getLocalPermissionsSet(PackageInfo localApkInfo) {
@ -116,4 +115,15 @@ class ApkVerifier {
} }
} }
public static class ApkPermissionUnequalException extends Exception {
ApkPermissionUnequalException(String message) {
super(message);
}
ApkPermissionUnequalException(Throwable cause) {
super(cause);
}
}
} }

View File

@ -226,17 +226,35 @@ public abstract class Installer {
* @param apk apk object of the app that should be installed * @param apk apk object of the app that should be installed
*/ */
public void installPackage(Uri localApkUri, Uri downloadUri, Apk apk) { public void installPackage(Uri localApkUri, Uri downloadUri, Apk apk) {
Uri sanitizedUri;
try { try {
// verify that permissions of the apk file match the ones from the apk object // verify that permissions of the apk file match the ones from the apk object
ApkVerifier apkVerifier = new ApkVerifier(context, localApkUri, apk); ApkVerifier apkVerifier = new ApkVerifier(context, localApkUri, apk);
apkVerifier.verifyApk(); apkVerifier.verifyApk();
} catch (ApkVerifier.ApkVerificationException e) {
Log.e(TAG, e.getMessage(), e);
sendBroadcastInstall(downloadUri, Installer.ACTION_INSTALL_INTERRUPTED,
e.getMessage());
return;
} catch (ApkVerifier.ApkPermissionUnequalException e) {
// if permissions of apk are not the ones listed in the repo
// and an unattended installer is used, a wrong permission screen
// has been shown, thus fallback to AOSP DefaultInstaller!
if (isUnattended()) {
Log.e(TAG, e.getMessage(), e);
Log.e(TAG, "Falling back to AOSP DefaultInstaller!");
DefaultInstaller defaultInstaller = new DefaultInstaller(context);
defaultInstaller.installPackageInternal(localApkUri, downloadUri, apk);
return;
}
}
Uri sanitizedUri;
try {
// move apk file to private directory for installation and check hash // move apk file to private directory for installation and check hash
sanitizedUri = ApkFileProvider.getSafeUri( sanitizedUri = ApkFileProvider.getSafeUri(
context, localApkUri, apk, supportsContentUri()); context, localApkUri, apk, supportsContentUri());
} catch (ApkVerifier.ApkVerificationException | IOException e) { } catch (IOException e) {
Log.e(TAG, "ApkVerifier / ApkFileProvider failed", e); Log.e(TAG, e.getMessage(), e);
sendBroadcastInstall(downloadUri, Installer.ACTION_INSTALL_INTERRUPTED, sendBroadcastInstall(downloadUri, Installer.ACTION_INSTALL_INTERRUPTED,
e.getMessage()); e.getMessage());
return; return;