Merge branch 'final-1.1-fixes' into 'master'
Final 1.1 fixes Closes #1310 See merge request fdroid/fdroidclient!658
This commit is contained in:
		
						commit
						51b22d50b6
					
				@ -25,7 +25,6 @@ import android.content.pm.PackageInfo;
 | 
			
		||||
import android.net.Uri;
 | 
			
		||||
import android.os.Build;
 | 
			
		||||
import android.support.v4.content.FileProvider;
 | 
			
		||||
 | 
			
		||||
import org.fdroid.fdroid.BuildConfig;
 | 
			
		||||
import org.fdroid.fdroid.data.Apk;
 | 
			
		||||
import org.fdroid.fdroid.data.SanitizedFile;
 | 
			
		||||
@ -34,7 +33,8 @@ import java.io.File;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This class has helper methods for preparing apks for installation.
 | 
			
		||||
 * Helper methods for preparing APKs and arbitrary files for installation,
 | 
			
		||||
 * either locally or for sending via bluetooth.
 | 
			
		||||
 * <p/>
 | 
			
		||||
 * APK handling for installations:
 | 
			
		||||
 * 1. APKs are downloaded into a cache directory that is either created on SD card
 | 
			
		||||
@ -56,34 +56,44 @@ public class ApkFileProvider extends FileProvider {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Copies the APK into private data directory of F-Droid and returns a "file" or "content" Uri
 | 
			
		||||
     * to be used for installation.
 | 
			
		||||
     * Copies the APK into private data directory of F-Droid and returns a
 | 
			
		||||
     * {@code file://} or {@code content://} URI to be used for the
 | 
			
		||||
     * actual installation process.  Only APKs will ever use a {@code content://}
 | 
			
		||||
     * URI, any other file will always use a {@code file://} URI since F-Droid
 | 
			
		||||
     * itself handles their whole installation process.
 | 
			
		||||
     */
 | 
			
		||||
    public static Uri getSafeUri(Context context, Uri localApkUri, Apk expectedApk, boolean useContentUri)
 | 
			
		||||
    public static Uri getSafeUri(Context context, Uri localApkUri, Apk expectedApk)
 | 
			
		||||
            throws IOException {
 | 
			
		||||
        File apkFile = new File(localApkUri.getPath());
 | 
			
		||||
        SanitizedFile tempApkFile = ApkCache.copyApkFromCacheToFiles(context, apkFile, expectedApk);
 | 
			
		||||
        return getSafeUri(context, tempApkFile, useContentUri);
 | 
			
		||||
        return getSafeUri(context, tempApkFile,
 | 
			
		||||
                Build.VERSION.SDK_INT >= 24 && expectedApk.isApk());
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static Uri getSafeUri(Context context, SanitizedFile tempApkFile, boolean useContentUri) {
 | 
			
		||||
    /**
 | 
			
		||||
     * Return a {@link Uri} for all install processes to install this package
 | 
			
		||||
     * from.  This supports APKs and all other supported files.  It also
 | 
			
		||||
     * supports all installation methods, e.g. default, privileged, etc.
 | 
			
		||||
     * It can return either a {@code content://} or {@code file://} URI.
 | 
			
		||||
     * <p>
 | 
			
		||||
     * APKs need to be world readable, so that the Android system installer
 | 
			
		||||
     * is able to read it.  Saving it into external storage to send it to the
 | 
			
		||||
     * installer have access is insecure, because apps with permission to write
 | 
			
		||||
     * to the external storage can overwrite the app between F-Droid asking for
 | 
			
		||||
     * it to be installed and the installer actually installing it.
 | 
			
		||||
     */
 | 
			
		||||
    private static Uri getSafeUri(Context context, SanitizedFile tempFile, boolean useContentUri) {
 | 
			
		||||
        if (useContentUri) {
 | 
			
		||||
            // return a content Uri using support libs FileProvider
 | 
			
		||||
            Uri apkUri = getUriForFile(context, AUTHORITY, tempApkFile);
 | 
			
		||||
            Uri apkUri = getUriForFile(context, AUTHORITY, tempFile);
 | 
			
		||||
            context.grantUriPermission("org.fdroid.fdroid.privileged", apkUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
 | 
			
		||||
            context.grantUriPermission("com.android.bluetooth", apkUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
 | 
			
		||||
            return apkUri;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Need the apk to be world readable, so that the installer is able to read it.
 | 
			
		||||
        // Note that saving it into external storage for the purpose of letting the installer
 | 
			
		||||
        // have access is insecure, because apps with permission to write to the external
 | 
			
		||||
        // storage can overwrite the app between F-Droid asking for it to be installed and
 | 
			
		||||
        // the installer actually installing it.
 | 
			
		||||
        tempApkFile.setReadable(true, false);
 | 
			
		||||
        tempFile.setReadable(true, false);
 | 
			
		||||
 | 
			
		||||
        return Uri.fromFile(tempApkFile);
 | 
			
		||||
        return Uri.fromFile(tempFile);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -24,8 +24,6 @@ import android.app.PendingIntent;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import android.net.Uri;
 | 
			
		||||
import android.os.Build;
 | 
			
		||||
 | 
			
		||||
import org.fdroid.fdroid.data.Apk;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@ -82,10 +80,4 @@ public class DefaultInstaller extends Installer {
 | 
			
		||||
    protected boolean isUnattended() {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected boolean supportsContentUri() {
 | 
			
		||||
        // Android N only supports content Uris
 | 
			
		||||
        return Build.VERSION.SDK_INT >= 24;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -24,7 +24,6 @@ import android.app.PendingIntent;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import android.net.Uri;
 | 
			
		||||
 | 
			
		||||
import org.fdroid.fdroid.BuildConfig;
 | 
			
		||||
import org.fdroid.fdroid.data.Apk;
 | 
			
		||||
import org.fdroid.fdroid.privileged.install.InstallExtensionDialogActivity;
 | 
			
		||||
@ -33,11 +32,16 @@ import java.io.File;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Special Installer that is only useful to install the Privileged Extension apk
 | 
			
		||||
 * as a privileged app into the system partition of Android.
 | 
			
		||||
 * as a privileged app into the system partition of Android.  It is deprecated
 | 
			
		||||
 * because it cannot work on Android versions newer than {@code android-20} or so,
 | 
			
		||||
 * due to increased SELinux enforcement that restricts what even root can do.
 | 
			
		||||
 * <p/>
 | 
			
		||||
 * This is installer requires user interaction and thus install/uninstall directly
 | 
			
		||||
 * return PendingIntents.
 | 
			
		||||
 *
 | 
			
		||||
 * @see <a href="https://www.androidauthority.com/chainfire-rooting-android-lollipop-541458/">Chainfire talks Android Lollipop and the future of rooting</a>
 | 
			
		||||
 */
 | 
			
		||||
@Deprecated
 | 
			
		||||
public class ExtensionInstaller extends Installer {
 | 
			
		||||
 | 
			
		||||
    ExtensionInstaller(Context context, Apk apk) {
 | 
			
		||||
@ -94,9 +98,4 @@ public class ExtensionInstaller extends Installer {
 | 
			
		||||
    protected boolean isUnattended() {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected boolean supportsContentUri() {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -85,10 +85,4 @@ public class FileInstaller extends Installer {
 | 
			
		||||
    protected boolean isUnattended() {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected boolean supportsContentUri() {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -30,8 +30,6 @@ import android.os.Build;
 | 
			
		||||
import android.os.PatternMatcher;
 | 
			
		||||
import android.support.v4.content.LocalBroadcastManager;
 | 
			
		||||
import android.text.TextUtils;
 | 
			
		||||
import android.util.Log;
 | 
			
		||||
 | 
			
		||||
import org.fdroid.fdroid.Utils;
 | 
			
		||||
import org.fdroid.fdroid.data.Apk;
 | 
			
		||||
import org.fdroid.fdroid.data.ApkProvider;
 | 
			
		||||
@ -144,7 +142,7 @@ public abstract class Installer {
 | 
			
		||||
        PackageManager pm = context.getPackageManager();
 | 
			
		||||
        if (Build.VERSION.SDK_INT >= 24 && (
 | 
			
		||||
                pm.getInstallerPackageName(apk.packageName).equals("com.android.packageinstaller")
 | 
			
		||||
                || pm.getInstallerPackageName(apk.packageName).equals("com.google.android.packageinstaller"))) {
 | 
			
		||||
                        || pm.getInstallerPackageName(apk.packageName).equals("com.google.android.packageinstaller"))) {
 | 
			
		||||
            Utils.debugLog(TAG, "Falling back to default installer for uninstall");
 | 
			
		||||
            Intent intent = new Intent(context, DefaultInstallerActivity.class);
 | 
			
		||||
            intent.setAction(DefaultInstallerActivity.ACTION_UNINSTALL_PACKAGE);
 | 
			
		||||
@ -195,7 +193,7 @@ public abstract class Installer {
 | 
			
		||||
        sendBroadcastUninstall(action, pendingIntent, null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void sendBroadcastUninstall(String action, PendingIntent pendingIntent, String errorMessage) {
 | 
			
		||||
    private void sendBroadcastUninstall(String action, PendingIntent pendingIntent, String errorMessage) {
 | 
			
		||||
        Uri uri = Uri.fromParts("package", apk.packageName, null);
 | 
			
		||||
 | 
			
		||||
        Intent intent = new Intent(action);
 | 
			
		||||
@ -236,20 +234,29 @@ public abstract class Installer {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Install apk
 | 
			
		||||
     * Install apk given the URI that points to the local APK file, and the
 | 
			
		||||
     * download URI to identify which session this belongs to.  This first
 | 
			
		||||
     * moves the APK file to private directory for the installation process
 | 
			
		||||
     * to read from.  Then the hash of the APK is checked against the
 | 
			
		||||
     * {@link Apk} instance provided when this {@code Installer} object was
 | 
			
		||||
     * instantiated.  The list of permissions in the APK file and the
 | 
			
		||||
     * {@code Apk} instance are compared, if they do not match, then the user
 | 
			
		||||
     * is prompted with the system installer dialog, which shows all the
 | 
			
		||||
     * permissions that the APK is requesting.
 | 
			
		||||
     *
 | 
			
		||||
     * @param localApkUri points to the local copy of the APK to be installed
 | 
			
		||||
     * @param downloadUri serves as the unique ID for all actions related to the
 | 
			
		||||
     *                    installation of that specific APK
 | 
			
		||||
     * @see InstallManagerService
 | 
			
		||||
     * @see <a href="https://issuetracker.google.com/issues/37091886">ACTION_INSTALL_PACKAGE Fails For Any Possible Uri</a>
 | 
			
		||||
     */
 | 
			
		||||
    public void installPackage(Uri localApkUri, Uri downloadUri) {
 | 
			
		||||
        Uri sanitizedUri;
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            // move apk file to private directory for installation and check hash
 | 
			
		||||
            sanitizedUri = ApkFileProvider.getSafeUri(context, localApkUri, apk, supportsContentUri());
 | 
			
		||||
            sanitizedUri = ApkFileProvider.getSafeUri(context, localApkUri, apk);
 | 
			
		||||
        } catch (IOException e) {
 | 
			
		||||
            Log.e(TAG, e.getMessage(), e);
 | 
			
		||||
            Utils.debugLog(TAG, e.getMessage(), e);
 | 
			
		||||
            sendBroadcastInstall(downloadUri, Installer.ACTION_INSTALL_INTERRUPTED, e.getMessage());
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
@ -259,7 +266,7 @@ public abstract class Installer {
 | 
			
		||||
            ApkVerifier apkVerifier = new ApkVerifier(context, localApkUri, apk);
 | 
			
		||||
            apkVerifier.verifyApk();
 | 
			
		||||
        } catch (ApkVerifier.ApkVerificationException e) {
 | 
			
		||||
            Log.e(TAG, e.getMessage(), e);
 | 
			
		||||
            Utils.debugLog(TAG, e.getMessage(), e);
 | 
			
		||||
            sendBroadcastInstall(downloadUri, Installer.ACTION_INSTALL_INTERRUPTED, e.getMessage());
 | 
			
		||||
            return;
 | 
			
		||||
        } catch (ApkVerifier.ApkPermissionUnequalException e) {
 | 
			
		||||
@ -267,17 +274,10 @@ public abstract class Installer {
 | 
			
		||||
            // 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!");
 | 
			
		||||
                Utils.debugLog(TAG, e.getMessage(), e);
 | 
			
		||||
                Utils.debugLog(TAG, "Falling back to AOSP DefaultInstaller!");
 | 
			
		||||
                DefaultInstaller defaultInstaller = new DefaultInstaller(context, apk);
 | 
			
		||||
                // https://code.google.com/p/android/issues/detail?id=205827
 | 
			
		||||
                if (Build.VERSION.SDK_INT >= 24) {
 | 
			
		||||
                    // content scheme for N and above
 | 
			
		||||
                    defaultInstaller.installPackageInternal(sanitizedUri, downloadUri);
 | 
			
		||||
                } else {
 | 
			
		||||
                    // file scheme for below N
 | 
			
		||||
                    defaultInstaller.installPackageInternal(localApkUri, downloadUri);
 | 
			
		||||
                }
 | 
			
		||||
                defaultInstaller.installPackageInternal(sanitizedUri, downloadUri);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@ -298,10 +298,4 @@ public abstract class Installer {
 | 
			
		||||
     * uninstall activities, without the system enforcing a user prompt.
 | 
			
		||||
     */
 | 
			
		||||
    protected abstract boolean isUnattended();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return true if the Installer supports content Uris and not just file Uris
 | 
			
		||||
     */
 | 
			
		||||
    protected abstract boolean supportsContentUri();
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -27,7 +27,6 @@ import android.content.Intent;
 | 
			
		||||
import android.content.ServiceConnection;
 | 
			
		||||
import android.content.pm.PackageManager;
 | 
			
		||||
import android.net.Uri;
 | 
			
		||||
import android.os.Build;
 | 
			
		||||
import android.os.IBinder;
 | 
			
		||||
import android.os.RemoteException;
 | 
			
		||||
import android.util.Log;
 | 
			
		||||
@ -404,9 +403,4 @@ public class PrivilegedInstaller extends Installer {
 | 
			
		||||
    protected boolean isUnattended() {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected boolean supportsContentUri() {
 | 
			
		||||
        return Build.VERSION.SDK_INT >= 24;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user