Refactored system app converter into classes.

The multiple occurances of "if (Build.SDK_INT < ... )" statements
hint at the prospect that there are a couple of different implementations
of this class which behave differently. The new classes start with
InstallFDroidAsSystem, and then there are SDK specific subclasses
which provide the customization relevant for those subclasses.
This commit is contained in:
Peter Serwylo 2015-05-13 12:09:20 +10:00 committed by Dominik Schürmann
parent c27d8f6287
commit 4775c17653
2 changed files with 174 additions and 76 deletions

View File

@ -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<String> getCommands() {
final List<String> 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<String> reinstallFDroidAsSystem() {
final List<String> commands = new ArrayList<>(3);
commands.add("mv " + installPath() + ".tmp " + installPath());
commands.add("pm install -r " + installPath());
commands.add("sleep 5");
return commands;
}
protected List<String> makePartitionReadOnly() {
final List<String> 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<String> makePartitionReadOnly() {
List<String> 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);
}
}
}

View File

@ -22,6 +22,7 @@ package org.fdroid.fdroid.installer;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.ProgressDialog; import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.os.AsyncTask; import android.os.AsyncTask;
@ -37,6 +38,9 @@ import org.fdroid.fdroid.FDroidApp;
import org.fdroid.fdroid.Preferences; import org.fdroid.fdroid.Preferences;
import org.fdroid.fdroid.R; import org.fdroid.fdroid.R;
import java.util.ArrayList;
import java.util.List;
import eu.chainfire.libsuperuser.Shell; 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_POST_INSTALL = "post_install";
public static final String ACTION_FIRST_TIME = "first_time"; 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; String action;
@Override @Override
@ -147,13 +102,7 @@ public class InstallIntoSystemDialogActivity extends FragmentActivity {
ContextThemeWrapper theme = new ContextThemeWrapper(this, FDroidApp.getCurThemeResId()); ContextThemeWrapper theme = new ContextThemeWrapper(this, FDroidApp.getCurThemeResId());
AlertDialog.Builder builder = new AlertDialog.Builder(theme); AlertDialog.Builder builder = new AlertDialog.Builder(theme);
String message = getString(R.string.system_install_first_time_message) + String message = getString(R.string.system_install_first_time_message) + "<br/><br/>" + InstallFDroidAsSystem.create(getApplicationContext()).getWarningInfo();
"<br/><br/>";
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);
}
builder.setMessage(Html.fromHtml(message)); builder.setMessage(Html.fromHtml(message));
builder.setPositiveButton(R.string.system_permission_install_via_root, new DialogInterface.OnClickListener() { builder.setPositiveButton(R.string.system_permission_install_via_root, new DialogInterface.OnClickListener() {
@Override @Override
@ -260,22 +209,7 @@ public class InstallIntoSystemDialogActivity extends FragmentActivity {
@Override @Override
protected Void doInBackground(Void... voids) { protected Void doInBackground(Void... voids) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { InstallFDroidAsSystem.create(getApplicationContext()).performInstall();
// 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()
}));
}
return null; return null;
} }
}; };
@ -365,10 +299,7 @@ public class InstallIntoSystemDialogActivity extends FragmentActivity {
@Override @Override
protected Void doInBackground(Void... voids) { protected Void doInBackground(Void... voids) {
Shell.SU.run(String.format(CMD_UNINSTALL, InstallFDroidAsSystem.create(getApplicationContext()).performUninstall();
new String[]{
InstallIntoSystemDialogActivity.this.getPackageName()
}));
return null; return null;
} }