Merge branch 'fix-837--file-provider-uri' into 'master'
Use file provider when bluetoothing apks See merge request !511
This commit is contained in:
commit
064be9482d
@ -54,12 +54,14 @@ import org.fdroid.fdroid.compat.PRNGFixes;
|
||||
import org.fdroid.fdroid.data.AppProvider;
|
||||
import org.fdroid.fdroid.data.InstalledAppProviderService;
|
||||
import org.fdroid.fdroid.data.Repo;
|
||||
import org.fdroid.fdroid.installer.ApkFileProvider;
|
||||
import org.fdroid.fdroid.data.SanitizedFile;
|
||||
import org.fdroid.fdroid.installer.InstallHistoryService;
|
||||
import org.fdroid.fdroid.net.ImageLoaderForUIL;
|
||||
import org.fdroid.fdroid.net.WifiStateChangeService;
|
||||
import sun.net.www.protocol.bluetooth.Handler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.net.URLStreamHandler;
|
||||
import java.net.URLStreamHandlerFactory;
|
||||
@ -368,20 +370,21 @@ public class FDroidApp extends Application {
|
||||
if (resultCode == Activity.RESULT_CANCELED) {
|
||||
return;
|
||||
}
|
||||
|
||||
String bluetoothPackageName = null;
|
||||
String className = null;
|
||||
boolean found = false;
|
||||
Intent sendBt = null;
|
||||
|
||||
try {
|
||||
PackageManager pm = getPackageManager();
|
||||
ApplicationInfo appInfo = pm.getApplicationInfo(packageName,
|
||||
PackageManager.GET_META_DATA);
|
||||
ApplicationInfo appInfo = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA);
|
||||
sendBt = new Intent(Intent.ACTION_SEND);
|
||||
// The APK type is blocked by stock Android, so use zip
|
||||
// sendBt.setType("application/vnd.android.package-archive");
|
||||
|
||||
// The APK type ("application/vnd.android.package-archive") is blocked by stock Android, so use zip
|
||||
sendBt.setType("application/zip");
|
||||
sendBt.putExtra(Intent.EXTRA_STREAM,
|
||||
Uri.parse("file://" + appInfo.publicSourceDir));
|
||||
sendBt.putExtra(Intent.EXTRA_STREAM, ApkFileProvider.getSafeUri(this, appInfo));
|
||||
|
||||
// not all devices have the same Bluetooth Activities, so
|
||||
// let's find it
|
||||
for (ResolveInfo info : pm.queryIntentActivities(sendBt, 0)) {
|
||||
@ -396,7 +399,11 @@ public class FDroidApp extends Application {
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Log.e(TAG, "Could not get application info to send via bluetooth", e);
|
||||
found = false;
|
||||
} catch (IOException e) {
|
||||
Exception toLog = new RuntimeException("Error preparing file to send via Bluetooth", e);
|
||||
ACRA.getErrorReporter().handleException(toLog, false);
|
||||
}
|
||||
|
||||
if (sendBt != null) {
|
||||
if (found) {
|
||||
sendBt.setClassName(bluetoothPackageName, className);
|
||||
|
@ -20,6 +20,7 @@
|
||||
package org.fdroid.fdroid.installer;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.net.Uri;
|
||||
|
||||
import com.nostra13.universalimageloader.utils.StorageUtils;
|
||||
@ -36,6 +37,15 @@ public class ApkCache {
|
||||
|
||||
private static final String CACHE_DIR = "apks";
|
||||
|
||||
/**
|
||||
* Same as {@link #copyApkFromCacheToFiles(Context, File, Apk)}, except it does not need to
|
||||
* verify the hash after copying. This is because we are copying from an installed apk, which
|
||||
* other apps do not have permission to modify.
|
||||
*/
|
||||
public static SanitizedFile copyInstalledApkToFiles(Context context, ApplicationInfo appInfo) throws IOException {
|
||||
return copyApkToFiles(context, new File(appInfo.publicSourceDir), false, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the APK to the safe location inside of the protected area
|
||||
* of the app to prevent attacks based on other apps swapping the file
|
||||
@ -44,12 +54,24 @@ public class ApkCache {
|
||||
*/
|
||||
public static SanitizedFile copyApkFromCacheToFiles(Context context, File apkFile, Apk expectedApk)
|
||||
throws IOException {
|
||||
return copyApkToFiles(context, apkFile, true, expectedApk.hash, expectedApk.hashType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy an APK from {@param apkFile} to our internal files directory for 20 minutes.
|
||||
* @param verifyHash If the file was just downloaded, then you should mark this as true and
|
||||
* request the file to be verified once it has finished copying. Otherwise,
|
||||
* if the app was installed from part of the system where it can't be tampered
|
||||
* with (e.g. installed apks on disk) then
|
||||
*/
|
||||
private static SanitizedFile copyApkToFiles(Context context, File apkFile, boolean verifyHash, String hash, String hashType)
|
||||
throws IOException {
|
||||
SanitizedFile sanitizedApkFile = SanitizedFile.knownSanitized(
|
||||
File.createTempFile("install-", ".apk", context.getFilesDir()));
|
||||
FileUtils.copyFile(apkFile, sanitizedApkFile);
|
||||
|
||||
// verify copied file's hash with expected hash from Apk class
|
||||
if (!Hasher.isFileMatchingHash(sanitizedApkFile, expectedApk.hash, expectedApk.hashType)) {
|
||||
if (verifyHash && !Hasher.isFileMatchingHash(sanitizedApkFile, hash, hashType)) {
|
||||
FileUtils.deleteQuietly(apkFile);
|
||||
throw new IOException(apkFile + " failed to verify!");
|
||||
}
|
||||
|
@ -21,7 +21,9 @@ package org.fdroid.fdroid.installer;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.support.v4.content.FileProvider;
|
||||
|
||||
import org.fdroid.fdroid.data.Apk;
|
||||
@ -47,6 +49,11 @@ public class ApkFileProvider extends FileProvider {
|
||||
|
||||
private static final String AUTHORITY = "org.fdroid.fdroid.installer.ApkFileProvider";
|
||||
|
||||
public static Uri getSafeUri(Context context, ApplicationInfo appInfo) throws IOException {
|
||||
SanitizedFile tempApkFile = ApkCache.copyInstalledApkToFiles(context, appInfo);
|
||||
return getSafeUri(context, tempApkFile, Build.VERSION.SDK_INT >= 24);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the APK into private data directory of F-Droid and returns a "file" or "content" Uri
|
||||
* to be used for installation.
|
||||
@ -54,14 +61,17 @@ public class ApkFileProvider extends FileProvider {
|
||||
public static Uri getSafeUri(Context context, Uri localApkUri, Apk expectedApk, boolean useContentUri)
|
||||
throws IOException {
|
||||
File apkFile = new File(localApkUri.getPath());
|
||||
SanitizedFile tempApkFile = ApkCache.copyApkFromCacheToFiles(context, apkFile, expectedApk);
|
||||
return getSafeUri(context, tempApkFile, useContentUri);
|
||||
|
||||
SanitizedFile sanitizedApkFile =
|
||||
ApkCache.copyApkFromCacheToFiles(context, apkFile, expectedApk);
|
||||
}
|
||||
|
||||
private static Uri getSafeUri(Context context, SanitizedFile tempApkFile, boolean useContentUri) {
|
||||
if (useContentUri) {
|
||||
// return a content Uri using support libs FileProvider
|
||||
Uri apkUri = getUriForFile(context, AUTHORITY, sanitizedApkFile);
|
||||
Uri apkUri = getUriForFile(context, AUTHORITY, tempApkFile);
|
||||
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;
|
||||
}
|
||||
|
||||
@ -70,9 +80,9 @@ public class ApkFileProvider extends FileProvider {
|
||||
// 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.
|
||||
sanitizedApkFile.setReadable(true, false);
|
||||
tempApkFile.setReadable(true, false);
|
||||
|
||||
return Uri.fromFile(sanitizedApkFile);
|
||||
return Uri.fromFile(tempApkFile);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -245,12 +245,10 @@ public abstract class Installer {
|
||||
|
||||
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, supportsContentUri());
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, e.getMessage(), e);
|
||||
sendBroadcastInstall(downloadUri, Installer.ACTION_INSTALL_INTERRUPTED,
|
||||
e.getMessage());
|
||||
sendBroadcastInstall(downloadUri, Installer.ACTION_INSTALL_INTERRUPTED, e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
@ -260,8 +258,7 @@ public abstract class Installer {
|
||||
apkVerifier.verifyApk();
|
||||
} catch (ApkVerifier.ApkVerificationException e) {
|
||||
Log.e(TAG, e.getMessage(), e);
|
||||
sendBroadcastInstall(downloadUri, Installer.ACTION_INSTALL_INTERRUPTED,
|
||||
e.getMessage());
|
||||
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
|
||||
|
Loading…
x
Reference in New Issue
Block a user