OSDN Git Service

add reboot binary
[android-x86/external-koush-Superuser.git] / Superuser / src / com / koushikdutta / superuser / MainActivity.java
1 /*
2  * Copyright (C) 2013 Koushik Dutta (@koush)
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.koushikdutta.superuser;
18
19 import java.io.File;
20 import java.io.FileOutputStream;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.util.zip.ZipEntry;
24 import java.util.zip.ZipFile;
25 import java.util.zip.ZipOutputStream;
26
27 import android.app.AlertDialog;
28 import android.app.ProgressDialog;
29 import android.content.DialogInterface;
30 import android.content.Intent;
31 import android.content.DialogInterface.OnClickListener;
32 import android.net.Uri;
33 import android.os.Bundle;
34 import android.view.Menu;
35 import android.view.MenuInflater;
36 import android.view.MenuItem;
37 import android.view.MenuItem.OnMenuItemClickListener;
38
39 import com.koushikdutta.superuser.util.Settings;
40 import com.koushikdutta.superuser.util.StreamUtility;
41 import com.koushikdutta.superuser.util.SuHelper;
42 import com.koushikdutta.widgets.BetterListActivity;
43
44 public class MainActivity extends BetterListActivity {
45     public MainActivity() {
46         super(PolicyFragment.class);
47     }
48
49     public PolicyFragmentInternal getFragment() {
50         return (PolicyFragmentInternal)super.getFragment();
51     }
52     
53     @Override
54     public boolean onCreateOptionsMenu(Menu menu) {
55         MenuInflater mi = new MenuInflater(this);
56         mi.inflate(R.menu.app, menu);
57         MenuItem about = menu.findItem(R.id.about);
58         about.setOnMenuItemClickListener(new OnMenuItemClickListener() {
59             @Override
60             public boolean onMenuItemClick(MenuItem item) {
61                 getFragment().setContent(new AboutFragment(), true, getString(R.string.about));
62                 return true;
63             }
64         });
65         
66         return super.onCreateOptionsMenu(menu);
67     }
68     
69     File extractSu() throws IOException, InterruptedException {
70         String arch = "armeabi";
71         if (System.getProperty("os.arch").contains("x86") || System.getProperty("os.arch").contains("i686") || System.getProperty("os.arch").contains("i386"))
72             arch = "x86";
73         ZipFile zf = new ZipFile(getPackageCodePath());
74         ZipEntry su = zf.getEntry("assets/" + arch + "/su");
75         InputStream zin = zf.getInputStream(su);
76         File ret = getFileStreamPath("su");
77         FileOutputStream fout = new FileOutputStream(ret);
78         StreamUtility.copyStream(zin, fout);
79         zin.close();
80         zf.close();
81         fout.close();
82         return ret;
83     }
84     
85     void doRecoveryInstall() {
86         final ProgressDialog dlg = new ProgressDialog(this);
87         dlg.setTitle(R.string.installing);
88         dlg.setMessage(getString(R.string.installing_superuser));
89         dlg.setIndeterminate(true);
90         dlg.show();
91         new Thread() {
92             void doEntry(ZipOutputStream zout, String entryName, String dest) throws IOException {
93                 ZipFile zf = new ZipFile(getPackageCodePath());
94                 ZipEntry ze = zf.getEntry(entryName);
95                 zout.putNextEntry(new ZipEntry(dest));
96                 InputStream in;
97                 StreamUtility.copyStream(in = zf.getInputStream(ze), zout);
98                 zout.closeEntry();
99                 in.close();
100                 zf.close();
101             }
102             
103             public void run() {
104                 try {
105                     File zip = getFileStreamPath("superuser.zip");
106                     ZipOutputStream zout = new ZipOutputStream(new FileOutputStream(zip));
107                     doEntry(zout, "assets/update-binary", "META-INF/com/google/android/update-binary");
108                     zout.close();
109
110                     ZipFile zf = new ZipFile(getPackageCodePath());
111                     ZipEntry ze = zf.getEntry("assets/reboot");
112                     InputStream in;
113                     FileOutputStream reboot;
114                     StreamUtility.copyStream(in = zf.getInputStream(ze), reboot = openFileOutput("reboot", MODE_PRIVATE));
115                     reboot.close();
116                     in.close();
117
118                     final File su = extractSu();
119
120                     String command =
121                             String.format("cat %s > /cache/superuser.zip\n", zip.getAbsolutePath()) +
122                             String.format("cat %s > /cache/su\n", su.getAbsolutePath()) +
123                             String.format("cat %s > /cache/Superuser.apk\n", getPackageCodePath()) +
124                             "mkdir /cache/recovery\n" +
125                             "echo '--update_package=CACHE:superuser.zip' > /cache/recovery/command\n" +
126                             "chmod 644 /cache/superuser.zip\n" +
127                             "chmod 644 /cache/recovery/command\n" +
128                             "sync\n" +
129                             String.format("chmod 755 %s\n", getFileStreamPath("reboot").getAbsolutePath()) +
130                             "reboot recovery\n";
131                     Process p = Runtime.getRuntime().exec("su");
132                     p.getOutputStream().write(command.getBytes());
133                     p.getOutputStream().close();
134                     File rebootScript = getFileStreamPath("reboot.sh");
135                     StreamUtility.writeFile(rebootScript, "reboot recovery ; " + getFileStreamPath("reboot").getAbsolutePath() + " recovery ;");
136                     p.waitFor();
137                     Runtime.getRuntime().exec(new String[] { "su", "-c", ". " + rebootScript.getAbsolutePath() });
138                     if (p.waitFor() != 0)
139                         throw new Exception("non zero result");
140                 }
141                 catch (Exception ex) {
142                     ex.printStackTrace();
143                     dlg.dismiss();
144
145                     runOnUiThread(new Runnable() {
146                         @Override
147                         public void run() {
148                             AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
149                             builder.setPositiveButton(android.R.string.ok, null);
150                             builder.setTitle(R.string.install);
151                             builder.setMessage(R.string.install_error);
152                             builder.create().show();
153                         }
154                     });
155                 }
156             }
157         }.start();
158     }
159     
160     void doSystemInstall() {
161         final ProgressDialog dlg = new ProgressDialog(this);
162         dlg.setTitle(R.string.installing);
163         dlg.setMessage(getString(R.string.installing_superuser));
164         dlg.setIndeterminate(true);
165         dlg.show();
166         new Thread() {
167             public void run() {
168                 boolean _error = false;
169                 try {
170                     final File su = extractSu();
171                     final String command =
172                             "mount -orw,remount /system\n" +
173                             "rm /system/xbin/su\n" +
174                             "rm /system/bin/su\n" +
175                             "rm /system/app/Supersu.*\n" +
176                             "rm /system/app/superuser.*\n" +
177                             "rm /system/app/supersu.*\n" +
178                             "rm /system/app/SuperUser.*\n" +
179                             "rm /system/app/SuperSU.*\n" +
180                             String.format("cat %s > /system/xbin/su\n", su.getAbsolutePath()) +
181                             "chmod 6755 /system/xbin/su\n" +
182                             "ln -s /system/xbin/su /system/bin/su\n" +
183                             "mount -oro,remount /system\n" +
184                             "sync\n";
185                     Process p = Runtime.getRuntime().exec("su");
186                     p.getOutputStream().write(command.getBytes());
187                     p.getOutputStream().close();
188                     if (p.waitFor() != 0)
189                         throw new Exception("non zero result");
190                     SuHelper.checkSu(MainActivity.this);
191                 }
192                 catch (Exception ex) {
193                     _error = true;
194                     ex.printStackTrace();
195                 }
196                 dlg.dismiss();
197                 final boolean error = _error;
198                 runOnUiThread(new Runnable() {
199                     @Override
200                     public void run() {
201                         AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
202                         builder.setPositiveButton(android.R.string.ok, null);
203                         builder.setTitle(R.string.install);
204                         
205                         if (error) {
206                             builder.setMessage(R.string.install_error);
207                         }
208                         else {
209                             builder.setMessage(R.string.install_success);
210                         }
211                         builder.create().show();
212                     }
213                 });
214             };
215         }.start();
216     }
217     
218     void doInstall() {
219         AlertDialog.Builder builder = new AlertDialog.Builder(this);
220         builder.setTitle(R.string.install);
221         builder.setMessage(R.string.install_superuser_info);
222         builder.setPositiveButton(R.string.install, new OnClickListener() {
223             @Override
224             public void onClick(DialogInterface dialog, int which) {
225                 doSystemInstall();
226             }
227         });
228         builder.setNegativeButton(android.R.string.cancel, null);
229         builder.setNeutralButton(R.string.recovery_install, new OnClickListener() {
230             @Override
231             public void onClick(DialogInterface dialog, int which) {
232                 doRecoveryInstall();
233             }
234         });
235         builder.create().show();
236     }
237     
238     private void saveWhatsNew() {
239         Settings.setString(this, "whats_new", WHATS_NEW);
240     }
241     
242     // this is intentionally not localized as it will change constantly.
243     private static final String WHATS_NEW = "This update includes a security fix. See Google Play release notes for more information.";
244     protected void doWhatsNew() {
245         if (WHATS_NEW.equals(Settings.getString(this, "whats_new")))
246             return;
247         saveWhatsNew();
248         AlertDialog.Builder builder = new AlertDialog.Builder(this);
249         builder.setTitle(R.string.whats_new);
250         builder.setIcon(R.drawable.ic_launcher);
251         builder.setMessage(WHATS_NEW);
252         builder.setPositiveButton(R.string.rate, new OnClickListener() {
253             @Override
254             public void onClick(DialogInterface dialog, int which) {
255                 Intent i = new Intent();
256                 i.setData(Uri.parse("market://details?id=com.koushikdutta.superuser"));
257                 startActivity(i);
258             }
259         });
260         builder.setNegativeButton(android.R.string.cancel, null);
261         builder.create().show();
262     }
263
264     @Override
265     protected void onCreate(Bundle savedInstanceState) {
266         Settings.applyDarkThemeSetting(this, R.style.SuperuserDarkActivity);
267         super.onCreate(savedInstanceState);
268         
269         if (Settings.getBoolean(this, "first_run", true)) {
270             saveWhatsNew();
271             Settings.setBoolean(this, "first_run", false);
272         }
273         
274         final ProgressDialog dlg = new ProgressDialog(this);
275         dlg.setTitle(R.string.superuser);
276         dlg.setMessage(getString(R.string.checking_superuser));
277         dlg.setIndeterminate(true);
278         dlg.show();
279         new Thread() {
280             public void run() {
281                 boolean _error = false;
282                 try {
283                     SuHelper.checkSu(MainActivity.this);
284                 }
285                 catch (Exception e) {
286                     e.printStackTrace();
287                     _error = true;
288                 }
289                 final boolean error = _error;
290                 dlg.dismiss();
291                 runOnUiThread(new Runnable() {
292                     @Override
293                     public void run() {
294                         if (error) {
295                             doInstall();
296                         }
297                         else {
298                             doWhatsNew();
299                         }
300                     }
301                 });
302             };
303         }.start();
304     }
305 }