diff --git a/F-Droid/res/values/strings.xml b/F-Droid/res/values/strings.xml
index 737cbdc04..3c181aa36 100644
--- a/F-Droid/res/values/strings.xml
+++ b/F-Droid/res/values/strings.xml
@@ -308,8 +308,8 @@
The installation of F-Droid as a privileged app failed. The installation method is not supported by all Android distributions, please consult the F-Droid bug tracker for more information.
installing…
uninstalling…
- Do you want to install F-Droid as a privileged app?\nThis takes up to 10 seconds where <b>no user interface</b> is shown.
- Do you want to install F-Droid as a privileged app?\nThis takes up to 10 seconds where <b>no user interface</b> is shown and the device will be <b>rebooted</b> afterwards.
+ Do you want to install F-Droid as a privileged app?\nThis takes up to 10 seconds.
+ Do you want to install F-Droid as a privileged app?\nThis takes up to 10 seconds and the device will be <b>rebooted</b> afterwards.
Looks like you have root access on your device. You can now install F-Droid as a privileged app, tightly coupled with the Android operating system. This allows F-Droid to install, upgrade and uninstall apps on its own.
Do you want to uninstall F-Droid?
This will uninstall F-Droid completely.
diff --git a/F-Droid/src/org/fdroid/fdroid/installer/Installer.java b/F-Droid/src/org/fdroid/fdroid/installer/Installer.java
index 7eb69a6d5..77f3f9143 100644
--- a/F-Droid/src/org/fdroid/fdroid/installer/Installer.java
+++ b/F-Droid/src/org/fdroid/fdroid/installer/Installer.java
@@ -109,7 +109,7 @@ abstract public class Installer {
// system permissions and pref enabled -> SystemInstaller
boolean isSystemInstallerEnabled = Preferences.get().isSystemInstallerEnabled();
if (isSystemInstallerEnabled) {
- if (hasSystemPermissions(activity, pm)) {
+ if (PrivilegedInstaller.isAvailable(activity)) {
Utils.DebugLog(TAG, "system permissions -> SystemInstaller");
try {
@@ -147,17 +147,6 @@ abstract public class Installer {
return null;
}
- public static boolean hasSystemPermissions(Context context, PackageManager pm) {
- boolean hasInstallPermission =
- (pm.checkPermission(permission.INSTALL_PACKAGES, context.getPackageName())
- == PackageManager.PERMISSION_GRANTED);
- boolean hasDeletePermission =
- (pm.checkPermission(permission.DELETE_PACKAGES, context.getPackageName())
- == PackageManager.PERMISSION_GRANTED);
-
- return (hasInstallPermission && hasDeletePermission);
- }
-
public void installPackage(File apkFile) throws AndroidNotCompatibleException {
// check if file exists...
if (!apkFile.exists()) {
diff --git a/F-Droid/src/org/fdroid/fdroid/installer/PrivilegedInstaller.java b/F-Droid/src/org/fdroid/fdroid/installer/PrivilegedInstaller.java
index 1fa06dce8..23830c26b 100644
--- a/F-Droid/src/org/fdroid/fdroid/installer/PrivilegedInstaller.java
+++ b/F-Droid/src/org/fdroid/fdroid/installer/PrivilegedInstaller.java
@@ -29,6 +29,7 @@ import android.content.ServiceConnection;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AlertDialog;
@@ -84,7 +85,54 @@ public class PrivilegedInstaller extends Installer {
InstallerCallback callback) throws AndroidNotCompatibleException {
super(activity, pm, callback);
this.mActivity = activity;
+ }
+ public static boolean isAvailable(Context context) {
+
+ // check if installed
+ PackageManager pm = context.getPackageManager();
+ try {
+ pm.getPackageInfo(PRIVILEGED_PACKAGE_NAME, PackageManager.GET_ACTIVITIES);
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+
+ // check if it has the privileged permissions granted
+ final Object mutex = new Object();
+ final Bundle returnBundle = new Bundle();
+ ServiceConnection mServiceConnection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ IPrivilegedService privService = IPrivilegedService.Stub.asInterface(service);
+
+ try {
+ boolean hasPermissions = privService.hasPrivilegedPermissions();
+ returnBundle.putBoolean("has_permissions", hasPermissions);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException", e);
+ }
+
+ synchronized (mutex) {
+ mutex.notify();
+ }
+ }
+
+ public void onServiceDisconnected(ComponentName name) {
+ }
+ };
+ Intent serviceIntent = new Intent(PRIVILEGED_INTENT);
+ serviceIntent.setPackage(PRIVILEGED_PACKAGE_NAME);
+ context.getApplicationContext().bindService(serviceIntent, mServiceConnection,
+ Context.BIND_AUTO_CREATE);
+
+ synchronized (mutex) {
+ try {
+ mutex.wait(3000);
+ } catch (InterruptedException e) {
+ // don't care
+ }
+ }
+
+ return (returnBundle.getBoolean("has_permission", false));
}
@Override
diff --git a/F-Droid/src/org/fdroid/fdroid/privileged/install/InstallPrivileged.java b/F-Droid/src/org/fdroid/fdroid/privileged/install/InstallPrivileged.java
index 0b7d13bd3..c69292f55 100644
--- a/F-Droid/src/org/fdroid/fdroid/privileged/install/InstallPrivileged.java
+++ b/F-Droid/src/org/fdroid/fdroid/privileged/install/InstallPrivileged.java
@@ -38,18 +38,22 @@ import eu.chainfire.libsuperuser.Shell;
abstract class InstallPrivileged {
protected final Context context;
+ protected final String apkPath;
- public InstallPrivileged(final Context context) {
+ private static final String PACKAGE_NAME = "org.fdroid.fdroid.privileged";
+
+ public InstallPrivileged(final Context context, final String apkPath) {
this.context = context;
+ this.apkPath = apkPath;
}
- public static InstallPrivileged create(final Context context) {
+ public static InstallPrivileged create(final Context context, final String apkPath) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- return new LollipopImpl(context);
+ return new LollipopImpl(context, apkPath);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
- return new KitKatToLollipopImpl(context);
+ return new KitKatToLollipopImpl(context, apkPath);
} else {
- return new PreKitKatImpl(context);
+ return new PreKitKatImpl(context, apkPath);
}
}
@@ -65,10 +69,10 @@ abstract class InstallPrivileged {
final void runUninstall() {
final String[] commands = {
- "am force-stop org.fdroid.fdroid",
- "pm clear org.fdroid.fdroid",
+ "am force-stop " + PACKAGE_NAME,
+ "pm clear " + PACKAGE_NAME,
"mount -o rw,remount /system",
- "pm uninstall " + context.getPackageName(),
+ "pm uninstall " + PACKAGE_NAME,
"rm -f " + getInstallPath(),
"sleep 5",
"mount -o ro,remount /system"
@@ -82,26 +86,26 @@ abstract class InstallPrivileged {
}
protected String getInstallPath() {
- return getSystemFolder() + "FDroid.apk";
+ return getSystemFolder() + "FDroidPrivileged.apk";
}
private List getInstallCommands() {
final List commands = new ArrayList<>();
commands.add("mount -o rw,remount /system");
commands.addAll(getCopyToSystemCommands());
- commands.add("pm uninstall -k " + context.getPackageName()); // -k to retain data
+ commands.add("pm uninstall " + PACKAGE_NAME);
commands.add("mv " + getInstallPath() + ".tmp " + getInstallPath());
commands.add("pm install -r " + getInstallPath());
commands.add("sleep 5"); // wait until the app is really installed
commands.add("mount -o ro,remount /system");
- commands.add("am force-stop org.fdroid.fdroid");
+ commands.add("am force-stop " + PACKAGE_NAME);
commands.addAll(getPostInstallCommands());
return commands;
}
protected List getCopyToSystemCommands() {
final List commands = new ArrayList<>(2);
- commands.add("cat " + context.getPackageCodePath() + " > " + getInstallPath() + ".tmp");
+ commands.add("cat " + apkPath + " > " + getInstallPath() + ".tmp");
commands.add("chmod 644 " + getInstallPath() + ".tmp");
return commands;
}
@@ -114,8 +118,8 @@ abstract class InstallPrivileged {
private static class PreKitKatImpl extends InstallPrivileged {
- public PreKitKatImpl(Context context) {
- super(context);
+ public PreKitKatImpl(Context context, String apkPath) {
+ super(context, apkPath);
}
@Override
@@ -127,8 +131,8 @@ abstract class InstallPrivileged {
private static class KitKatToLollipopImpl extends InstallPrivileged {
- public KitKatToLollipopImpl(Context context) {
- super(context);
+ public KitKatToLollipopImpl(Context context, String apkPath) {
+ super(context, apkPath);
}
/**
@@ -148,8 +152,8 @@ abstract class InstallPrivileged {
*/
private static class LollipopImpl extends InstallPrivileged {
- public LollipopImpl(Context context) {
- super(context);
+ public LollipopImpl(Context context, String apkPath) {
+ super(context, apkPath);
}
@Override
@@ -167,7 +171,7 @@ abstract class InstallPrivileged {
*/
@Override
protected String getSystemFolder() {
- return "/system/priv-app/FDroid/";
+ return "/system/priv-app/FDroidPrivileged/";
}
/**
@@ -178,13 +182,13 @@ abstract class InstallPrivileged {
List commands = new ArrayList<>(3);
commands.add("mkdir -p " + getSystemFolder()); // create app directory if not existing
commands.add("chmod 755 " + getSystemFolder());
- commands.add("cat " + context.getPackageCodePath() + " > " + getInstallPath() + ".tmp");
+ commands.add("cat " + apkPath + " > " + getInstallPath() + ".tmp");
commands.add("chmod 644 " + getInstallPath() + ".tmp");
return commands;
}
/**
- * TODO: Currently only works with reboot
+ * NOTE: Only works with reboot
*
* File observers on /system/priv-app/ have been removed because they don't work with the new
* cluser-style layout. See
diff --git a/F-Droid/src/org/fdroid/fdroid/privileged/install/InstallPrivilegedDialogActivity.java b/F-Droid/src/org/fdroid/fdroid/privileged/install/InstallPrivilegedDialogActivity.java
index c5680e5be..b413cad0b 100644
--- a/F-Droid/src/org/fdroid/fdroid/privileged/install/InstallPrivilegedDialogActivity.java
+++ b/F-Droid/src/org/fdroid/fdroid/privileged/install/InstallPrivilegedDialogActivity.java
@@ -41,6 +41,7 @@ import org.fdroid.fdroid.FDroidApp;
import org.fdroid.fdroid.Preferences;
import org.fdroid.fdroid.R;
import org.fdroid.fdroid.installer.Installer;
+import org.fdroid.fdroid.installer.PrivilegedInstaller;
import eu.chainfire.libsuperuser.Shell;
@@ -86,7 +87,7 @@ public class InstallPrivilegedDialogActivity extends FragmentActivity {
if (Preferences.get().isFirstTime()) {
Preferences.get().setFirstTime(false);
- if (Installer.hasSystemPermissions(context, context.getPackageManager())) {
+ if (PrivilegedInstaller.isAvailable(context)) {
Preferences.get().setSystemInstallerEnabled(true);
} else {
runFirstTime(context);
@@ -158,7 +159,8 @@ public class InstallPrivilegedDialogActivity extends FragmentActivity {
// hack to get holo design (which is not automatically applied due to activity's Theme.NoDisplay
ContextThemeWrapper theme = new ContextThemeWrapper(this, FDroidApp.getCurThemeResId());
- String message = getString(R.string.system_install_first_time_message) + "
" + InstallPrivileged.create(getApplicationContext()).getWarningInfo();
+ String message = getString(R.string.system_install_first_time_message) + "
"
+ + InstallPrivileged.create(getApplicationContext(), null).getWarningInfo();
AlertDialog.Builder builder = new AlertDialog.Builder(theme)
.setMessage(Html.fromHtml(message))
@@ -279,7 +281,7 @@ public class InstallPrivilegedDialogActivity extends FragmentActivity {
// hack to get holo design (which is not automatically applied due to activity's Theme.NoDisplay
ContextThemeWrapper theme = new ContextThemeWrapper(this, FDroidApp.getCurThemeResId());
- final boolean success = Installer.hasSystemPermissions(this, this.getPackageManager());
+ final boolean success = PrivilegedInstaller.isAvailable(this);
// enable system installer on installation success
Preferences.get().setSystemInstallerEnabled(success);
@@ -303,7 +305,7 @@ public class InstallPrivilegedDialogActivity extends FragmentActivity {
// hack to get holo design (which is not automatically applied due to activity's Theme.NoDisplay
ContextThemeWrapper theme = new ContextThemeWrapper(this, FDroidApp.getCurThemeResId());
- final boolean systemApp = Installer.hasSystemPermissions(this, this.getPackageManager());
+ final boolean systemApp = PrivilegedInstaller.isAvailable(this);
if (systemApp) {
AlertDialog.Builder builder = new AlertDialog.Builder(theme)
diff --git a/F-Droid/src/org/fdroid/fdroid/views/fragments/PreferencesFragment.java b/F-Droid/src/org/fdroid/fdroid/views/fragments/PreferencesFragment.java
index 40804dd88..f87644e75 100644
--- a/F-Droid/src/org/fdroid/fdroid/views/fragments/PreferencesFragment.java
+++ b/F-Droid/src/org/fdroid/fdroid/views/fragments/PreferencesFragment.java
@@ -19,6 +19,7 @@ import org.fdroid.fdroid.Preferences;
import org.fdroid.fdroid.PreferencesActivity;
import org.fdroid.fdroid.R;
import org.fdroid.fdroid.Utils;
+import org.fdroid.fdroid.installer.PrivilegedInstaller;
import org.fdroid.fdroid.privileged.install.InstallPrivilegedDialogActivity;
import org.fdroid.fdroid.installer.Installer;
@@ -195,7 +196,7 @@ public class PreferencesFragment extends PreferenceFragment
final CheckBoxPreference pref = (CheckBoxPreference) preference;
if (pref.isChecked()) {
- if (Installer.hasSystemPermissions(getActivity(), getActivity().getPackageManager())) {
+ if (PrivilegedInstaller.isAvailable(getActivity())) {
// system-permission are granted, i.e. F-Droid is a system-app
SharedPreferences.Editor editor = pref.getSharedPreferences().edit();
editor.putBoolean(Preferences.PREF_SYSTEM_INSTALLER, true);