<activity
android:name=".MainActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
- android:excludeFromRecents="true"
- android:noHistory="true"
android:label="@string/superuser" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
android:label="@string/superuser"
android:launchMode="singleTask"
android:permission="android.permission.REQUEST_SUPERUSER"
+ android:taskAffinity="internal.superuser"
android:theme="@style/RequestTheme" />
<!-- Only system/su can open this activity -->
<!-- This is activity is started in multiuser mode when the user invoking su -->
android:label="@string/superuser"
android:launchMode="singleTask"
android:permission="android.permission.REQUEST_SUPERUSER"
+ android:taskAffinity="internal.superuser"
android:theme="@style/RequestTheme" />
<!-- Multiple instances of this activity can be running for multiple su requests -->
android:excludeFromRecents="true"
android:exported="false"
android:label="@string/request"
+ android:taskAffinity="internal.superuser"
android:theme="@style/RequestTheme" />
<receiver
# arg 3 is the zip file
echo -n -e 'ui_print Installing Superuser...\n' > /proc/self/fd/$2
echo -n -e 'ui_print\n' > /proc/self/fd/$2
-cd /tmp
-mkdir superuser
-cd superuser
-unzip -o "$3"
X86=$(uname -a | grep x86)
if [ ! -z "$X86" ]
PLATFORM=armeabi
fi
+cd /tmp
+mkdir superuser
+cd superuser
+unzip -o "$3"
+if [ "$?" -ne "0" ]
+then
+ mkdir -p $PLATFORM
+ cp /cache/su $PLATFORM/su
+ cp /cache/Superuser.apk .
+fi
+
+
echo -n -e 'ui_print Installing '$PLATFORM' binaries...\n' > /proc/self/fd/$2
echo -n -e 'ui_print\n' > /proc/self/fd/$2
mount /system
rm -f /system/bin/su
rm -f /system/xbin/su
-rm -f /system/app/Superuser.apk
+rm -f /system/app/Superuser.*
+rm -f /system/app/Supersu.*
+rm -f /system/app/superuser.*
+rm -f /system/app/supersu.*
+rm -f /system/app/SuperUser.*
+rm -f /system/app/SuperSU.*
cp $PLATFORM/su /system/xbin/su
chown 0:0 /system/xbin/su
ln -s /system/xbin/su /system/bin/su
cp Superuser.apk /system/app
+chmod 644 /system/app/Superuser.apk
umount /system
\ No newline at end of file
</exec>
<delete file="bin/update.zip"/>
<delete file="bin/Superuser.apk"/>
+ <delete dir="bin/META-INF" />
</target>
<target name="-post-build">
- <exec executable="zip" failonerror="true" dir="../recoveryzip">
+ <mkdir dir="bin/recoveryzip/META-INF/com/google/android" />
+ <copy file="assets/update-binary" tofile="bin/recoveryzip/META-INF/com/google/android/update-binary"/>
+ <copy file="bin/Superuser-release.apk" tofile="bin/recoveryzip/Superuser.apk"/>
+
+ <exec executable="zip" failonerror="true" dir="bin/recoveryzip">
<arg value="-ry"/>
- <arg value="../Superuser/bin/update.zip"/>
+ <arg value="../update.zip"/>
<arg value="."/>
</exec>
<exec executable="zip" failonerror="true" dir="libs">
<arg value="armeabi"/>
<arg value="x86"/>
</exec>
- <copy file="bin/Superuser-release.apk" tofile="bin/Superuser.apk"/>
- <exec executable="zip" failonerror="true" dir="bin">
- <arg value="update.zip"/>
- <arg value="Superuser.apk"/>
- </exec>
</target>
<!-- Import the actual build file.
<string name="all_commands">All Commands</string>
<string name="about">About</string>
<string name="apps">Apps</string>
+ <string name="installing">Installing</string>
+ <string name="installing_superuser">Installing Superuser...</string>
+ <string name="install_superuser">Install Superuser</string>
+ <string name="install_superuser_info">The Superuser binary (su) must be updated.\n\nPlease choose an installation method.\n\Recovery mode installation is recommended for HTC devices.</string>
+ <string name="recovery_install">Recovery Install</string>
+ <string name="install">Install</string>
+ <string name="checking_superuser">Checking Superuser...</string>
+ <string name="install_error">There was an error instaling Superuser. Please send a log of the error to the developer.</string>
+ <string name="install_success">Installation of Superuser was successful.</string>
</resources>
startActivity(i);
}
});
-
+
addItem(R.string.apps, new ListItem(getInternal(), "ROM Manager", "The ultimate backup, restore, and ROM installation tool", R.drawable.clockwork512) {
@Override
public void onClick(View view) {
package com.koushikdutta.superuser;
+import java.io.File;
+import java.io.FileInputStream;
+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;
+
+import android.app.AlertDialog;
+import android.app.ProgressDialog;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.os.Bundle;
+import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MenuItem.OnMenuItemClickListener;
+import com.koushikdutta.superuser.util.Settings;
+import com.koushikdutta.superuser.util.StreamUtility;
import com.koushikdutta.widgets.BetterListActivity;
public class MainActivity extends BetterListActivity {
return super.onCreateOptionsMenu(menu);
}
+
+ 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");
+ zout.close();
+
+// doEntry(zout, "lib/armeabi/libsu.so", "armeabi/su");
+// doEntry(zout, "lib/x86/libsu.so", "x86/su");
+// zout.putNextEntry(new ZipEntry("Superuser.apk"));
+// FileInputStream fin = new FileInputStream(getPackageCodePath());
+// StreamUtility.copyStream(fin, zout);
+// fin.close();
+// zout.closeEntry();
+// zout.close();
+ final File libsu = new File(getApplicationInfo().dataDir, "lib/libsu.so");
+
+ String command =
+ String.format("cat %s > /cache/superuser.zip\n", zip.getAbsolutePath()) +
+ String.format("cat %s > /cache/su\n", libsu.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" +
+ "reboot recovery\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");
+ }
+ 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();
+ final File libsu = new File(getApplicationInfo().dataDir, "lib/libsu.so");
+ final String command =
+ "mount -orw,remount /system\n" +
+ "rm /system/xbin/su\n" +
+ "rm /system/bin/su\n" +
+ "rm -f /system/app/Supersu.*\n" +
+ "rm -f /system/app/superuser.*\n" +
+ "rm -f /system/app/supersu.*\n" +
+ "rm -f /system/app/SuperUser.*\n" +
+ "rm -f /system/app/SuperSU.*\n" +
+ String.format("cat %s > /system/xbin/su\n", libsu.getAbsolutePath()) +
+ "chmod 6777 /system/xbin/su\n" +
+ "ln -s /system/xbin/su /system/bin/su\n" +
+ "mount -oro,remount /system\n" +
+ "sync\n";
+ new Thread() {
+ public void run() {
+ boolean _error = false;
+ try {
+ if (!libsu.exists())
+ throw new Exception(libsu.getAbsolutePath() + " not found");
+ Process p = Runtime.getRuntime().exec("su");
+ p.getOutputStream().write(command.getBytes());
+ p.getOutputStream().close();
+ if (p.waitFor() != 0)
+ throw new Exception("non zero result");
+ checkSu();
+ }
+ catch (Exception ex) {
+ _error = true;
+ ex.printStackTrace();
+ }
+ 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);
+ 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();
+ }
+
+ void checkSu() throws Exception {
+ Process p = Runtime.getRuntime().exec("su -v");
+ String result = Settings.readToEnd(p.getInputStream());
+ Log.i("Superuser", "Result: " + result);
+ if (0 != p.waitFor())
+ throw new Exception("non zero result");
+ if (result == null)
+ throw new Exception("no data");
+ if (!result.contains(getPackageName()))
+ throw new Exception("unknown su");
+ // TODO: upgrades herp derp
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ 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 {
+ checkSu();
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ error = true;
+ }
+ dlg.dismiss();
+ if (error) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ doInstall();
+ }
+ });
+ }
+ };
+ }.start();
+ }
}
--- /dev/null
+package com.koushikdutta.superuser.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channels;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import android.net.http.AndroidHttpClient;
+
+public class StreamUtility {
+ private static final String LOGTAG = StreamUtility.class.getSimpleName();
+ public static void fastChannelCopy(final ReadableByteChannel src, final WritableByteChannel dest) throws IOException {
+ final ByteBuffer buffer = ByteBuffer.allocateDirect(1 << 17);
+ while (src.read(buffer) != -1) {
+ // prepare the buffer to be drained
+ buffer.flip();
+ // write to the channel, may block
+ dest.write(buffer);
+ // If partial transfer, shift remainder down
+ // If buffer is empty, same as doing clear()
+ buffer.compact();
+ }
+ // EOF will leave buffer in fill state
+ buffer.flip();
+ // make sure the buffer is fully drained.
+ while (buffer.hasRemaining()) {
+ dest.write(buffer);
+ }
+ }
+
+ public static void copyStream(InputStream input, OutputStream output) throws IOException
+ {
+ final ReadableByteChannel inputChannel = Channels.newChannel(input);
+ final WritableByteChannel outputChannel = Channels.newChannel(output);
+ // copy the channels
+ fastChannelCopy(inputChannel, outputChannel);
+ }
+
+ public static String downloadUriAsString(String uri) throws IOException {
+ HttpGet get = new HttpGet(uri);
+ return downloadUriAsString(get);
+ }
+
+
+ public static String downloadUriAsString(final HttpUriRequest req) throws IOException {
+ AndroidHttpClient client = AndroidHttpClient.newInstance("Android");
+ try {
+ HttpResponse res = client.execute(req);
+ return readToEnd(res.getEntity().getContent());
+ }
+ finally {
+ client.close();
+ }
+ }
+
+ public static JSONObject downloadUriAsJSONObject(String uri) throws IOException, JSONException {
+ return new JSONObject(downloadUriAsString(uri));
+ }
+
+ public static JSONObject downloadUriAsJSONObject(HttpUriRequest req) throws IOException, JSONException {
+ return new JSONObject(downloadUriAsString(req));
+ }
+
+ public static byte[] readToEndAsArray(InputStream input) throws IOException
+ {
+ DataInputStream dis = new DataInputStream(input);
+ byte[] stuff = new byte[1024];
+ ByteArrayOutputStream buff = new ByteArrayOutputStream();
+ int read = 0;
+ while ((read = dis.read(stuff)) != -1)
+ {
+ buff.write(stuff, 0, read);
+ }
+ input.close();
+ return buff.toByteArray();
+ }
+
+ public static void eat(InputStream input) throws IOException {
+ byte[] stuff = new byte[1024];
+ while (input.read(stuff) != -1);
+ }
+
+ public static String readToEnd(InputStream input) throws IOException
+ {
+ return new String(readToEndAsArray(input));
+ }
+
+ static public String readFile(String filename) throws IOException {
+ return readFile(new File(filename));
+ }
+
+ static public String readFile(File file) throws IOException {
+ byte[] buffer = new byte[(int) file.length()];
+ DataInputStream input = new DataInputStream(new FileInputStream(file));
+ input.readFully(buffer);
+ return new String(buffer);
+ }
+
+ public static void writeFile(File file, String string) throws IOException {
+ writeFile(file.getAbsolutePath(), string);
+ }
+
+ public static void writeFile(String file, String string) throws IOException {
+ File f = new File(file);
+ f.getParentFile().mkdirs();
+ DataOutputStream dout = new DataOutputStream(new FileOutputStream(f));
+ dout.write(string.getBytes());
+ dout.close();
+ }
+}
+