package com.koushikdutta.superuser;
+import android.app.AlertDialog;
+import android.app.ProgressDialog;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.util.Log;
import android.view.Menu;
+import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MenuItem.OnMenuItemClickListener;
+import com.koushikdutta.superuser.util.Settings;
+import com.koushikdutta.superuser.util.StreamUtility;
+import com.koushikdutta.superuser.util.SuHelper;
import com.koushikdutta.widgets.BetterListActivity;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
public class MainActivity extends BetterListActivity {
public MainActivity() {
super(PolicyFragment.class);
@Override
public boolean onCreateOptionsMenu(Menu menu) {
- MenuItem about = menu.add(R.string.about);
+ MenuInflater mi = new MenuInflater(this);
+ mi.inflate(R.menu.app, menu);
+ MenuItem about = menu.findItem(R.id.about);
about.setOnMenuItemClickListener(new OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
return super.onCreateOptionsMenu(menu);
}
+
+ private String getArch() {
+ String prop = System.getProperty("os.arch");
+ if (prop.contains("x86") || prop.contains("i686") || prop.contains("i386")) {
+ return "x86";
+ } else if (prop.contains("mips")) {
+ return "mips";
+ } else {
+ return "armeabi";
+ }
+ }
+
+ File extractSu() throws IOException, InterruptedException {
+ ZipFile zf = new ZipFile(getPackageCodePath());
+ ZipEntry su = zf.getEntry("assets/" + getArch() + "/su");
+ InputStream zin = zf.getInputStream(su);
+ File ret = getFileStreamPath("su");
+ FileOutputStream fout = new FileOutputStream(ret);
+ StreamUtility.copyStream(zin, fout);
+ zin.close();
+ zf.close();
+ fout.close();
+ return ret;
+ }
+
+ void doRecoveryInstall() {
+ final ProgressDialog dlg = new ProgressDialog(this);
+ dlg.setTitle(R.string.installing);
+ dlg.setMessage(getString(R.string.installing_superuser));
+ dlg.setIndeterminate(true);
+ dlg.show();
+ new Thread() {
+ void doEntry(ZipOutputStream zout, String entryName, String dest) throws IOException {
+ ZipFile zf = new ZipFile(getPackageCodePath());
+ ZipEntry ze = zf.getEntry(entryName);
+ zout.putNextEntry(new ZipEntry(dest));
+ InputStream in;
+ StreamUtility.copyStream(in = zf.getInputStream(ze), zout);
+ zout.closeEntry();
+ in.close();
+ zf.close();
+ }
+
+ public void run() {
+ try {
+ File zip = getFileStreamPath("superuser.zip");
+ ZipOutputStream zout = new ZipOutputStream(new FileOutputStream(zip));
+ doEntry(zout, "assets/update-binary", "META-INF/com/google/android/update-binary");
+ doEntry(zout, "assets/install-recovery.sh", "install-recovery.sh");
+ zout.close();
+
+ ZipFile zf = new ZipFile(getPackageCodePath());
+ ZipEntry ze = zf.getEntry("assets/" + getArch() + "/reboot");
+ InputStream in;
+ FileOutputStream reboot;
+ StreamUtility.copyStream(in = zf.getInputStream(ze), reboot = openFileOutput("reboot", MODE_PRIVATE));
+ reboot.close();
+ in.close();
+
+ final File su = extractSu();
+
+ String command =
+ String.format("cat %s > /cache/superuser.zip\n", zip.getAbsolutePath()) +
+ String.format("cat %s > /cache/su\n", su.getAbsolutePath()) +
+ String.format("cat %s > /cache/Superuser.apk\n", getPackageCodePath()) +
+ "mkdir /cache/recovery\n" +
+ "echo '--update_package=CACHE:superuser.zip' > /cache/recovery/command\n" +
+ "chmod 644 /cache/superuser.zip\n" +
+ "chmod 644 /cache/recovery/command\n" +
+ "sync\n" +
+ String.format("chmod 755 %s\n", getFileStreamPath("reboot").getAbsolutePath()) +
+ "reboot recovery\n";
+ Process p = Runtime.getRuntime().exec("su");
+ p.getOutputStream().write(command.getBytes());
+ p.getOutputStream().close();
+ File rebootScript = getFileStreamPath("reboot.sh");
+ StreamUtility.writeFile(rebootScript, "reboot recovery ; " + getFileStreamPath("reboot").getAbsolutePath() + " recovery ;");
+ p.waitFor();
+ Runtime.getRuntime().exec(new String[] { "su", "-c", ". " + rebootScript.getAbsolutePath() });
+ if (p.waitFor() != 0)
+ throw new Exception("non zero result");
+ }
+ catch (Exception ex) {
+ ex.printStackTrace();
+ dlg.dismiss();
+
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
+ builder.setPositiveButton(android.R.string.ok, null);
+ builder.setTitle(R.string.install);
+ builder.setMessage(R.string.install_error);
+ builder.create().show();
+ }
+ });
+ }
+ }
+ }.start();
+ }
+
+ void doSystemInstall() {
+ final ProgressDialog dlg = new ProgressDialog(this);
+ dlg.setTitle(R.string.installing);
+ dlg.setMessage(getString(R.string.installing_superuser));
+ dlg.setIndeterminate(true);
+ dlg.show();
+ new Thread() {
+ public void run() {
+ boolean _error = false;
+ try {
+ final File su = extractSu();
+ final String command =
+ "mount -orw,remount /system\n" +
+ "rm /system/xbin/su\n" +
+ "rm /system/bin/su\n" +
+ "rm /system/app/Supersu.*\n" +
+ "rm /system/app/superuser.*\n" +
+ "rm /system/app/supersu.*\n" +
+ "rm /system/app/SuperUser.*\n" +
+ "rm /system/app/SuperSU.*\n" +
+ String.format("cat %s > /system/xbin/su\n", su.getAbsolutePath()) +
+ "chmod 6755 /system/xbin/su\n" +
+ "ln -s /system/xbin/su /system/bin/su\n" +
+ "mount -oro,remount /system\n" +
+ "sync\n";
+ Process p = Runtime.getRuntime().exec("su");
+ p.getOutputStream().write(command.getBytes());
+ p.getOutputStream().close();
+ if (p.waitFor() != 0)
+ throw new Exception("non zero result");
+ SuHelper.checkSu(MainActivity.this);
+ }
+ catch (Exception ex) {
+ _error = true;
+ Log.e("Superuser", "error upgrading", ex);
+ }
+ dlg.dismiss();
+ final boolean error = _error;
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
+ builder.setPositiveButton(android.R.string.ok, null);
+ builder.setTitle(R.string.install);
+
+ if (error) {
+ builder.setMessage(R.string.install_error);
+ }
+ else {
+ builder.setMessage(R.string.install_success);
+ }
+ builder.create().show();
+ }
+ });
+ };
+ }.start();
+ }
+
+ void doInstall() {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(R.string.install);
+ builder.setMessage(R.string.install_superuser_info);
+ if (Build.VERSION.SDK_INT < 18) {
+ builder.setPositiveButton(R.string.install, new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ doSystemInstall();
+ }
+ });
+ }
+ builder.setNegativeButton(android.R.string.cancel, null);
+ builder.setNeutralButton(R.string.recovery_install, new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ doRecoveryInstall();
+ }
+ });
+ builder.create().show();
+ }
+
+ private void saveWhatsNew() {
+ Settings.setString(this, "whats_new", WHATS_NEW);
+ }
+
+ // this is intentionally not localized as it will change constantly.
+ private static final String WHATS_NEW = "Added support for Android 4.3.";
+ protected void doWhatsNew() {
+ if (WHATS_NEW.equals(Settings.getString(this, "whats_new")))
+ return;
+ saveWhatsNew();
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(R.string.whats_new);
+ builder.setIcon(R.drawable.ic_launcher);
+ builder.setMessage(WHATS_NEW);
+ builder.setPositiveButton(R.string.rate, new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Intent i = new Intent();
+ i.setData(Uri.parse("market://details?id=com.koushikdutta.superuser"));
+ startActivity(i);
+ }
+ });
+ builder.setNegativeButton(android.R.string.cancel, null);
+ builder.create().show();
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ Settings.applyDarkThemeSetting(this, R.style.SuperuserDarkActivity);
+ super.onCreate(savedInstanceState);
+
+ if (Settings.getBoolean(this, "first_run", true)) {
+ saveWhatsNew();
+ Settings.setBoolean(this, "first_run", false);
+ }
+
+ final ProgressDialog dlg = new ProgressDialog(this);
+ dlg.setTitle(R.string.superuser);
+ dlg.setMessage(getString(R.string.checking_superuser));
+ dlg.setIndeterminate(true);
+ dlg.show();
+ new Thread() {
+ public void run() {
+ boolean _error = false;
+ try {
+ SuHelper.checkSu(MainActivity.this);
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ _error = true;
+ }
+ final boolean error = _error;
+ dlg.dismiss();
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ if (error) {
+ doInstall();
+ }
+ else {
+ doWhatsNew();
+ }
+ }
+ });
+ };
+ }.start();
+ }
}