From eba94bf2b6e19311c43ef6576dcc63372a0883a6 Mon Sep 17 00:00:00 2001 From: cvpcs Date: Mon, 3 Jan 2011 14:27:40 -0600 Subject: [PATCH] restore d2 source files restoration includes addition of configuration parameters to allow ROMs to define which features the bootstrapper should or shouldn't implement --- .../droid2/bootstrap/BootReceiver.java | 14 ++ .../droid2/bootstrap/BootService.java | 67 +++++++ .../droid2/bootstrap/Bootstrap.java | 185 ++++++++++++++++++ .../koushikdutta/droid2/bootstrap/Helper.java | 36 ++++ .../bootstrap/ROMBootstrapSettings.java | 67 +++++++ 5 files changed, 369 insertions(+) create mode 100644 src/com/koushikdutta/droid2/bootstrap/BootReceiver.java create mode 100644 src/com/koushikdutta/droid2/bootstrap/BootService.java create mode 100644 src/com/koushikdutta/droid2/bootstrap/Bootstrap.java create mode 100644 src/com/koushikdutta/droid2/bootstrap/Helper.java create mode 100644 src/com/koushikdutta/droid2/bootstrap/ROMBootstrapSettings.java diff --git a/src/com/koushikdutta/droid2/bootstrap/BootReceiver.java b/src/com/koushikdutta/droid2/bootstrap/BootReceiver.java new file mode 100644 index 0000000..70116a5 --- /dev/null +++ b/src/com/koushikdutta/droid2/bootstrap/BootReceiver.java @@ -0,0 +1,14 @@ +package com.koushikdutta.droid2.bootstrap; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +public class BootReceiver extends BroadcastReceiver { + + @Override + public void onReceive(Context context, Intent intent) { + Intent i = new Intent(context, BootService.class); + context.startService(i); } + +} diff --git a/src/com/koushikdutta/droid2/bootstrap/BootService.java b/src/com/koushikdutta/droid2/bootstrap/BootService.java new file mode 100644 index 0000000..925fcda --- /dev/null +++ b/src/com/koushikdutta/droid2/bootstrap/BootService.java @@ -0,0 +1,67 @@ +package com.koushikdutta.droid2.bootstrap; + +import android.app.Service; +import android.content.Intent; +import android.os.Handler; +import android.os.IBinder; +import android.util.Log; + +public class BootService extends Service { + + private static final String TAG = "D2B/BootService"; + + @Override + public void onStart(Intent intent, int startId) { + super.onStart(intent, startId); + + // wait to make sure the system is sane. + mHandler.postDelayed(new Runnable() + { + @Override + public void run() { + + String filesDir = getFilesDir().getAbsolutePath(); + String busybox = filesDir + "/busybox"; + String adbd = filesDir + "/adbd"; + + StringBuilder command = new StringBuilder(); + + // prevent recovery from booting here + Log.d(TAG, "Removing recovery_mode trigger"); + command.append("rm /data/.recovery_mode ; "); + + ROMBootstrapSettings settings = new ROMBootstrapSettings(); + + if(settings.restartAdb()) { + // restart adbd as root + Log.d(TAG, "Restarting ADB as Root"); + command.append(busybox + " mount -orw,remount / ; "); + command.append("mv /sbin/adbd /sbin/adbd.old ; "); + command.append(busybox + " cp " + adbd + " /sbin/adbd ; "); + command.append(busybox + " mount -oro,remount / ; "); + command.append(busybox + " kill $(ps | " + busybox + " grep adbd) ;"); + } + + try { + Helper.runSuCommand(BootService.this, command.toString()); + } + catch (Exception e) { + e.printStackTrace(); + } + finally + { + stopSelf(); + } + } + }, + 30000); + } + + Handler mHandler = new Handler(); + + @Override + public IBinder onBind(Intent arg0) { + return null; + } + +} diff --git a/src/com/koushikdutta/droid2/bootstrap/Bootstrap.java b/src/com/koushikdutta/droid2/bootstrap/Bootstrap.java new file mode 100644 index 0000000..75b5689 --- /dev/null +++ b/src/com/koushikdutta/droid2/bootstrap/Bootstrap.java @@ -0,0 +1,185 @@ +package com.koushikdutta.droid2.bootstrap; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Enumeration; +import java.util.Vector; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.AlertDialog.Builder; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.Button; + +public class Bootstrap extends Activity { + + private static final String TAG = "D2B/Bootstrap"; + + /** Called when the activity is first created. */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.main); + + unzipAssets(); + + Button flash = (Button)findViewById(R.id.flash); + flash.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + String filesDir = getFilesDir().getAbsolutePath(); + String busybox = filesDir + "/busybox"; + String hijack = filesDir + "/hijack"; + String logwrapper = filesDir + "/logwrapper.bin"; + String updatebinary = filesDir + "/update-binary"; + String recoveryzip = filesDir + "/update.zip"; + String adbd = filesDir + "/adbd"; + + StringBuilder command = new StringBuilder(); + + ROMBootstrapSettings settings = new ROMBootstrapSettings(); + + if(settings.installHijack()) { + Log.d(TAG, "Installing hijack"); + command.append(busybox + " mount -oremount,rw /system ; "); + command.append(busybox + " cp " + logwrapper + " /system/bin/logwrapper.bin ; "); + command.append(busybox + " cp " + hijack + " /system/bin/hijack ; "); + command.append("cd /system/bin ; rm logwrapper ; ln -s hijack logwrapper ; "); + command.append(busybox + " mount -oremount,ro /system ; "); + } + + if(settings.installRecovery()) { + Log.d(TAG, "Installing recovery"); + command.append(busybox + " mkdir -p /preinstall/recovery ; "); + command.append(busybox + " cp " + updatebinary + " /preinstall/recovery/update-binary ; "); + command.append(busybox + " cp " + recoveryzip + " /preinstall/recovery/recovery.zip ; "); + command.append(busybox + " cp " + hijack + " /preinstall/recovery/hijack ; "); + command.append(busybox + " cp " + logwrapper + " /preinstall/recovery/logwrapper ; "); + } + + if(settings.restartAdb()) { + // restart adbd as root + Log.d(TAG, "Restarting ADB as Root"); + command.append(busybox + " mount -orw,remount / ; "); + command.append("mv /sbin/adbd /sbin/adbd.old ; "); + command.append(busybox + " cp " + adbd + " /sbin/adbd ; "); + command.append(busybox + " mount -oro,remount / ; "); + command.append(busybox + " kill $(ps | " + busybox + " grep adbd) ;"); + } + + // prevent recovery from booting here + Log.d(TAG, "Removing recovery_mode trigger"); + command.append("rm /data/.recovery_mode ; "); + + AlertDialog.Builder builder = new Builder(Bootstrap.this); + builder.setPositiveButton(android.R.string.ok, null); + try { + Helper.runSuCommand(Bootstrap.this, command.toString()); + builder.setMessage("Success!"); + } + catch (Exception e) { + builder.setTitle("Failure"); + builder.setMessage(e.getMessage()); + e.printStackTrace(); + } + builder.create().show(); + } + }); + + Button reboot = (Button)findViewById(R.id.reboot); + reboot.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + StringBuilder command = new StringBuilder(); + command.append("echo 1 > /data/.recovery_mode ; "); + command.append("sync ; reboot ; "); + try { + Helper.runSuCommand(Bootstrap.this, command.toString()); + } + catch (Exception e) { + AlertDialog.Builder builder = new Builder(Bootstrap.this); + builder.setPositiveButton(android.R.string.ok, null); + builder.setTitle("Failure"); + builder.setMessage(e.getMessage()); + e.printStackTrace(); + } + } + }); + } + + final static String LOGTAG = "Droid2Bootstrap"; + final static String ZIP_FILTER = "assets"; + + void unzipAssets() { + String apkPath = getPackageCodePath(); + String mAppRoot = getFilesDir().toString(); + try { + File zipFile = new File(apkPath); + long zipLastModified = zipFile.lastModified(); + ZipFile zip = new ZipFile(apkPath); + Vector files = getAssets(zip); + int zipFilterLength = ZIP_FILTER.length(); + + Enumeration entries = files.elements(); + while (entries.hasMoreElements()) { + ZipEntry entry = (ZipEntry) entries.nextElement(); + String path = entry.getName().substring(zipFilterLength); + File outputFile = new File(mAppRoot, path); + outputFile.getParentFile().mkdirs(); + + if (outputFile.exists() && entry.getSize() == outputFile.length() && zipLastModified < outputFile.lastModified()) + continue; + FileOutputStream fos = new FileOutputStream(outputFile); + copyStreams(zip.getInputStream(entry), fos); + Runtime.getRuntime().exec("chmod 755 " + outputFile.getAbsolutePath()); + } + } catch (IOException e) { + Log.e(LOGTAG, "Error: " + e.getMessage()); + } + } + + static final int BUFSIZE = 5192; + + void copyStreams(InputStream is, FileOutputStream fos) { + BufferedOutputStream os = null; + try { + byte data[] = new byte[BUFSIZE]; + int count; + os = new BufferedOutputStream(fos, BUFSIZE); + while ((count = is.read(data, 0, BUFSIZE)) != -1) { + os.write(data, 0, count); + } + os.flush(); + } catch (IOException e) { + Log.e(LOGTAG, "Exception while copying: " + e); + } finally { + try { + if (os != null) { + os.close(); + } + } catch (IOException e2) { + Log.e(LOGTAG, "Exception while closing the stream: " + e2); + } + } + } + + public Vector getAssets(ZipFile zip) { + Vector list = new Vector(); + Enumeration entries = zip.entries(); + while (entries.hasMoreElements()) { + ZipEntry entry = (ZipEntry) entries.nextElement(); + if (entry.getName().startsWith(ZIP_FILTER)) { + list.add(entry); + } + } + return list; + } +} diff --git a/src/com/koushikdutta/droid2/bootstrap/Helper.java b/src/com/koushikdutta/droid2/bootstrap/Helper.java new file mode 100644 index 0000000..9ce4ac3 --- /dev/null +++ b/src/com/koushikdutta/droid2/bootstrap/Helper.java @@ -0,0 +1,36 @@ +package com.koushikdutta.droid2.bootstrap; + +import java.io.DataOutputStream; +import java.io.IOException; + +import android.content.Context; + +public class Helper { + static final String LOGTAG = "Droid2Bootstrap"; + + public final static String SCRIPT_NAME = "surunner.sh"; + + public static Process runSuCommandAsync(Context context, String command) throws IOException + { + DataOutputStream fout = new DataOutputStream(context.openFileOutput(SCRIPT_NAME, 0)); + fout.writeBytes(command); + fout.close(); + + String[] args = new String[] { "su", "-c", ". " + context.getFilesDir().getAbsolutePath() + "/" + SCRIPT_NAME }; + Process proc = Runtime.getRuntime().exec(args); + return proc; + } + + public static int runSuCommand(Context context, String command) throws IOException, InterruptedException + { + return runSuCommandAsync(context, command).waitFor(); + } + + public static int runSuCommandNoScriptWrapper(Context context, String command) throws IOException, InterruptedException + { + String[] args = new String[] { "su", "-c", command }; + Process proc = Runtime.getRuntime().exec(args); + return proc.waitFor(); + } + +} diff --git a/src/com/koushikdutta/droid2/bootstrap/ROMBootstrapSettings.java b/src/com/koushikdutta/droid2/bootstrap/ROMBootstrapSettings.java new file mode 100644 index 0000000..8b6b13e --- /dev/null +++ b/src/com/koushikdutta/droid2/bootstrap/ROMBootstrapSettings.java @@ -0,0 +1,67 @@ +package com.koushikdutta.droid2.bootstrap; + +import java.io.File; +import java.io.FileInputStream; + +import android.util.Log; + +import org.json.JSONObject; + +public class ROMBootstrapSettings { + private static final String TAG = "D2B/ROMBootstrapSettings"; + + private static final File SETTINGS_FILE = new File("/system/etc/Droid2Bootstrap.cfg"); + private static final String RESTART_ADB_KEY = "restart_adb"; + private static final String INSTALL_HIJACK_KEY = "install_hijack"; + private static final String INSTALL_RECOVERY_KEY = "install_recovery"; + + private boolean mRestartAdb = true; + private boolean mInstallHijack = true; + private boolean mInstallRecovery = true; + + public ROMBootstrapSettings() { + if(SETTINGS_FILE.exists()) { + Log.d(TAG, "Found settings file, parsing"); + + FileInputStream f = null; + String data = ""; + + try { + // read file into a string + byte[] buffer = new byte[(int) SETTINGS_FILE.length()]; + f = new FileInputStream(SETTINGS_FILE); + f.read(buffer); + data = new String(buffer); + } catch(Exception e) { + Log.e(TAG, "Error reading settings file", e); + } finally { + // ensure the stream is closed + if(f != null) try { f.close(); } catch(Exception ignored) { } + } + + try { + // parse as JSON + JSONObject json = new JSONObject(data); + + if(json.has(RESTART_ADB_KEY)) { + mRestartAdb = json.optBoolean(RESTART_ADB_KEY, true); + Log.d(TAG, "Setting [RestartAdb] to [" + (mRestartAdb ? "true" : "false") + "]"); + } + if(json.has(INSTALL_HIJACK_KEY)) { + mInstallHijack = json.optBoolean(INSTALL_HIJACK_KEY, true); + Log.d(TAG, "Setting [InstallHijack] to [" + (mInstallHijack ? "true" : "false") + "]"); + } + if(json.has(INSTALL_RECOVERY_KEY)) { + mInstallRecovery = json.optBoolean(INSTALL_RECOVERY_KEY, true); + Log.d(TAG, "Setting [InstallRecovery] to [" + (mInstallRecovery ? "true" : "false") + "]"); + } + } catch(Exception e) { + Log.e(TAG, "Error parsing settings file", e); + } + } + } + + public boolean restartAdb() { return mRestartAdb; } + public boolean installHijack() { return mInstallHijack; } + public boolean installRecovery() { return mInstallRecovery; } +}