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;
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;
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;
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;
44 public class MainActivity extends BetterListActivity {
45 public MainActivity() {
46 super(PolicyFragment.class);
49 public PolicyFragmentInternal getFragment() {
50 return (PolicyFragmentInternal)super.getFragment();
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() {
60 public boolean onMenuItemClick(MenuItem item) {
61 getFragment().setContent(new AboutFragment(), true, getString(R.string.about));
66 return super.onCreateOptionsMenu(menu);
69 File extractSu() throws IOException, InterruptedException {
70 Process p = Runtime.getRuntime().exec("cat /proc/cpuinfo");
71 String contents = StreamUtility.readToEnd(p.getInputStream());
72 p.getInputStream().close();
74 contents = contents.toLowerCase();
75 String arch = "armeabi";
76 if (contents.contains("x86"))
78 ZipFile zf = new ZipFile(getPackageCodePath());
79 ZipEntry su = zf.getEntry("assets/" + arch + "/su");
80 InputStream zin = zf.getInputStream(su);
81 File ret = getFileStreamPath("su");
82 FileOutputStream fout = new FileOutputStream(ret);
83 StreamUtility.copyStream(zin, fout);
90 void doRecoveryInstall() {
91 final ProgressDialog dlg = new ProgressDialog(this);
92 dlg.setTitle(R.string.installing);
93 dlg.setMessage(getString(R.string.installing_superuser));
94 dlg.setIndeterminate(true);
97 void doEntry(ZipOutputStream zout, String entryName, String dest) throws IOException {
98 ZipFile zf = new ZipFile(getPackageCodePath());
99 ZipEntry ze = zf.getEntry(entryName);
100 zout.putNextEntry(new ZipEntry(dest));
102 StreamUtility.copyStream(in = zf.getInputStream(ze), zout);
110 File zip = getFileStreamPath("superuser.zip");
111 ZipOutputStream zout = new ZipOutputStream(new FileOutputStream(zip));
112 doEntry(zout, "assets/update-binary", "META-INF/com/google/android/update-binary");
115 final File su = extractSu();
118 String.format("cat %s > /cache/superuser.zip\n", zip.getAbsolutePath()) +
119 String.format("cat %s > /cache/su\n", su.getAbsolutePath()) +
120 String.format("cat %s > /cache/Superuser.apk\n", getPackageCodePath()) +
121 "mkdir /cache/recovery\n" +
122 "echo '--update_package=CACHE:superuser.zip' > /cache/recovery/command\n" +
123 "chmod 644 /cache/superuser.zip\n" +
124 "chmod 644 /cache/recovery/command\n" +
127 Process p = Runtime.getRuntime().exec("su");
128 p.getOutputStream().write(command.getBytes());
129 p.getOutputStream().close();
130 if (p.waitFor() != 0)
131 throw new Exception("non zero result");
133 catch (Exception ex) {
134 ex.printStackTrace();
137 runOnUiThread(new Runnable() {
140 AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
141 builder.setPositiveButton(android.R.string.ok, null);
142 builder.setTitle(R.string.install);
143 builder.setMessage(R.string.install_error);
144 builder.create().show();
152 void doSystemInstall() {
153 final ProgressDialog dlg = new ProgressDialog(this);
154 dlg.setTitle(R.string.installing);
155 dlg.setMessage(getString(R.string.installing_superuser));
156 dlg.setIndeterminate(true);
160 boolean _error = false;
162 final File su = extractSu();
163 final String command =
164 "mount -orw,remount /system\n" +
165 "rm /system/xbin/su\n" +
166 "rm /system/bin/su\n" +
167 "rm /system/app/Supersu.*\n" +
168 "rm /system/app/superuser.*\n" +
169 "rm /system/app/supersu.*\n" +
170 "rm /system/app/SuperUser.*\n" +
171 "rm /system/app/SuperSU.*\n" +
172 String.format("cat %s > /system/xbin/su\n", su.getAbsolutePath()) +
173 "chmod 6755 /system/xbin/su\n" +
174 "ln -s /system/xbin/su /system/bin/su\n" +
175 "mount -oro,remount /system\n" +
177 Process p = Runtime.getRuntime().exec("su");
178 p.getOutputStream().write(command.getBytes());
179 p.getOutputStream().close();
180 if (p.waitFor() != 0)
181 throw new Exception("non zero result");
182 SuHelper.checkSu(MainActivity.this);
184 catch (Exception ex) {
186 ex.printStackTrace();
189 final boolean error = _error;
190 runOnUiThread(new Runnable() {
193 AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
194 builder.setPositiveButton(android.R.string.ok, null);
195 builder.setTitle(R.string.install);
198 builder.setMessage(R.string.install_error);
201 builder.setMessage(R.string.install_success);
203 builder.create().show();
211 AlertDialog.Builder builder = new AlertDialog.Builder(this);
212 builder.setTitle(R.string.install);
213 builder.setMessage(R.string.install_superuser_info);
214 builder.setPositiveButton(R.string.install, new OnClickListener() {
216 public void onClick(DialogInterface dialog, int which) {
220 builder.setNegativeButton(android.R.string.cancel, null);
221 builder.setNeutralButton(R.string.recovery_install, new OnClickListener() {
223 public void onClick(DialogInterface dialog, int which) {
227 builder.create().show();
230 private void saveWhatsNew() {
231 Settings.setString(this, "whats_new", WHATS_NEW);
234 // this is intentionally not localized as it will change constantly.
235 private static final String WHATS_NEW = "Notifications can now be configured per app.\n\nThe Superuser theme can be configured in Settings.";
236 protected void doWhatsNew() {
237 if (WHATS_NEW.equals(Settings.getString(this, "whats_new")))
240 AlertDialog.Builder builder = new AlertDialog.Builder(this);
241 builder.setTitle(R.string.whats_new);
242 builder.setIcon(R.drawable.ic_launcher);
243 builder.setMessage(WHATS_NEW);
244 builder.setPositiveButton(R.string.rate, new OnClickListener() {
246 public void onClick(DialogInterface dialog, int which) {
247 Intent i = new Intent();
248 i.setData(Uri.parse("market://details?id=com.koushikdutta.superuser"));
252 builder.setNegativeButton(android.R.string.cancel, null);
253 builder.create().show();
257 protected void onCreate(Bundle savedInstanceState) {
258 Settings.applyDarkThemeSetting(this, R.style.SuperuserDarkActivity);
259 super.onCreate(savedInstanceState);
261 if (Settings.getBoolean(this, "first_run", true)) {
263 Settings.setBoolean(this, "first_run", false);
266 final ProgressDialog dlg = new ProgressDialog(this);
267 dlg.setTitle(R.string.superuser);
268 dlg.setMessage(getString(R.string.checking_superuser));
269 dlg.setIndeterminate(true);
273 boolean _error = false;
275 SuHelper.checkSu(MainActivity.this);
277 catch (Exception e) {
281 final boolean error = _error;
283 runOnUiThread(new Runnable() {