diff --git a/F-Droid/src/org/fdroid/fdroid/installer/InstallFDroidAsSystem.java b/F-Droid/src/org/fdroid/fdroid/installer/InstallFDroidAsSystem.java new file mode 100644 index 000000000..fec08f6a0 --- /dev/null +++ b/F-Droid/src/org/fdroid/fdroid/installer/InstallFDroidAsSystem.java @@ -0,0 +1,167 @@ +package org.fdroid.fdroid.installer; + +import android.content.Context; +import android.os.Build; + +import org.fdroid.fdroid.Preferences; +import org.fdroid.fdroid.R; + +import java.util.ArrayList; +import java.util.List; + +import eu.chainfire.libsuperuser.Shell; + +abstract class InstallFDroidAsSystem { + + protected final Context context; + + public InstallFDroidAsSystem(final Context context) { + this.context = context; + } + + public static InstallFDroidAsSystem create(final Context context) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + return new LollipopImpl(context); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + return new KitKatToLollipopImpl(context); + } else { + return new PreKitKatImpl(context); + } + } + + protected abstract String getSystemFolder(); + + protected void onPreInstall() { + // To be overridden by relevant base class[es] + } + + public String getWarningInfo() { + return context.getString(R.string.system_install_question); + } + + final void performUninstall() { + final String[] commands = { + "mount -o rw,remount /system", + "pm uninstall " + context.getPackageName(), + "rm -f " + installPath(), + "sleep 5", + "mount -o ro,remount /system" + }; + Shell.SU.run(commands); + } + + final void performInstall() { + onPreInstall(); + Shell.SU.run(getCommands()); + } + + private String installPath() { + return getSystemFolder() + "FDroid.apk"; + } + + private List getCommands() { + final List commands = new ArrayList<>(); + commands.add(makePartitionWriteable()); + commands.add(copyApkToPartition()); + commands.add(uninstallFDroid()); + commands.addAll(reinstallFDroidAsSystem()); + commands.addAll(makePartitionReadOnly()); + return commands; + } + + protected String makePartitionWriteable() { + return "mount -o rw,remount /system"; + } + + protected String copyApkToPartition() { + return "cat " + context.getPackageCodePath() + " > " + installPath() + ".tmp"; + } + + protected String uninstallFDroid() { + return "pm uninstall -k " + context.getPackageName(); + } + + protected List reinstallFDroidAsSystem() { + final List commands = new ArrayList<>(3); + commands.add("mv " + installPath() + ".tmp " + installPath()); + commands.add("pm install -r " + installPath()); + commands.add("sleep 5"); + return commands; + } + + protected List makePartitionReadOnly() { + final List commands = new ArrayList<>(2); + commands.add("mount -o ro,remount /system"); + commands.add("am start -n org.fdroid.fdroid/.installer.InstallIntoSystemDialogActivity --ez post_install true"); + return commands; + } + + private static class PreKitKatImpl extends InstallFDroidAsSystem { + + public PreKitKatImpl(Context context) { + super(context); + } + + @Override + protected String getSystemFolder() { + return "/system/app"; + } + + } + + private static class KitKatToLollipopImpl extends InstallFDroidAsSystem { + + public KitKatToLollipopImpl(Context context) { + super(context); + } + + /** + * New folder introduced in + * https://github.com/android/platform_frameworks_base/commit/ccbf84f44c9e6a5ed3c08673614826bb237afc54 + */ + @Override + protected String getSystemFolder() { + return "/system/priv-app/"; + } + + } + + private static class LollipopImpl extends InstallFDroidAsSystem { + + public LollipopImpl(Context context) { + super(context); + } + + /** + * New cluster based installation and app dirs + */ + @Override + protected String getSystemFolder() { + return "/system/priv-app/FDroid/"; + } + + /** + * TODO: Currently only works with reboot. Find a way how this could work without. + * See http://stackoverflow.com/q/26487750 + */ + @Override + protected List makePartitionReadOnly() { + List commands = new ArrayList<>(3); + commands.add("am broadcast -a android.intent.action.ACTION_SHUTDOWN"); + commands.add("sleep 1"); + commands.add("reboot"); + return commands; + } + + protected void onPreInstall() { + // Setup preference to execute postInstall after reboot + Preferences.get().setPostSystemInstall(true); + } + + public String getWarningInfo() { + return context.getString(R.string.system_install_question_lollipop); + } + + } + +} diff --git a/F-Droid/src/org/fdroid/fdroid/installer/InstallIntoSystemDialogActivity.java b/F-Droid/src/org/fdroid/fdroid/installer/InstallIntoSystemDialogActivity.java index dee1a24dc..b23374b4e 100644 --- a/F-Droid/src/org/fdroid/fdroid/installer/InstallIntoSystemDialogActivity.java +++ b/F-Droid/src/org/fdroid/fdroid/installer/InstallIntoSystemDialogActivity.java @@ -22,6 +22,7 @@ package org.fdroid.fdroid.installer; import android.app.Activity; import android.app.AlertDialog; import android.app.ProgressDialog; +import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.os.AsyncTask; @@ -37,6 +38,9 @@ import org.fdroid.fdroid.FDroidApp; import org.fdroid.fdroid.Preferences; import org.fdroid.fdroid.R; +import java.util.ArrayList; +import java.util.List; + import eu.chainfire.libsuperuser.Shell; /** @@ -63,55 +67,6 @@ public class InstallIntoSystemDialogActivity extends FragmentActivity { public static final String ACTION_POST_INSTALL = "post_install"; public static final String ACTION_FIRST_TIME = "first_time"; - private static String SYSTEM_FOLDER; - - static { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { - SYSTEM_FOLDER = "/system/app/"; - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT - && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { - // new folder introduced in - // https://github.com/android/platform_frameworks_base/commit/ccbf84f44c9e6a5ed3c08673614826bb237afc54 - SYSTEM_FOLDER = "/system/priv-app/"; - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - // new cluster based installation and app dirs - SYSTEM_FOLDER = "/system/priv-app/FDroid/"; - } - } - - private static final String APK_NAME = "FDroid.apk"; - - private static final String CMD_SCRIPT_KITKAT = "mount -o rw,remount /system\n" + - "cat %s > " + SYSTEM_FOLDER + APK_NAME + ".tmp\n" + - "chmod 655 " + SYSTEM_FOLDER + APK_NAME + ".tmp\n" + - "pm uninstall -k %s\n" + // -k to retain data - "mv " + SYSTEM_FOLDER + APK_NAME + ".tmp " + SYSTEM_FOLDER + APK_NAME + "\n" + - "pm install -r " + SYSTEM_FOLDER + APK_NAME + "\n" + - "sleep 5\n" + - "mount -o ro,remount /system\n" + - "am start -n org.fdroid.fdroid/.installer.InstallIntoSystemDialogActivity --ez post_install true"; - - // TODO: Currently only works with reboot. Find a way how this could work without. - // See http://stackoverflow.com/q/26487750 - private static final String CMD_SCRIPT_LOLLIPOP_REBOOT = "am broadcast -a android.intent.action.ACTION_SHUTDOWN\n" + - "sleep 1\n" + - "reboot"; - - private static final String CMD_SCRIPT_LOLLIPOP = "mount -o rw,remount /system\n" + - "mkdir " + SYSTEM_FOLDER + "\n" + // cluster based app directories - "cat %s > " + SYSTEM_FOLDER + APK_NAME + ".tmp\n" + - "chmod 655 " + SYSTEM_FOLDER + APK_NAME + ".tmp\n" + - "pm uninstall -k %s\n" + // -k to retain data - "mv " + SYSTEM_FOLDER + APK_NAME + ".tmp " + SYSTEM_FOLDER + APK_NAME + "\n" + - "pm install -r " + SYSTEM_FOLDER + APK_NAME + "\n" + - "sleep 5\n" + CMD_SCRIPT_LOLLIPOP_REBOOT; - - private static final String CMD_UNINSTALL = "mount -o rw,remount /system\n" + - "pm uninstall %s\n" + - "rm -f " + SYSTEM_FOLDER + APK_NAME + "\n" + - "sleep 5\n" + - "mount -o ro,remount /system"; - String action; @Override @@ -147,13 +102,7 @@ public class InstallIntoSystemDialogActivity extends FragmentActivity { ContextThemeWrapper theme = new ContextThemeWrapper(this, FDroidApp.getCurThemeResId()); AlertDialog.Builder builder = new AlertDialog.Builder(theme); - String message = getString(R.string.system_install_first_time_message) + - "

"; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - message += getString(R.string.system_install_question_lollipop); - } else { - message += getString(R.string.system_install_question); - } + String message = getString(R.string.system_install_first_time_message) + "

" + InstallFDroidAsSystem.create(getApplicationContext()).getWarningInfo(); builder.setMessage(Html.fromHtml(message)); builder.setPositiveButton(R.string.system_permission_install_via_root, new DialogInterface.OnClickListener() { @Override @@ -260,22 +209,7 @@ public class InstallIntoSystemDialogActivity extends FragmentActivity { @Override protected Void doInBackground(Void... voids) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - // execute postInstall after reboot - Preferences.get().setPostSystemInstall(true); - - Shell.SU.run(String.format(CMD_SCRIPT_LOLLIPOP, - new String[]{ - InstallIntoSystemDialogActivity.this.getPackageCodePath(), - InstallIntoSystemDialogActivity.this.getPackageName() - })); - } else { - Shell.SU.run(String.format(CMD_SCRIPT_KITKAT, - new String[]{ - InstallIntoSystemDialogActivity.this.getPackageCodePath(), - InstallIntoSystemDialogActivity.this.getPackageName() - })); - } + InstallFDroidAsSystem.create(getApplicationContext()).performInstall(); return null; } }; @@ -365,10 +299,7 @@ public class InstallIntoSystemDialogActivity extends FragmentActivity { @Override protected Void doInBackground(Void... voids) { - Shell.SU.run(String.format(CMD_UNINSTALL, - new String[]{ - InstallIntoSystemDialogActivity.this.getPackageName() - })); + InstallFDroidAsSystem.create(getApplicationContext()).performUninstall(); return null; }