From db4aa3b519349abfd6d30678e67a849ec0a359be Mon Sep 17 00:00:00 2001
From: Chirayu Desai <chirayudesai1@gmail.com>
Date: Tue, 18 Apr 2017 23:05:21 +0530
Subject: [PATCH 1/3] Installer: Use content URI on API >= 24 for
 DefaultInstaller fallback

* When there's a permission mismatch (#951, #890), the fallback
  DefaultInstaller is invoked, which enforces file and content schemes
  for API < 24 and >= 24 respectively.
* Use content URI in that case, which allows the fallback to work.
---
 .../fdroid/fdroid/installer/Installer.java    | 35 ++++++++++++-------
 1 file changed, 22 insertions(+), 13 deletions(-)

diff --git a/app/src/main/java/org/fdroid/fdroid/installer/Installer.java b/app/src/main/java/org/fdroid/fdroid/installer/Installer.java
index 0b72bf157..e77b83634 100644
--- a/app/src/main/java/org/fdroid/fdroid/installer/Installer.java
+++ b/app/src/main/java/org/fdroid/fdroid/installer/Installer.java
@@ -25,6 +25,7 @@ import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.net.Uri;
+import android.os.Build;
 import android.os.PatternMatcher;
 import android.support.v4.content.LocalBroadcastManager;
 import android.text.TextUtils;
@@ -228,6 +229,19 @@ public abstract class Installer {
      *                    installation of that specific APK
      */
     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());
+        } catch (IOException e) {
+            Log.e(TAG, e.getMessage(), e);
+            sendBroadcastInstall(downloadUri, Installer.ACTION_INSTALL_INTERRUPTED,
+                    e.getMessage());
+            return;
+        }
+
         try {
             // verify that permissions of the apk file match the ones from the apk object
             ApkVerifier apkVerifier = new ApkVerifier(context, localApkUri, apk);
@@ -245,23 +259,18 @@ public abstract class Installer {
                 Log.e(TAG, e.getMessage(), e);
                 Log.e(TAG, "Falling back to AOSP DefaultInstaller!");
                 DefaultInstaller defaultInstaller = new DefaultInstaller(context, apk);
-                defaultInstaller.installPackageInternal(localApkUri, downloadUri);
+                // 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);
+                }
                 return;
             }
         }
 
-        Uri sanitizedUri;
-        try {
-            // move apk file to private directory for installation and check hash
-            sanitizedUri = ApkFileProvider.getSafeUri(
-                    context, localApkUri, apk, supportsContentUri());
-        } catch (IOException e) {
-            Log.e(TAG, e.getMessage(), e);
-            sendBroadcastInstall(downloadUri, Installer.ACTION_INSTALL_INTERRUPTED,
-                    e.getMessage());
-            return;
-        }
-
         installPackageInternal(sanitizedUri, downloadUri);
     }
 

From 432f45254b569125446d6d69507b134c4e85e56b Mon Sep 17 00:00:00 2001
From: Chirayu Desai <chirayudesai1@gmail.com>
Date: Tue, 18 Apr 2017 23:49:16 +0530
Subject: [PATCH 2/3] PrivilegedInstaller: fallback to default installer for
 uninstall

* On API >= 24, in cases when the installer package name is not set
  to privext, the system won't let us uninstall.
* Fallback to the DefaultInstaller so that uninstall still works.
---
 .../fdroid/fdroid/compat/PackageManagerCompat.java   |  2 ++
 .../fdroid/installer/InstallManagerService.java      |  6 +++++-
 .../java/org/fdroid/fdroid/installer/Installer.java  | 12 ++++++++++++
 .../fdroid/fdroid/installer/PrivilegedInstaller.java |  5 +----
 4 files changed, 20 insertions(+), 5 deletions(-)

