Check if PrivilegedInstaller is available, adapt InstallPrivileged

This commit is contained in:
Dominik Schürmann 2015-08-27 00:11:39 +02:00
parent 976d06ea1a
commit 57af421561
6 changed files with 84 additions and 40 deletions

View File

@ -308,8 +308,8 @@
<string name="system_install_post_fail_message">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.</string>
<string name="system_install_installing">installing…</string>
<string name="system_install_uninstalling">uninstalling…</string>
<string name="system_install_question">Do you want to install F-Droid as a privileged app?\nThis takes up to 10 seconds where &lt;b>no user interface&lt;/b> is shown.</string>
<string name="system_install_question_lollipop">Do you want to install F-Droid as a privileged app?\nThis takes up to 10 seconds where &lt;b>no user interface&lt;/b> is shown and the device will be &lt;b>rebooted&lt;/b> afterwards.</string>
<string name="system_install_question">Do you want to install F-Droid as a privileged app?\nThis takes up to 10 seconds.</string>
<string name="system_install_question_lollipop">Do you want to install F-Droid as a privileged app?\nThis takes up to 10 seconds and the device will be &lt;b>rebooted&lt;/b> afterwards.</string>
<string name="system_install_first_time_message">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.</string>
<string name="system_uninstall">Do you want to uninstall F-Droid?</string>
<string name="system_uninstall_message">This will uninstall F-Droid completely.</string>

View File

@ -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()) {

View File

@ -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

View File

@ -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<String> getInstallCommands() {
final List<String> 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<String> getCopyToSystemCommands() {
final List<String> 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<String> 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
* <p/>
* File observers on /system/priv-app/ have been removed because they don't work with the new
* cluser-style layout. See

View File

@ -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) + "<br/><br/>" + InstallPrivileged.create(getApplicationContext()).getWarningInfo();
String message = getString(R.string.system_install_first_time_message) + "<br/><br/>"
+ 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)

View File

@ -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);