clean up content:// vs file:// logic in installation process
This hopefully makes apparent which pieces are only related to APKs, and which pieces are used for all installable file types (media, OTA ZIPs, etc) ExtensionInstaller only works on < android-20 anyway, so that's self- enforcing in terms of URI scheme: it'll only ever see file:// URIs.
This commit is contained in:
parent
32e5471d6c
commit
72fcc3d2c5
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -31,7 +31,6 @@ 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;
|
||||
@ -195,7 +194,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);
|
||||
@ -247,7 +246,7 @@ 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);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, e.getMessage(), e);
|
||||
sendBroadcastInstall(downloadUri, Installer.ACTION_INSTALL_INTERRUPTED, e.getMessage());
|
||||
@ -270,7 +269,7 @@ public abstract class Installer {
|
||||
Log.e(TAG, e.getMessage(), e);
|
||||
Log.e(TAG, "Falling back to AOSP DefaultInstaller!");
|
||||
DefaultInstaller defaultInstaller = new DefaultInstaller(context, apk);
|
||||
// https://code.google.com/p/android/issues/detail?id=205827
|
||||
// https://issuetracker.google.com/issues/37091886
|
||||
if (Build.VERSION.SDK_INT >= 24) {
|
||||
// content scheme for N and above
|
||||
defaultInstaller.installPackageInternal(sanitizedUri, downloadUri);
|
||||
@ -298,10 +297,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