OSDN Git Service

In app installation done. Release on Play soon.
authorKoushik Dutta <koushd@gmail.com>
Sun, 3 Mar 2013 12:13:13 +0000 (04:13 -0800)
committerKoushik Dutta <koushd@gmail.com>
Sun, 3 Mar 2013 12:13:13 +0000 (04:13 -0800)
Change-Id: I28428fe8796cd36eb51c3dd9c6164b822ed84a14

Superuser/AndroidManifest.xml
Superuser/assets/update-binary [moved from recoveryzip/META-INF/com/google/android/update-binary with 69% similarity]
Superuser/build.xml
Superuser/res/values/strings.xml
Superuser/src/com/koushikdutta/superuser/AboutFragment.java
Superuser/src/com/koushikdutta/superuser/MainActivity.java
Superuser/src/com/koushikdutta/superuser/util/StreamUtility.java [new file with mode: 0644]

index 810f058..468c8e2 100644 (file)
@@ -43,8 +43,6 @@
         <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" />
@@ -61,6 +59,7 @@
             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 -->
@@ -72,6 +71,7 @@
             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 -->
@@ -81,6 +81,7 @@
             android:excludeFromRecents="true"
             android:exported="false"
             android:label="@string/request"
+            android:taskAffinity="internal.superuser"
             android:theme="@style/RequestTheme" />
 
         <receiver
similarity index 69%
rename from recoveryzip/META-INF/com/google/android/update-binary
rename to Superuser/assets/update-binary
index ab37cf8..1c5cc31 100644 (file)
@@ -5,10 +5,6 @@
 # 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" ]
@@ -18,13 +14,30 @@ else
   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
@@ -32,5 +45,6 @@ chmod 6755 /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
index a430d53..5500c8a 100644 (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.
index 0f9cc0b..c818944 100644 (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>
index 294e12c..e39b7b9 100644 (file)
@@ -51,7 +51,7 @@ public class AboutFragment extends BetterListFragment {
                 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) {
index c13b157..b8f0198 100644 (file)
 
 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 {
@@ -44,4 +61,199 @@ 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();
+    }
 }
diff --git a/Superuser/src/com/koushikdutta/superuser/util/StreamUtility.java b/Superuser/src/com/koushikdutta/superuser/util/StreamUtility.java
new file mode 100644 (file)
index 0000000..c90420e
--- /dev/null
@@ -0,0 +1,126 @@
+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();
+    }
+}
+