packages/apps/Superuser
リビジョン | e5940190f346347db7ec49f6e5e9c279cafeb78b (tree) |
---|---|
日時 | 2010-08-23 08:31:32 |
作者 | ChainsDD <chainsdd@gmai...> |
コミッター | ChainsDD |
Download binaries instead of packaging them in the apk.
@@ -1,44 +1,27 @@ | ||
1 | 1 | package com.noshufou.android.su; |
2 | 2 | |
3 | 3 | import java.io.BufferedReader; |
4 | -import java.io.DataOutputStream; | |
5 | -import java.io.File; | |
6 | -import java.io.FileInputStream; | |
7 | -import java.io.FileNotFoundException; | |
8 | -import java.io.FileOutputStream; | |
9 | 4 | import java.io.IOException; |
10 | -import java.io.InputStream; | |
11 | 5 | import java.io.InputStreamReader; |
12 | 6 | |
13 | -import android.app.AlertDialog; | |
14 | 7 | import android.app.TabActivity; |
15 | -import android.content.Context; | |
16 | -import android.content.DialogInterface; | |
17 | 8 | import android.content.Intent; |
18 | 9 | import android.content.SharedPreferences; |
19 | 10 | import android.content.pm.PackageManager; |
20 | 11 | import android.content.pm.PackageManager.NameNotFoundException; |
21 | 12 | import android.content.res.Resources; |
22 | -import android.os.AsyncTask; | |
23 | 13 | import android.os.Bundle; |
24 | -import android.os.Environment; | |
25 | -import android.os.Build.VERSION; | |
26 | 14 | import android.preference.PreferenceManager; |
27 | 15 | import android.util.Log; |
28 | 16 | import android.widget.TabHost; |
29 | -import android.widget.Toast; | |
30 | 17 | |
31 | 18 | public class Su extends TabActivity { |
32 | 19 | private static final String TAG = "Su"; |
33 | 20 | |
34 | - private Context mContext; | |
35 | - | |
36 | 21 | @Override |
37 | 22 | protected void onCreate(Bundle savedInstanceState) { |
38 | 23 | super.onCreate(savedInstanceState); |
39 | 24 | |
40 | - mContext = this; | |
41 | - | |
42 | 25 | setContentView(R.layout.main); |
43 | 26 | |
44 | 27 | Resources res = getResources(); |
@@ -88,38 +71,22 @@ public class Su extends TabActivity { | ||
88 | 71 | } |
89 | 72 | Log.d(TAG, "First run for version " + versionCode); |
90 | 73 | |
91 | - copyNewFiles(); | |
92 | - | |
93 | 74 | String suVer = getSuVersion(); |
94 | - String expectedSuVer = getString(R.string.expected_su_version) + ((versionCode < 5)?"-cd":"-ef"); | |
95 | - Log.d(TAG, "Actual su version: " + suVer); | |
96 | - Log.d(TAG, "Expected su version: " + expectedSuVer); | |
97 | - if (suVer == null || !suVer.equals(expectedSuVer)) { | |
98 | - Log.d(TAG, "System has outdated su, attempting to copy new version"); | |
99 | - new AlertDialog.Builder(mContext).setTitle(R.string.new_su) | |
100 | - .setMessage(R.string.new_su_msg) | |
101 | - .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { | |
102 | - | |
103 | - @Override | |
104 | - public void onClick(DialogInterface dialog, int which) { | |
105 | - new AutoUpdateTask().execute(); | |
106 | - } | |
107 | - }) | |
108 | - .setNegativeButton(android.R.string.cancel, null) | |
109 | - .create().show(); | |
110 | - } | |
75 | + Log.d(TAG, "su version: " + suVer); | |
76 | + new Updater(this, suVer).doUpdate(); | |
77 | + | |
111 | 78 | SharedPreferences.Editor editor = prefs.edit(); |
112 | 79 | editor.putInt("last_run", versionCode); |
113 | 80 | editor.commit(); |
114 | 81 | } |
115 | 82 | |
116 | - public static String getSuVersion() | |
83 | + public static String getSuVersion() | |
117 | 84 | { |
118 | 85 | Process process = null; |
119 | 86 | try { |
120 | 87 | process = Runtime.getRuntime().exec("su -v"); |
121 | 88 | BufferedReader stdInput = new BufferedReader(new InputStreamReader(process.getInputStream())); |
122 | - String suVersion = stdInput.readLine(); | |
89 | + String suVersion = stdInput.readLine(); | |
123 | 90 | stdInput.close(); |
124 | 91 | return suVersion; |
125 | 92 | } catch (IOException e) { |
@@ -127,148 +94,4 @@ public class Su extends TabActivity { | ||
127 | 94 | return null; |
128 | 95 | } |
129 | 96 | } |
130 | - | |
131 | - private void copyNewFiles() { | |
132 | - String[] files = fileList(); | |
133 | - for (String s : files) { | |
134 | - deleteFile(s); | |
135 | - } | |
136 | - int sdkVersion = Integer.parseInt(VERSION.SDK); | |
137 | - InputStream fileInput = null; | |
138 | - FileOutputStream fileOutput = null; | |
139 | - int zipFile, binFile; | |
140 | - Log.d(TAG, "sdkVersion = " + sdkVersion); | |
141 | - if (sdkVersion < 5) { | |
142 | - Log.d(TAG, "Copying files for cupcake/donut"); | |
143 | - zipFile = R.raw.su_2_3_1_bin_cd_signed; | |
144 | - binFile = R.raw.su_cd; | |
145 | - } else { | |
146 | - Log.d(TAG, "Copying files for eclair/froyo"); | |
147 | - zipFile = R.raw.su_2_3_1_bin_ef_signed; | |
148 | - binFile = R.raw.su_ef; | |
149 | - } | |
150 | - try { | |
151 | - fileInput = getResources().openRawResource(zipFile); | |
152 | - byte[] zipReader = new byte[fileInput.available()]; | |
153 | - while (fileInput.read(zipReader) != -1); | |
154 | - fileInput.close(); | |
155 | - fileOutput = openFileOutput(getString(R.string.update_filename), MODE_WORLD_READABLE); | |
156 | - fileOutput.write(zipReader); | |
157 | - fileOutput.close(); | |
158 | - | |
159 | - fileInput = getResources().openRawResource(binFile); | |
160 | - byte[] binReader = new byte[fileInput.available()]; | |
161 | - while (fileInput.read(binReader) != -1); | |
162 | - fileInput.close(); | |
163 | - fileOutput = openFileOutput("su", MODE_WORLD_READABLE); | |
164 | - fileOutput.write(binReader); | |
165 | - fileOutput.close(); | |
166 | - } catch (IOException e) { | |
167 | - e.printStackTrace(); | |
168 | - } | |
169 | - | |
170 | - } | |
171 | - | |
172 | - private void copyUpdateZip(boolean fromError) { | |
173 | - final String updateFilename = getString(R.string.update_filename); | |
174 | - if (fromError) { | |
175 | - new AlertDialog.Builder(mContext).setTitle(R.string.su_not_updated_title) | |
176 | - .setMessage(R.string.su_not_updated) | |
177 | - .setNeutralButton(android.R.string.ok, null) | |
178 | - .create().show(); | |
179 | - } | |
180 | - File sdDir = new File(Environment.getExternalStorageDirectory().getPath()); | |
181 | - if (sdDir.exists() && sdDir.canWrite()) { | |
182 | - File file = new File(sdDir.getAbsolutePath() + "/" + updateFilename); | |
183 | - try { | |
184 | - file.createNewFile(); | |
185 | - } catch (IOException e) { | |
186 | - Log.e(TAG, "Couldn't create destination for " + updateFilename, e); | |
187 | - } | |
188 | - if (file.exists() && file.canWrite()) { | |
189 | - FileInputStream fileIn; | |
190 | - FileOutputStream fileOut; | |
191 | - try { | |
192 | - fileIn = openFileInput(updateFilename); | |
193 | - fileOut = new FileOutputStream(file); | |
194 | - byte[] reader = new byte[fileIn.available()]; | |
195 | - while (fileIn.read(reader) != -1); | |
196 | - fileOut.write(reader); | |
197 | - if (fileIn != null) { | |
198 | - fileIn.close(); | |
199 | - } | |
200 | - if (fileOut != null) { | |
201 | - fileOut.close(); | |
202 | - } | |
203 | - } catch (FileNotFoundException e) { | |
204 | - Log.e(TAG, "Error:", e); | |
205 | - } catch (IOException e) { | |
206 | - Log.e(TAG, "Error:", e); | |
207 | - } | |
208 | - } | |
209 | - } | |
210 | - } | |
211 | - | |
212 | - public class AutoUpdateTask extends AsyncTask<String, Integer, Boolean> { | |
213 | - | |
214 | - @Override | |
215 | - protected Boolean doInBackground(String... params) { | |
216 | - String device = null; | |
217 | - boolean foundSystem = false; | |
218 | - try { | |
219 | - Process process = Runtime.getRuntime().exec("mount"); | |
220 | - BufferedReader stdInput = new BufferedReader(new InputStreamReader(process.getInputStream())); | |
221 | - String line; | |
222 | - while ((line = stdInput.readLine()) != null) { | |
223 | - String[] array = line.split(" "); | |
224 | - device = array[0]; | |
225 | - if ((array[1].equals("on") && array[2].equals("/system")) || | |
226 | - array[1].equals("/system")) { | |
227 | - foundSystem = true; | |
228 | - break; | |
229 | - } | |
230 | - } | |
231 | - } catch (IOException e) { | |
232 | - Log.e(TAG, "Problem remounting /system", e); | |
233 | - return false; | |
234 | - } | |
235 | - | |
236 | - if (foundSystem && device != null) { | |
237 | - final String mountDev = device; | |
238 | - Process process; | |
239 | - try { | |
240 | - process = Runtime.getRuntime().exec("su"); | |
241 | - DataOutputStream os = new DataOutputStream(process.getOutputStream()); | |
242 | - os.writeBytes("mount -o remount,rw " + mountDev + " /system\n"); | |
243 | - os.writeBytes("cat /data/data/com.noshufou.android.su/files/su > /system/bin/su\n"); | |
244 | - os.writeBytes("chmod 06755 /system/bin/su\n"); | |
245 | - os.writeBytes("mount -o remount,ro " + mountDev + " /system\n"); | |
246 | - os.writeBytes("exit\n"); | |
247 | - try { | |
248 | - process.waitFor(); | |
249 | - if (process.exitValue() != 255) { | |
250 | - return true; | |
251 | - } else { | |
252 | - return false; | |
253 | - } | |
254 | - } catch (InterruptedException e) { | |
255 | - return false; | |
256 | - } | |
257 | - } catch (IOException e) { | |
258 | - return false; | |
259 | - } | |
260 | - } | |
261 | - return false; | |
262 | - } | |
263 | - | |
264 | - @Override | |
265 | - protected void onPostExecute(Boolean result) { | |
266 | - if (result) { | |
267 | - Toast.makeText(mContext, R.string.su_updated, Toast.LENGTH_SHORT).show(); | |
268 | - } else { | |
269 | - copyUpdateZip(true); | |
270 | - } | |
271 | - } | |
272 | - | |
273 | - } | |
274 | 97 | } |
@@ -0,0 +1,238 @@ | ||
1 | +package com.noshufou.android.su; | |
2 | + | |
3 | +import java.io.BufferedInputStream; | |
4 | +import java.io.BufferedReader; | |
5 | +import java.io.DataOutputStream; | |
6 | +import java.io.File; | |
7 | +import java.io.FileInputStream; | |
8 | +import java.io.FileNotFoundException; | |
9 | +import java.io.FileOutputStream; | |
10 | +import java.io.IOException; | |
11 | +import java.io.InputStreamReader; | |
12 | +import java.net.MalformedURLException; | |
13 | +import java.net.URL; | |
14 | +import java.net.URLConnection; | |
15 | + | |
16 | +import org.apache.http.util.ByteArrayBuffer; | |
17 | +import org.json.JSONException; | |
18 | +import org.json.JSONObject; | |
19 | + | |
20 | +import android.app.AlertDialog; | |
21 | +import android.content.Context; | |
22 | +import android.content.DialogInterface; | |
23 | +import android.os.AsyncTask; | |
24 | +import android.os.Environment; | |
25 | +import android.os.Build.VERSION; | |
26 | +import android.util.Log; | |
27 | +import android.widget.Toast; | |
28 | + | |
29 | +public class Updater { | |
30 | + private static final String TAG = "Su.Updater"; | |
31 | + | |
32 | + private JSONObject mManifest; | |
33 | + private JSONObject mBinaries; | |
34 | + private Context mContext; | |
35 | + private String mSuVersion; | |
36 | + | |
37 | + public Updater(Context context, String suVersion) { | |
38 | + mContext = context; | |
39 | + mSuVersion = suVersion; | |
40 | + } | |
41 | + | |
42 | + public void doUpdate() { | |
43 | + // Get the process started, it all goes from here. | |
44 | + new DownloadFileTask().execute("http://dl.dropbox.com/u/6408470/Superuser/manifest.json"); | |
45 | + } | |
46 | + | |
47 | + private void postManifest() { | |
48 | + String expectedSuVersion = ""; | |
49 | + try { | |
50 | + expectedSuVersion = mManifest.getString("version"); | |
51 | + int sdkVersion = Integer.parseInt(VERSION.SDK); | |
52 | + if (sdkVersion < 5) { | |
53 | + expectedSuVersion += "-cd"; | |
54 | + mBinaries = mManifest.getJSONObject("cd"); | |
55 | + } else { | |
56 | + expectedSuVersion += "-ef"; | |
57 | + mBinaries = mManifest.getJSONObject("ef"); | |
58 | + } | |
59 | + if (mSuVersion == null || !mSuVersion.equals(expectedSuVersion)) { | |
60 | + Log.d(TAG, "System has outdated su, attempting to copy new version"); | |
61 | + new AlertDialog.Builder(mContext).setTitle(R.string.new_su) | |
62 | + .setMessage(R.string.new_su_msg) | |
63 | + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { | |
64 | + | |
65 | + @Override | |
66 | + public void onClick(DialogInterface dialog, int which) { | |
67 | + try { | |
68 | + new DownloadFileTask() | |
69 | + .execute(mBinaries.getString("binary"), mBinaries.getString("update")); | |
70 | + } catch (JSONException e) { | |
71 | + Log.e(TAG, "Malformed JSON", e); | |
72 | + } | |
73 | + } | |
74 | + }) | |
75 | + .setNegativeButton(android.R.string.cancel, null) | |
76 | + .create().show(); | |
77 | + } else { | |
78 | + Toast.makeText(mContext, R.string.su_up_to_date, Toast.LENGTH_SHORT).show(); | |
79 | + } | |
80 | + } catch (JSONException e) { | |
81 | + Log.e(TAG, "Malformed JSON", e); | |
82 | + return; | |
83 | + } | |
84 | + } | |
85 | + | |
86 | + private void copyUpdateZip(boolean fromError) { | |
87 | + final String updateFilename = mContext.getString(R.string.update_filename); | |
88 | + if (fromError) { | |
89 | + new AlertDialog.Builder(mContext).setTitle(R.string.su_not_updated_title) | |
90 | + .setMessage(R.string.su_not_updated) | |
91 | + .setNeutralButton(android.R.string.ok, null) | |
92 | + .create().show(); | |
93 | + } | |
94 | + File sdDir = new File(Environment.getExternalStorageDirectory().getPath()); | |
95 | + if (sdDir.exists() && sdDir.canWrite()) { | |
96 | + File file = new File(sdDir.getAbsolutePath() + "/" + updateFilename); | |
97 | + try { | |
98 | + file.createNewFile(); | |
99 | + } catch (IOException e) { | |
100 | + Log.e(TAG, "Couldn't create destination for " + updateFilename, e); | |
101 | + } | |
102 | + if (file.exists() && file.canWrite()) { | |
103 | + FileInputStream fileIn; | |
104 | + FileOutputStream fileOut; | |
105 | + try { | |
106 | + fileIn = mContext.openFileInput(updateFilename); | |
107 | + fileOut = new FileOutputStream(file); | |
108 | + byte[] reader = new byte[fileIn.available()]; | |
109 | + while (fileIn.read(reader) != -1); | |
110 | + fileOut.write(reader); | |
111 | + if (fileIn != null) { | |
112 | + fileIn.close(); | |
113 | + } | |
114 | + if (fileOut != null) { | |
115 | + fileOut.close(); | |
116 | + } | |
117 | + } catch (FileNotFoundException e) { | |
118 | + Log.e(TAG, "Error:", e); | |
119 | + } catch (IOException e) { | |
120 | + Log.e(TAG, "Error:", e); | |
121 | + } | |
122 | + } | |
123 | + } | |
124 | + } | |
125 | + | |
126 | + private class DownloadFileTask extends AsyncTask<String, Integer, String> { | |
127 | + | |
128 | + @Override | |
129 | + protected String doInBackground(String... params) { | |
130 | + for (String s : params) { | |
131 | + try { | |
132 | + URL url = new URL(s); | |
133 | + String file = s.substring(s.lastIndexOf("/") + 1); | |
134 | + | |
135 | + URLConnection urlCon = url.openConnection(); | |
136 | + BufferedInputStream bis = new BufferedInputStream(urlCon.getInputStream()); | |
137 | + | |
138 | + ByteArrayBuffer baf = new ByteArrayBuffer(50); | |
139 | + int current = 0; | |
140 | + while ((current = bis.read()) != -1) { | |
141 | + baf.append((byte) current); | |
142 | + } | |
143 | + if (file.equals("manifest.json")) { | |
144 | + try { | |
145 | + mManifest = new JSONObject(new String(baf.toByteArray())); | |
146 | + } catch (JSONException e) { | |
147 | + Log.e(TAG, "Bad JSON file", e); | |
148 | + } | |
149 | + } else { | |
150 | + FileOutputStream fos = mContext.openFileOutput(file, | |
151 | + Context.MODE_WORLD_READABLE); | |
152 | + fos.write(baf.toByteArray()); | |
153 | + fos.close(); | |
154 | + } | |
155 | + return file; | |
156 | + } catch (MalformedURLException e) { | |
157 | + Log.e(TAG, "Bad URL", e); | |
158 | + } catch (IOException e) { | |
159 | + Log.e(TAG, "Problem downloading file", e); | |
160 | + } | |
161 | + } | |
162 | + return null; | |
163 | + } | |
164 | + | |
165 | + @Override | |
166 | + protected void onPostExecute(String result) { | |
167 | + if (result.equals("manifest.json")) { | |
168 | + postManifest(); | |
169 | + } else if (result.equals("su")) { | |
170 | + new AutoUpdateTask().execute(); | |
171 | + } | |
172 | + } | |
173 | + } | |
174 | + | |
175 | + public class AutoUpdateTask extends AsyncTask<String, Integer, Boolean> { | |
176 | + | |
177 | + @Override | |
178 | + protected Boolean doInBackground(String... params) { | |
179 | + String device = null; | |
180 | + boolean foundSystem = false; | |
181 | + try { | |
182 | + Process process = Runtime.getRuntime().exec("mount"); | |
183 | + BufferedReader stdInput = new BufferedReader(new InputStreamReader(process.getInputStream())); | |
184 | + String line; | |
185 | + while ((line = stdInput.readLine()) != null) { | |
186 | + String[] array = line.split(" "); | |
187 | + device = array[0]; | |
188 | + if ((array[1].equals("on") && array[2].equals("/system")) || | |
189 | + array[1].equals("/system")) { | |
190 | + foundSystem = true; | |
191 | + break; | |
192 | + } | |
193 | + } | |
194 | + } catch (IOException e) { | |
195 | + Log.e(TAG, "Problem remounting /system", e); | |
196 | + return false; | |
197 | + } | |
198 | + | |
199 | + if (foundSystem && device != null) { | |
200 | + final String mountDev = device; | |
201 | + Process process; | |
202 | + try { | |
203 | + process = Runtime.getRuntime().exec("su"); | |
204 | + DataOutputStream os = new DataOutputStream(process.getOutputStream()); | |
205 | + os.writeBytes("mount -o remount,rw " + mountDev + " /system\n"); | |
206 | + os.writeBytes("cat /data/data/com.noshufou.android.su/files/su > /system/bin/su\n"); | |
207 | + os.writeBytes("chmod 06755 /system/bin/su\n"); | |
208 | + os.writeBytes("cat /data/data/com.noshufou.android.su/files/su > /system/sbin/su\n"); | |
209 | + os.writeBytes("chmod 06755 /system/sbin/su\n"); | |
210 | + os.writeBytes("mount -o remount,ro " + mountDev + " /system\n"); | |
211 | + os.writeBytes("exit\n"); | |
212 | + try { | |
213 | + process.waitFor(); | |
214 | + if (process.exitValue() != 255) { | |
215 | + return true; | |
216 | + } else { | |
217 | + return false; | |
218 | + } | |
219 | + } catch (InterruptedException e) { | |
220 | + return false; | |
221 | + } | |
222 | + } catch (IOException e) { | |
223 | + return false; | |
224 | + } | |
225 | + } | |
226 | + return false; | |
227 | + } | |
228 | + | |
229 | + @Override | |
230 | + protected void onPostExecute(Boolean result) { | |
231 | + if (result) { | |
232 | + Toast.makeText(mContext, R.string.su_updated, Toast.LENGTH_SHORT).show(); | |
233 | + } else { | |
234 | + copyUpdateZip(true); | |
235 | + } | |
236 | + } | |
237 | + } | |
238 | +} |