diff --git a/app/src/main/java/org/fdroid/fdroid/compat/PackageManagerCompat.java b/app/src/main/java/org/fdroid/fdroid/compat/PackageManagerCompat.java
index 0daf49d6b..9c625c4ac 100644
--- a/app/src/main/java/org/fdroid/fdroid/compat/PackageManagerCompat.java
+++ b/app/src/main/java/org/fdroid/fdroid/compat/PackageManagerCompat.java
@@ -29,6 +29,8 @@ public class PackageManagerCompat {
                 mPm.setInstallerPackageName(packageName, "org.fdroid.fdroid");
             }
             Utils.debugLog(TAG, "Installer package name for " + packageName + " set successfully");
+        } catch (SecurityException e) {
+            throw new SecurityException(e);
         } catch (Exception e) {
             // Many problems can occur:
             //  * App wasn't installed due to incompatibility
diff --git a/app/src/main/java/org/fdroid/fdroid/installer/InstallManagerService.java b/app/src/main/java/org/fdroid/fdroid/installer/InstallManagerService.java
index e36d21f4c..1237d5181 100644
--- a/app/src/main/java/org/fdroid/fdroid/installer/InstallManagerService.java
+++ b/app/src/main/java/org/fdroid/fdroid/installer/InstallManagerService.java
@@ -338,7 +338,11 @@ public class InstallManagerService extends Service {
                         Apk apkComplete =  appUpdateStatusManager.getApk(downloadUrl);
 
                         if (apkComplete != null) {
-                            PackageManagerCompat.setInstaller(context, getPackageManager(), apkComplete.packageName);
+                            try {
+                                PackageManagerCompat.setInstaller(context, getPackageManager(), apkComplete.packageName);
+                            } catch (SecurityException e) {
+                                // Will happen if we fell back to DefaultInstaller for some reason.
+                            }
                         }
                         localBroadcastManager.unregisterReceiver(this);
                         break;
diff --git a/app/src/main/java/org/fdroid/fdroid/installer/Installer.java b/app/src/main/java/org/fdroid/fdroid/installer/Installer.java
index e77b83634..7bf061005 100644
--- a/app/src/main/java/org/fdroid/fdroid/installer/Installer.java
+++ b/app/src/main/java/org/fdroid/fdroid/installer/Installer.java
@@ -31,6 +31,8 @@ import android.support.v4.content.LocalBroadcastManager;
 import android.text.TextUtils;
 import android.util.Log;
 
+import org.fdroid.fdroid.Utils;
+import org.fdroid.fdroid.compat.PackageManagerCompat;
 import org.fdroid.fdroid.data.Apk;
 import org.fdroid.fdroid.data.ApkProvider;
 import org.fdroid.fdroid.privileged.views.AppDiff;
@@ -138,6 +140,16 @@ public abstract class Installer {
             return null;
         }
 
+        try {
+            PackageManagerCompat.setInstaller(context, context.getPackageManager(), apk.packageName);
+        } catch (SecurityException e) {
+            Utils.debugLog(TAG, "Falling back to default installer for uninstall");
+            Intent intent = new Intent(context, DefaultInstallerActivity.class);
+            intent.setAction(DefaultInstallerActivity.ACTION_UNINSTALL_PACKAGE);
+            intent.putExtra(Installer.EXTRA_APK, apk);
+            return intent;
+        }
+
         Intent intent = new Intent(context, UninstallDialogActivity.class);
         intent.putExtra(Installer.EXTRA_APK, apk);
 
diff --git a/app/src/main/java/org/fdroid/fdroid/installer/PrivilegedInstaller.java b/app/src/main/java/org/fdroid/fdroid/installer/PrivilegedInstaller.java
index e0e9a8623..719b64288 100644
--- a/app/src/main/java/org/fdroid/fdroid/installer/PrivilegedInstaller.java
+++ b/app/src/main/java/org/fdroid/fdroid/installer/PrivilegedInstaller.java
@@ -34,6 +34,7 @@ import android.util.Log;
 
 import org.fdroid.fdroid.Preferences;
 import org.fdroid.fdroid.R;
+import org.fdroid.fdroid.Utils;
 import org.fdroid.fdroid.compat.PackageManagerCompat;
 import org.fdroid.fdroid.data.Apk;
 import org.fdroid.fdroid.privileged.IPrivilegedCallback;
@@ -395,10 +396,6 @@ public class PrivilegedInstaller extends Installer {
             }
         };
 
-        /*
-         * Set installer to the privileged extension
-         */
-        PackageManagerCompat.setInstaller(context, context.getPackageManager(), apk.packageName);
         Intent serviceIntent = new Intent(PRIVILEGED_EXTENSION_SERVICE_INTENT);
         serviceIntent.setPackage(PRIVILEGED_EXTENSION_PACKAGE_NAME);
         context.getApplicationContext().bindService(serviceIntent, mServiceConnection,

From 06dff8184bdd585343e6a62f88aa51745d1471c0 Mon Sep 17 00:00:00 2001
From: Hans-Christoph Steiner <hans@eds.org>
Date: Tue, 18 Apr 2017 21:07:16 +0200
Subject: [PATCH 3/3] no need to catch an exception, only to throw it

Just let the SecurityException be throwing where it originated.

From PMD: "A catch statement that catches an exception only to wrap it
in a new instance of the same type of exception and throw it should be
avoided".

This cleans up a little from !482.  Ctrl-Alt-L and Ctrl-Alt-O before
committing! :-)
---
 .../java/org/fdroid/fdroid/compat/PackageManagerCompat.java  | 5 +----
 .../org/fdroid/fdroid/installer/PrivilegedInstaller.java     | 3 ---
 2 files changed, 1 insertion(+), 7 deletions(-)

diff --git a/app/src/main/java/org/fdroid/fdroid/compat/PackageManagerCompat.java b/app/src/main/java/org/fdroid/fdroid/compat/PackageManagerCompat.java
index 9c625c4ac..7c02411cc 100644
--- a/app/src/main/java/org/fdroid/fdroid/compat/PackageManagerCompat.java
+++ b/app/src/main/java/org/fdroid/fdroid/compat/PackageManagerCompat.java
@@ -5,9 +5,8 @@ import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.Build;
 import android.util.Log;
-
-import org.fdroid.fdroid.installer.PrivilegedInstaller;
 import org.fdroid.fdroid.Utils;
+import org.fdroid.fdroid.installer.PrivilegedInstaller;
 
 public class PackageManagerCompat {
 
@@ -29,8 +28,6 @@ public class PackageManagerCompat {
                 mPm.setInstallerPackageName(packageName, "org.fdroid.fdroid");
             }
             Utils.debugLog(TAG, "Installer package name for " + packageName + " set successfully");
-        } catch (SecurityException e) {
-            throw new SecurityException(e);
         } catch (Exception e) {
             // Many problems can occur:
             //  * App wasn't installed due to incompatibility
diff --git a/app/src/main/java/org/fdroid/fdroid/installer/PrivilegedInstaller.java b/app/src/main/java/org/fdroid/fdroid/installer/PrivilegedInstaller.java
index 719b64288..93b077ec9 100644
--- a/app/src/main/java/org/fdroid/fdroid/installer/PrivilegedInstaller.java
+++ b/app/src/main/java/org/fdroid/fdroid/installer/PrivilegedInstaller.java
@@ -31,11 +31,8 @@ import android.os.Build;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
-
 import org.fdroid.fdroid.Preferences;
 import org.fdroid.fdroid.R;
-import org.fdroid.fdroid.Utils;
-import org.fdroid.fdroid.compat.PackageManagerCompat;
 import org.fdroid.fdroid.data.Apk;
 import org.fdroid.fdroid.privileged.IPrivilegedCallback;
 import org.fdroid.fdroid.privileged.IPrivilegedService;