2 * Copyright (C) 2013 Koushik Dutta (@koush)
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.koushikdutta.superuser;
19 import android.app.AlertDialog;
20 import android.app.ProgressDialog;
21 import android.content.DialogInterface;
22 import android.content.DialogInterface.OnClickListener;
23 import android.content.Intent;
24 import android.net.Uri;
25 import android.os.Build;
26 import android.os.Bundle;
27 import android.util.Log;
28 import android.view.Menu;
29 import android.view.MenuInflater;
30 import android.view.MenuItem;
31 import android.view.MenuItem.OnMenuItemClickListener;
33 import com.koushikdutta.superuser.util.Settings;
34 import com.koushikdutta.superuser.util.StreamUtility;
35 import com.koushikdutta.superuser.util.SuHelper;
36 import com.koushikdutta.widgets.BetterListActivity;
39 import java.io.FileOutputStream;
40 import java.io.IOException;
41 import java.io.InputStream;
42 import java.util.zip.ZipEntry;
43 import java.util.zip.ZipFile;
44 import java.util.zip.ZipOutputStream;
46 public class MainActivity extends BetterListActivity {
47 public MainActivity() {
48 super(PolicyFragment.class);
51 public PolicyFragmentInternal getFragment() {
52 return (PolicyFragmentInternal)super.getFragment();
56 public boolean onCreateOptionsMenu(Menu menu) {
57 MenuInflater mi = new MenuInflater(this);
58 mi.inflate(R.menu.app, menu);
59 MenuItem about = menu.findItem(R.id.about);
60 about.setOnMenuItemClickListener(new OnMenuItemClickListener() {
62 public boolean onMenuItemClick(MenuItem item) {
63 getFragment().setContent(new AboutFragment(), true, getString(R.string.about));
68 return super.onCreateOptionsMenu(menu);
71 File extractSu() throws IOException, InterruptedException {
72 String arch = "armeabi";
73 if (System.getProperty("os.arch").contains("x86") || System.getProperty("os.arch").contains("i686") || System.getProperty("os.arch").contains("i386"))
75 ZipFile zf = new ZipFile(getPackageCodePath());
76 ZipEntry su = zf.getEntry("assets/" + arch + "/su");
77 InputStream zin = zf.getInputStream(su);
78 File ret = getFileStreamPath("su");
79 FileOutputStream fout = new FileOutputStream(ret);
80 StreamUtility.copyStream(zin, fout);
87 void doRecoveryInstall() {
88 final ProgressDialog dlg = new ProgressDialog(this);
89 dlg.setTitle(R.string.installing);
90 dlg.setMessage(getString(R.string.installing_superuser));
91 dlg.setIndeterminate(true);
94 void doEntry(ZipOutputStream zout, String entryName, String dest) throws IOException {
95 ZipFile zf = new ZipFile(getPackageCodePath());
96 ZipEntry ze = zf.getEntry(entryName);
97 zout.putNextEntry(new ZipEntry(dest));
99 StreamUtility.copyStream(in = zf.getInputStream(ze), zout);
107 File zip = getFileStreamPath("superuser.zip");
108 ZipOutputStream zout = new ZipOutputStream(new FileOutputStream(zip));
109 doEntry(zout, "assets/update-binary", "META-INF/com/google/android/update-binary");
110 doEntry(zout, "assets/install-recovery.sh", "install-recovery.sh");
113 ZipFile zf = new ZipFile(getPackageCodePath());
114 ZipEntry ze = zf.getEntry("assets/reboot");
116 FileOutputStream reboot;
117 StreamUtility.copyStream(in = zf.getInputStream(ze), reboot = openFileOutput("reboot", MODE_PRIVATE));
121 final File su = extractSu();
124 String.format("cat %s > /cache/superuser.zip\n", zip.getAbsolutePath()) +
125 String.format("cat %s > /cache/su\n", su.getAbsolutePath()) +
126 String.format("cat %s > /cache/Superuser.apk\n", getPackageCodePath()) +
127 "mkdir /cache/recovery\n" +
128 "echo '--update_package=CACHE:superuser.zip' > /cache/recovery/command\n" +
129 "chmod 644 /cache/superuser.zip\n" +
130 "chmod 644 /cache/recovery/command\n" +
132 String.format("chmod 755 %s\n", getFileStreamPath("reboot").getAbsolutePath()) +
134 Process p = Runtime.getRuntime().exec("su");
135 p.getOutputStream().write(command.getBytes());
136 p.getOutputStream().close();
137 File rebootScript = getFileStreamPath("reboot.sh");
138 StreamUtility.writeFile(rebootScript, "reboot recovery ; " + getFileStreamPath("reboot").getAbsolutePath() + " recovery ;");
140 Runtime.getRuntime().exec(new String[] { "su", "-c", ". " + rebootScript.getAbsolutePath() });
141 if (p.waitFor() != 0)
142 throw new Exception("non zero result");
144 catch (Exception ex) {
145 ex.printStackTrace();
148 runOnUiThread(new Runnable() {
151 AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
152 builder.setPositiveButton(android.R.string.ok, null);
153 builder.setTitle(R.string.install);
154 builder.setMessage(R.string.install_error);
155 builder.create().show();
163 void doSystemInstall() {
164 final ProgressDialog dlg = new ProgressDialog(this);
165 dlg.setTitle(R.string.installing);
166 dlg.setMessage(getString(R.string.installing_superuser));
167 dlg.setIndeterminate(true);
171 boolean _error = false;
173 final File su = extractSu();
174 final String command =
175 "mount -orw,remount /system\n" +
176 "rm /system/xbin/su\n" +
177 "rm /system/bin/su\n" +
178 "rm /system/app/Supersu.*\n" +
179 "rm /system/app/superuser.*\n" +
180 "rm /system/app/supersu.*\n" +
181 "rm /system/app/SuperUser.*\n" +
182 "rm /system/app/SuperSU.*\n" +
183 String.format("cat %s > /system/xbin/su\n", su.getAbsolutePath()) +
184 "chmod 6755 /system/xbin/su\n" +
185 "ln -s /system/xbin/su /system/bin/su\n" +
186 "mount -oro,remount /system\n" +
188 Process p = Runtime.getRuntime().exec("su");
189 p.getOutputStream().write(command.getBytes());
190 p.getOutputStream().close();
191 if (p.waitFor() != 0)
192 throw new Exception("non zero result");
193 SuHelper.checkSu(MainActivity.this);
195 catch (Exception ex) {
197 Log.e("Superuser", "error upgrading", ex);
200 final boolean error = _error;
201 runOnUiThread(new Runnable() {
204 AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
205 builder.setPositiveButton(android.R.string.ok, null);
206 builder.setTitle(R.string.install);
209 builder.setMessage(R.string.install_error);
212 builder.setMessage(R.string.install_success);
214 builder.create().show();
222 AlertDialog.Builder builder = new AlertDialog.Builder(this);
223 builder.setTitle(R.string.install);
224 builder.setMessage(R.string.install_superuser_info);
225 if (Build.VERSION.SDK_INT < 18) {
226 builder.setPositiveButton(R.string.install, new OnClickListener() {
228 public void onClick(DialogInterface dialog, int which) {
233 builder.setNegativeButton(android.R.string.cancel, null);
234 builder.setNeutralButton(R.string.recovery_install, new OnClickListener() {
236 public void onClick(DialogInterface dialog, int which) {
240 builder.create().show();
243 private void saveWhatsNew() {
244 Settings.setString(this, "whats_new", WHATS_NEW);
247 // this is intentionally not localized as it will change constantly.
248 private static final String WHATS_NEW = "Added support for Android 4.3.";
249 protected void doWhatsNew() {
250 if (WHATS_NEW.equals(Settings.getString(this, "whats_new")))
253 AlertDialog.Builder builder = new AlertDialog.Builder(this);
254 builder.setTitle(R.string.whats_new);
255 builder.setIcon(R.drawable.ic_launcher);
256 builder.setMessage(WHATS_NEW);
257 builder.setPositiveButton(R.string.rate, new OnClickListener() {
259 public void onClick(DialogInterface dialog, int which) {
260 Intent i = new Intent();
261 i.setData(Uri.parse("market://details?id=com.koushikdutta.superuser"));
265 builder.setNegativeButton(android.R.string.cancel, null);
266 builder.create().show();
270 protected void onCreate(Bundle savedInstanceState) {
271 Settings.applyDarkThemeSetting(this, R.style.SuperuserDarkActivity);
272 super.onCreate(savedInstanceState);
274 if (Settings.getBoolean(this, "first_run", true)) {
276 Settings.setBoolean(this, "first_run", false);
279 final ProgressDialog dlg = new ProgressDialog(this);
280 dlg.setTitle(R.string.superuser);
281 dlg.setMessage(getString(R.string.checking_superuser));
282 dlg.setIndeterminate(true);
286 boolean _error = false;
288 SuHelper.checkSu(MainActivity.this);
290 catch (Exception e) {
294 final boolean error = _error;
296 runOnUiThread(new Runnable() {