Merge lp:~humpolec-team/humpolec/UbuntuInstaller-ota into lp:humpolec
- UbuntuInstaller-ota
- Merge into UbuntuInstaller
Status: | Merged |
---|---|
Approved by: | Yuan-Chen Cheng |
Approved revision: | 45 |
Merge reported by: | Rex Tsai |
Merged at revision: | not available |
Proposed branch: | lp:~humpolec-team/humpolec/UbuntuInstaller-ota |
Merge into: | lp:humpolec |
Diff against target: |
1175 lines (+428/-315) 7 files modified
AndroidManifest.xml (+1/-0) assets/system-image-upgrader (+16/-11) assets/upgrade-checker (+16/-0) src/com/canonical/ubuntu/installer/InstallActivity.java (+34/-32) src/com/canonical/ubuntu/installer/LaunchActivity.java (+29/-6) src/com/canonical/ubuntu/installer/UbuntuInstallService.java (+332/-262) src/com/canonical/ubuntu/installer/VersionInfo.java (+0/-4) |
To merge this branch: | bzr merge lp:~humpolec-team/humpolec/UbuntuInstaller-ota |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Yuan-Chen Cheng (community) | Approve | ||
Review via email: mp+200035@code.launchpad.net |
Commit message
Description of the change
Support Ubuntu Touch upgrade.
* Find update_command in /cache/recovery, if update_command comes from /cache, it's upgradeable.
* Added Upgradeable checking script, check if upgrade image available in /cache
* Use INSTALLED_VERSION as DOWNLOADED_VERSION for system upgrade, since we don't know what's the version in /cache.
* New Intent Servcie for checking upgrade.
* make doInstallUbuntu works with images file stored in /cache.
* clean up UpdateComamnd function.
* only using executeSUCommands for shell access.
- 40. By Rex Tsai
-
use for busybox.
- 41. By Rex Tsai
-
Removed confusing comment on Ubuntu user data.
- 42. By Rex Tsai
-
s/UPGRADE_
UBUNTU/ IS_UBUNTU_ UPGRADABLE/ - 43. By Rex Tsai
-
Comment on updates command checking.
- 44. By Rex Tsai
-
Add comment on findUpgradeable()
- 45. By Rex Tsai
-
s/findUpgradeab
le/findInstallC ommand/
Rex Tsai (chihchun) wrote : | # |
Changes for meet YC's requests.
* 2f4e386 - s/findUpgradeab
* 6b5b8b9 - Add comment on findUpgradeable()
* 64ca88b - Comment on updates command checking.
* 686a879 - s/UPGRADE_
* 2d031a6 - Removed confusing comment on Ubuntu user data.
* 33e8eea - use ${BUSYBOX} for busybox.
Yuan-Chen Cheng (ycheng-twn) wrote : | # |
+1
I still don't like the function name isUpgradeable, but it's too personal and the code is good enough for now.
Rex Tsai (chihchun) wrote : | # |
I think we can integrate both checkifReadyToI
But need to clean up how we manage Shared Preferences data.
I will do merge first.
Preview Diff
1 | === modified file 'AndroidManifest.xml' |
2 | --- AndroidManifest.xml 2013-12-21 16:59:18 +0000 |
3 | +++ AndroidManifest.xml 2013-12-26 07:18:46 +0000 |
4 | @@ -62,6 +62,7 @@ |
5 | <action android:name="com.canonical.ubuntuinstaller.UbuntuInstallService.GET_SERVICE_STATE" /> |
6 | <action android:name="com.canonical.ubuntuinstaller.UbuntuInstallService.GET_PROGRESS_STATUS" /> |
7 | <action android:name="com.canonical.ubuntuinstaller.UbuntuInstallService.REBOOT_UBUNTU" /> |
8 | + <action android:name="com.canonical.ubuntuinstaller.UbuntuInstallService.IS_UBUNTU_UPGRADABLE" /> |
9 | </intent-filter> |
10 | </service> |
11 | </application> |
12 | |
13 | === modified file 'assets/system-image-upgrader' |
14 | --- assets/system-image-upgrader 2013-12-20 03:39:53 +0000 |
15 | +++ assets/system-image-upgrader 2013-12-26 07:18:46 +0000 |
16 | @@ -1,25 +1,30 @@ |
17 | -#!/sbin/sh |
18 | +#!/system/bin/sh |
19 | +# Copyright (C) 2013 Canonical Ltd. |
20 | |
21 | -export PATH=$PATH:${PWD} |
22 | -UPDATE_FOLDER=${PWD} |
23 | -PRIVATE_DIR=$2 |
24 | -BUSYBOX=busybox |
25 | -TAR=u_tar |
26 | -set -e |
27 | +# $0 [ubuntu command file] [app/private folder] |
28 | |
29 | if [ ! -e "$1" ]; then |
30 | echo "Command file doesn't exist: $1" |
31 | exit 1 |
32 | fi |
33 | -mv $1 $1.applying |
34 | + |
35 | COMMAND_FILE=$1.applying |
36 | - |
37 | REMOVE_LIST="$COMMAND_FILE" |
38 | - |
39 | +UPDATE_FOLDER=${PWD} |
40 | +PRIVATE_DIR=$2 |
41 | +BUSYBOX=busybox |
42 | +TAR=u_tar |
43 | TMP=/cache/tmp |
44 | +export PATH=${PWD}:${PRIVATE_DIR}:$PATH |
45 | |
46 | +set -e |
47 | echo "Starting image upgrader: $(date)" |
48 | |
49 | +# switch to the update command folder, which has the images |
50 | +cd `$BUSYBOX dirname $1` |
51 | +mv $1 $1.applying |
52 | + |
53 | + |
54 | # Functions |
55 | verify_signature() { |
56 | return 0 |
57 | @@ -143,7 +148,7 @@ |
58 | |
59 | # Process the command file |
60 | FULL_IMAGE=0 |
61 | -echo "Processing the command file" |
62 | +echo "Processing the command file $COMMAND_FILE" |
63 | while read line |
64 | do |
65 | set -- $line |
66 | |
67 | === added file 'assets/upgrade-checker' |
68 | --- assets/upgrade-checker 1970-01-01 00:00:00 +0000 |
69 | +++ assets/upgrade-checker 2013-12-26 07:18:46 +0000 |
70 | @@ -0,0 +1,16 @@ |
71 | +#!/system/bin/sh |
72 | +# Copyright (C) 2013 Canonical Ltd. |
73 | +# |
74 | +# Check if ubuntu_command exist |
75 | +# print the path and exit 0 if found. |
76 | + |
77 | +FILES=$@ |
78 | + |
79 | +for file in $FILES ; do |
80 | + if [[ -f $file ]] ; then |
81 | + echo $file |
82 | + exit 1 |
83 | + fi |
84 | +done |
85 | + |
86 | +exit 0 |
87 | |
88 | === modified file 'src/com/canonical/ubuntu/installer/InstallActivity.java' |
89 | --- src/com/canonical/ubuntu/installer/InstallActivity.java 2013-12-25 06:09:49 +0000 |
90 | +++ src/com/canonical/ubuntu/installer/InstallActivity.java 2013-12-26 07:18:46 +0000 |
91 | @@ -114,14 +114,14 @@ |
92 | } |
93 | mObserversRegistered = false; |
94 | } |
95 | - |
96 | + |
97 | @Override |
98 | public boolean onCreateOptionsMenu(Menu menu) { |
99 | // Inflate the menu; this adds items to the action bar if it is present. |
100 | getMenuInflater().inflate(R.menu.installer_menu, menu); |
101 | return true; |
102 | } |
103 | - |
104 | + |
105 | @Override |
106 | public boolean onOptionsItemSelected(MenuItem item) { |
107 | switch (item.getItemId()) { |
108 | @@ -183,11 +183,13 @@ |
109 | } |
110 | |
111 | private void checkIfUbuntuIsInstalled() { |
112 | - // check is there is Ubuntu installed |
113 | - if (UbuntuInstallService.isUbuntuInstalled(this.getApplicationContext())) { |
114 | - // go to launch screen, and kill this activity. |
115 | - LaunchActivity.startFrom(this); |
116 | - finish(); |
117 | + // check is there is Ubuntu installed or is upgradeable |
118 | + if (UbuntuInstallService.isUbuntuInstalled(getApplicationContext())) { |
119 | + if(!UbuntuInstallService.isUpgradeable(getApplicationContext())) { |
120 | + Log.d(TAG, "go to launch screen, and kill Install activity"); |
121 | + LaunchActivity.startFrom(this); |
122 | + finish(); |
123 | + } |
124 | } |
125 | } |
126 | |
127 | @@ -262,7 +264,7 @@ |
128 | // reset progress bar |
129 | mProgressBar.setProgress(0); |
130 | } |
131 | - |
132 | + |
133 | TextPickerDialog.OnChannelPicktListener mInstallDialogListener |
134 | = new TextPickerDialog.OnChannelPicktListener() { |
135 | public void onChannelPicked(Context context, String channel, boolean bootstrap, boolean latest) { |
136 | @@ -292,38 +294,38 @@ |
137 | |
138 | private void downloadVersion(final Context context, final String channel, final boolean bootstrap) { |
139 | final ProgressDialog progress = ProgressDialog.show(this, |
140 | - "Fetching versions", |
141 | - "Checking list of availanble versions for choosen channel", true); |
142 | + "Fetching versions", |
143 | + "Checking list of availanble versions for choosen channel", true); |
144 | new Thread(new Runnable() { |
145 | @Override |
146 | public void run() { |
147 | String jsonStr = Utils.httpDownload(UbuntuInstallService.BASE_URL |
148 | - + mAvailableChannels.get(channel)); |
149 | + + mAvailableChannels.get(channel)); |
150 | // TODO: handle malformed JSON |
151 | final List<Image> releases = JsonChannelParser.getAvailableReleases(jsonStr, ReleaseType.FULL); |
152 | - |
153 | + |
154 | runOnUiThread(new Runnable() { |
155 | - @Override |
156 | + @Override |
157 | public void run() { |
158 | progress.dismiss(); |
159 | // if there are available releases, show number picker |
160 | if (releases.size() != 0 && releases.get(0).files.length != 0) { |
161 | - int[] values = new int[releases.size()]; |
162 | - for (int i = 0 ; i < values.length ; ++i) { |
163 | - values[i] = releases.get(i).version; |
164 | - } |
165 | - new NumberPickerDialog(context, |
166 | - R.string.version_picker_dialog_title, |
167 | - R.string.action_install, |
168 | - R.string.cancel, |
169 | - values, |
170 | - 0, |
171 | - new NumberPickerDialog.OnNumberPicktListener() { |
172 | - @Override |
173 | - public void onNumberSelected(Context context, int value) { |
174 | - startDownload(channel, bootstrap, value); |
175 | - } |
176 | - }).show();; |
177 | + int[] values = new int[releases.size()]; |
178 | + for (int i = 0 ; i < values.length ; ++i) { |
179 | + values[i] = releases.get(i).version; |
180 | + } |
181 | + new NumberPickerDialog(context, |
182 | + R.string.version_picker_dialog_title, |
183 | + R.string.action_install, |
184 | + R.string.cancel, |
185 | + values, |
186 | + 0, |
187 | + new NumberPickerDialog.OnNumberPicktListener() { |
188 | + @Override |
189 | + public void onNumberSelected(Context context, int value) { |
190 | + startDownload(channel, bootstrap, value); |
191 | + } |
192 | + }).show();; |
193 | } |
194 | } |
195 | }); |
196 | @@ -359,7 +361,7 @@ |
197 | mProgressText.setText(""); |
198 | } |
199 | } |
200 | - break; |
201 | + break; |
202 | case FETCHING_CHANNELS: |
203 | mInstallButton.setText(Html.fromHtml(getResources().getString(R.string.install_button_label_fetching))); |
204 | mInstallButton.setEnabled(false); |
205 | @@ -406,7 +408,7 @@ |
206 | int progress = intent.getIntExtra(UbuntuInstallService.PROGRESS_EXTRA_INT, -1); |
207 | if (progress != -1) { |
208 | mProgressBar.setProgress(progress); |
209 | - Log.v(TAG, "Progress:" + progress); |
210 | + Log.d(TAG, "Progress: " + progress); |
211 | } |
212 | if (p != null && !p.equals("")) { |
213 | // update terminal with progress text |
214 | @@ -440,7 +442,7 @@ |
215 | } |
216 | updateUiElements(); |
217 | } |
218 | - |
219 | + |
220 | // Handle download result |
221 | } else if(action.equals(UbuntuInstallService.DOWNLOAD_RESULT)) { |
222 | int r = intent.getIntExtra(UbuntuInstallService.DOWNLOAD_RESULT_EXTRA_INT, -1); |
223 | |
224 | === modified file 'src/com/canonical/ubuntu/installer/LaunchActivity.java' |
225 | --- src/com/canonical/ubuntu/installer/LaunchActivity.java 2013-12-19 09:36:28 +0000 |
226 | +++ src/com/canonical/ubuntu/installer/LaunchActivity.java 2013-12-26 07:18:46 +0000 |
227 | @@ -8,6 +8,7 @@ |
228 | import android.content.Intent; |
229 | import android.content.IntentFilter; |
230 | import android.os.Bundle; |
231 | +import android.util.Log; |
232 | import android.view.Menu; |
233 | import android.view.MenuItem; |
234 | import android.view.View; |
235 | @@ -16,7 +17,6 @@ |
236 | |
237 | import com.canonical.ubuntu.widget.UbuntuButton; |
238 | |
239 | - |
240 | public class LaunchActivity extends Activity { |
241 | private static final String TAG = "UbuntuLaunchActivity"; |
242 | private UbuntuButton mRebootButton; |
243 | @@ -28,7 +28,7 @@ |
244 | private TextView mTextDescriptionLabel; |
245 | private TextView mTextDescription; |
246 | private VersionInfo mUbuntuVersion; |
247 | - |
248 | + |
249 | @Override |
250 | protected void onCreate(Bundle savedInstanceState) { |
251 | super.onCreate(savedInstanceState); |
252 | @@ -48,8 +48,11 @@ |
253 | mTextTitle.setText(R.string.launch_title); |
254 | mRebootButton.setText(R.string.reboot_button_label); |
255 | fillInstalledVersionInfo(); |
256 | + |
257 | + // check the upgradeable image. |
258 | + startService(new Intent(UbuntuInstallService.IS_UBUNTU_UPGRADABLE)); |
259 | } |
260 | - |
261 | + |
262 | @Override |
263 | public void onResume() { |
264 | super.onResume(); |
265 | @@ -62,7 +65,7 @@ |
266 | filter.addAction(UbuntuInstallService.VERSION_UPDATE); |
267 | registerReceiver(mServiceObserver, filter); |
268 | } |
269 | - |
270 | + |
271 | @Override |
272 | public void onPause() { |
273 | super.onPause(); |
274 | @@ -113,7 +116,19 @@ |
275 | } |
276 | return super.onOptionsItemSelected(item); |
277 | } |
278 | - |
279 | + |
280 | + /** |
281 | + * Create a dialog for confirmation. |
282 | + * @param title |
283 | + * @param positiveButton |
284 | + * @param negativeButton |
285 | + * @param action |
286 | + * @param toastText |
287 | + * @param choiceItemsArray |
288 | + * @param defaultChoiceValues |
289 | + * @param choiceClickListener |
290 | + * @return AlertDialog |
291 | + */ |
292 | private AlertDialog createConfirmationDialog(final int title, |
293 | final int positiveButton, |
294 | final int negativeButton, |
295 | @@ -151,7 +166,10 @@ |
296 | mTextVersionLabel.setText(R.string.label_version); |
297 | mTextDescriptionLabel.setText(R.string.label_description); |
298 | } |
299 | - |
300 | + |
301 | + /** |
302 | + * Get installed version to check Ubuntu is installed |
303 | + */ |
304 | private void ensureUbuntuIsInstalled() { |
305 | VersionInfo v = UbuntuInstallService.getInstalledVersion(this.getApplicationContext()); |
306 | if (v == null) { |
307 | @@ -177,6 +195,11 @@ |
308 | String action = intent.getAction(); |
309 | if (action.equals(UbuntuInstallService.VERSION_UPDATE)) { |
310 | ensureUbuntuIsInstalled(); |
311 | + if(UbuntuInstallService.isUpgradeable(getApplicationContext())) { |
312 | + Log.d(TAG, "Found upgradeable file, kill LaunchActivity"); |
313 | + InstallActivity.startFrom(context); |
314 | + finish(); |
315 | + } |
316 | } |
317 | } |
318 | }; |
319 | |
320 | === modified file 'src/com/canonical/ubuntu/installer/UbuntuInstallService.java' |
321 | --- src/com/canonical/ubuntu/installer/UbuntuInstallService.java 2013-12-24 09:14:25 +0000 |
322 | +++ src/com/canonical/ubuntu/installer/UbuntuInstallService.java 2013-12-26 07:18:46 +0000 |
323 | @@ -9,6 +9,7 @@ |
324 | import java.net.MalformedURLException; |
325 | import java.net.URL; |
326 | import java.net.URLConnection; |
327 | +import java.util.ArrayList; |
328 | import java.util.Collections; |
329 | import java.util.HashMap; |
330 | import java.util.Iterator; |
331 | @@ -90,6 +91,7 @@ |
332 | public static final String INSTALL_UBUNTU = "com.canonical.ubuntuinstaller.UbuntuInstallService.INSTALL_UBUNTU"; |
333 | public static final String CANCEL_INSTALL = "com.canonical.ubuntuinstaller.UbuntuInstallService.CANCEL_INSTALL"; |
334 | public static final String UNINSTALL_UBUNTU = "com.canonical.ubuntuinstaller.UbuntuInstallService.UINSTALL_UBUNTU"; |
335 | + public static final String IS_UBUNTU_UPGRADABLE = "com.canonical.ubuntuinstaller.UbuntuInstallService.IS_UBUNTU_UPGRADABLE"; |
336 | public static final String UNINSTALL_UBUNTU_EXTRA_REMOVE_USER_DATA = "user_data"; |
337 | public static final String CHECK_FOR_UPDATE = "com.canonical.ubuntuinstaller.UbuntuInstallService.CHECK_FOR_UPDATE"; |
338 | public static final String DELETE_UBUNTU_USER_DATA = "com.canonical.ubuntuinstaller.UbuntuInstallService.DELETE_USER_DATA"; |
339 | @@ -151,6 +153,7 @@ |
340 | private static final String TAR = "u_tar"; |
341 | private static final String ANDROID_LOOP_MOUNT = "aloopmount"; |
342 | private static final String ANDROID_BOOTMGR = "bootmgr"; |
343 | + private static final String UPGRADECHECKER = "upgrade-checker"; |
344 | private static final String UPDATE_SCRIPT = "system-image-upgrader"; |
345 | private static final String ARCHIVE_MASTER = "archive-master.tar.xz"; |
346 | private static final String ARCHIVE_MASTER_ASC = "archive-master.tar.xz.asc"; |
347 | @@ -179,6 +182,7 @@ |
348 | private static final int PROGRESS_MKSWAP_ADJUSTMENT = 17; // equivalent of time tar --checkpoint=200 |
349 | private PowerManager mPowerManager; |
350 | private PowerManager.WakeLock mWakeLock; |
351 | + // FIXME make workPath in Cache a private function |
352 | private boolean workPathInCache = false; |
353 | private String mRootOfWorkPath; |
354 | private boolean mIsCanceled; |
355 | @@ -210,7 +214,16 @@ |
356 | } |
357 | |
358 | public ESumNotMatchException(String string) { |
359 | - // TODO Auto-generated constructor stub |
360 | + super(string); |
361 | + } |
362 | + }; |
363 | + |
364 | + class EShellExecException extends Exception { |
365 | + public EShellExecException(){ |
366 | + super(); |
367 | + } |
368 | + |
369 | + public EShellExecException(String string) { |
370 | super(string); |
371 | } |
372 | }; |
373 | @@ -218,7 +231,7 @@ |
374 | public UbuntuInstallService() { |
375 | super(TAG); |
376 | } |
377 | - |
378 | + |
379 | @Override |
380 | public void onCreate() { |
381 | super.onCreate(); |
382 | @@ -231,7 +244,7 @@ |
383 | mRootOfWorkPath = "/cache"; |
384 | workPathInCache = true; |
385 | } else { |
386 | - mRootOfWorkPath = getFilesDir().toString(); // "/data/data/com.canonical.ubuntuinstaller/files"; |
387 | + mRootOfWorkPath = getFilesDir().toString(); // "/data/data/com.canonical.ubuntu.installer/files"; |
388 | workPathInCache = false; |
389 | } |
390 | mInstallerState = InstallerState.READY; |
391 | @@ -281,6 +294,12 @@ |
392 | } else if (action.equals(INSTALL_UBUNTU)) { |
393 | updateInstallerState(InstallerState.INSTALLING); |
394 | result = doInstallUbuntu(intent); |
395 | + } else if (action.equals(IS_UBUNTU_UPGRADABLE)) { |
396 | + // check if the upgradeable images available. |
397 | + if(findInstallCommand()) { |
398 | + Log.d(TAG, "There is a upgradeable file. send VERSION_UPDATE"); |
399 | + result = new Intent(VERSION_UPDATE); |
400 | + } |
401 | } else if (action.equals(CANCEL_INSTALL)) { |
402 | // install should be already cancelled, try to delete it now |
403 | updateInstallerState(InstallerState.UNINSTALLING); |
404 | @@ -309,7 +328,7 @@ |
405 | |
406 | private Intent doGetChannelList(Intent intent) { |
407 | Intent result = new Intent(AVAILABLE_CHANNELS); |
408 | - // |
409 | + |
410 | HashMap<String, String> channels= new HashMap<String, String>(); |
411 | boolean includeHidden = getSharedPreferences( SHARED_PREF, Context.MODE_PRIVATE).getBoolean(PREF_KEY_DEVELOPER, false); |
412 | String deviceModel = Build.DEVICE.toLowerCase(Locale.US); |
413 | @@ -365,149 +384,74 @@ |
414 | } |
415 | |
416 | private Intent doInstallUbuntu(Intent intent) { |
417 | - Log.w(TAG, "doInstallUbuntu"); |
418 | + Log.w(TAG, "doInstallUbuntu"); |
419 | Intent result = new Intent(INSTALL_RESULT); |
420 | + |
421 | // get update command file |
422 | - SharedPreferences pref = getSharedPreferences( SHARED_PREF, Context.MODE_PRIVATE); |
423 | - String updateCommand = pref.getString(PREF_KEY_UPDATE_COMMAND,""); |
424 | + String updateCommand = getUpdateCommand(); |
425 | + |
426 | + // 1. check if update command exist. |
427 | + // 2. However, if the udpate command is in "/cache", |
428 | + // the app can not access to /cache sometimes. |
429 | + if (updateCommand.equals("") || |
430 | + (!new File(updateCommand).exists() && |
431 | + !updateCommand.startsWith("/cache"))) { |
432 | + return handleInstallFail(result, -1, "Missing update command"); |
433 | + } |
434 | + |
435 | + SharedPreferences pref = getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE); |
436 | mTotalSize = pref.getInt(PREF_KEY_ESTIMATED_CHECKPOINTS, 0); |
437 | mLastSignalledProgress = 0; |
438 | - if (updateCommand.equals("") || ! new File(updateCommand).exists()) { |
439 | - return handleInstallFail(result, -1, "Missing update command"); |
440 | - } |
441 | - mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ubuntu-installing"); |
442 | - try { |
443 | - File rootFolder = new File(mRootOfWorkPath); |
444 | - File supportingFiles = new File(rootFolder, RELEASE_FOLDER); |
445 | - broadcastProgress(0, "Extracting supporting files"); |
446 | - try { |
447 | - Utils.extractExecutableAsset(this, BUSYBOX, supportingFiles.toString(), true); |
448 | - Utils.extractExecutableAsset(this, ANDROID_BOOTMGR, supportingFiles.toString(), true); |
449 | - Utils.extractExecutableAsset(this, GPG, supportingFiles.toString(), true); |
450 | - Utils.extractExecutableAsset(this, TAR, supportingFiles.toString(), true); |
451 | - Utils.extractExecutableAsset(this, UPDATE_SCRIPT, supportingFiles.toString(), true); |
452 | - Utils.extractExecutableAsset(this, ANDROID_LOOP_MOUNT, supportingFiles.toString(), true); |
453 | - Utils.extractExecutableAsset(this, ARCHIVE_MASTER, supportingFiles.toString(), false); |
454 | - Utils.extractExecutableAsset(this, ARCHIVE_MASTER_ASC, supportingFiles.toString(), false); |
455 | - Utils.extractExecutableAsset(this, U_REBOOT_APP, supportingFiles.toString(), false); |
456 | - Utils.extractExecutableAsset(this, U_REBOOT_APP_ASC, supportingFiles.toString(), false); |
457 | - } catch (IOException e) { |
458 | - e.printStackTrace(); |
459 | - return handleInstallFail(result, -1, "Failed to extract supporting assets"); |
460 | - } |
461 | - // get superuser and run update script |
462 | - broadcastProgress(-1, "Starting update script"); |
463 | - try { |
464 | - Process process = Runtime.getRuntime().exec("su", null, supportingFiles); |
465 | - DataOutputStream os = new DataOutputStream(process.getOutputStream()); |
466 | - // debug purpose. |
467 | - // os.writeBytes("set -x\n"); |
468 | - // make sure we are in work folder |
469 | - os.writeBytes(String.format("cd %s\n", supportingFiles.getAbsolutePath())); |
470 | - os.writeBytes("echo \"SU granted\"\n"); |
471 | - // run system-image-upgrader. |
472 | - os.writeBytes(String.format("sh %s %s %s\n", |
473 | - UPDATE_SCRIPT, |
474 | - updateCommand, |
475 | - getFilesDir().getAbsolutePath() |
476 | - )); |
477 | - os.writeBytes(String.format("cd %s\n", supportingFiles.getAbsolutePath())); |
478 | - // backup original recovery. |
479 | - if(!new File(getFilesDir().toString(), ANDROID_REOCVERY_IMG).exists()) { |
480 | - os.writeBytes(String.format("./%s -b %s %s/%s\n", |
481 | - ANDROID_BOOTMGR, |
482 | - Utils.getRecoveryPartitionPath(), |
483 | - getFilesDir().getAbsolutePath(), |
484 | - ANDROID_REOCVERY_IMG |
485 | - )); |
486 | - } |
487 | - os.writeBytes(String.format("cd %s\n", supportingFiles.getAbsolutePath())); |
488 | - // overwrite the recovery partition. |
489 | - os.writeBytes(String.format("./%s -b %s/%s %s\n", |
490 | + |
491 | + List<String> shellcmds = new ArrayList<String>(); |
492 | + { |
493 | + shellcmds.add("echo Installing\n"); |
494 | + // run system-image-upgrader. |
495 | + shellcmds.add(String.format("%s %s %s\n", |
496 | + UPDATE_SCRIPT, |
497 | + updateCommand, |
498 | + getFilesDir().getAbsolutePath() |
499 | + )); |
500 | + // Only backup original recovery, when there is no backuped file. |
501 | + if(!new File(getFilesDir().toString(), ANDROID_REOCVERY_IMG).exists()) { |
502 | + shellcmds.add(String.format("%s -b %s %s/%s\n", |
503 | ANDROID_BOOTMGR, |
504 | + Utils.getRecoveryPartitionPath(), |
505 | getFilesDir().getAbsolutePath(), |
506 | - UBUNTU_BOOT_IMG, |
507 | - Utils.getRecoveryPartitionPath() |
508 | + ANDROID_REOCVERY_IMG |
509 | )); |
510 | - |
511 | - // close terminal |
512 | - os.writeBytes("exit\n"); |
513 | - os.flush(); |
514 | - InputStream is = process.getInputStream(); |
515 | - InputStream es = process.getErrorStream(); |
516 | - int read = 0; |
517 | - byte[] buff = new byte[4096]; |
518 | - boolean running = true; |
519 | - boolean scriptExecuted = false; |
520 | - do { |
521 | - while( is.available() > 0) { |
522 | - read = is.read(buff); |
523 | - if ( read <= 0 ) { |
524 | - break; |
525 | - } |
526 | - scriptExecuted = true; |
527 | - String seg = new String(buff,0,read); |
528 | - Log.d(TAG, "Script Output: " + seg); |
529 | - broadcastProgress(-1, seg); |
530 | - } |
531 | - while( es.available() > 0) { |
532 | - read = es.read(buff); |
533 | - if ( read <= 0 ) { |
534 | - break; |
535 | - } |
536 | - scriptExecuted = true; |
537 | - String seg = new String(buff,0,read); |
538 | - if (seg.startsWith("SWAP-file-missing")) { |
539 | - // this is signal that we will also install swap, adjust progress estimates |
540 | - mTotalSize += PROGRESS_MKSWAP_ADJUSTMENT + PROGRESS_SWAP_CREATION_ADJUSTMENT; |
541 | - } else { |
542 | - mProgress++; |
543 | - if ( mLastSignalledProgress < (mProgress * 100 / mTotalSize)) { |
544 | - // update and signal new progress |
545 | - mLastSignalledProgress = (int) (mProgress * 100 / mTotalSize); |
546 | - broadcastProgress(mLastSignalledProgress, null); |
547 | - } |
548 | - } |
549 | - Log.d(TAG, "Stderr Output: " + seg); |
550 | - } |
551 | - try { |
552 | - int ret = process.exitValue(); |
553 | - Log.v(TAG, "Worker thread exited with: " + ret); |
554 | - // if script was not executed, then user did not granted SU permissions |
555 | - if (ret == 255 || !scriptExecuted ) { |
556 | - return handleInstallFail(result, -1, "Failed to get SU permissions"); |
557 | - } else if (ret != 0) { |
558 | - return handleInstallFail(result, -1, "Instalation failed"); |
559 | - } |
560 | - running =false; |
561 | - } catch (IllegalThreadStateException e) { |
562 | - // still running, wait a bit |
563 | - try { Thread.sleep(200); } catch(Exception ex) {} |
564 | - } |
565 | - } while (running); |
566 | - } catch (IOException e) { |
567 | - e.printStackTrace(); |
568 | - Log.w(TAG, "Update failed"); |
569 | - return handleInstallFail(result, -1, "Install failed"); |
570 | - } |
571 | - } finally { |
572 | - if (mWakeLock != null && mWakeLock.isHeld()) { |
573 | - mWakeLock.release(); |
574 | - } |
575 | - } |
576 | - SharedPreferences.Editor editor = pref.edit(); |
577 | - editor.putString(PREF_KEY_UPDATE_COMMAND, ""); |
578 | + } |
579 | + // overwrite the recovery partition. |
580 | + shellcmds.add(String.format("%s -b %s/%s %s\n", |
581 | + ANDROID_BOOTMGR, |
582 | + getFilesDir().getAbsolutePath(), |
583 | + UBUNTU_BOOT_IMG, |
584 | + Utils.getRecoveryPartitionPath() |
585 | + )); |
586 | + shellcmds.add("exit"); |
587 | + } |
588 | + |
589 | + broadcastProgress(-1, "Starting update script - " + updateCommand); |
590 | + try { |
591 | + int ret = executeSUCommands(shellcmds.toArray(new String[shellcmds.size()])); |
592 | + result.putExtra(INSTALL_RESULT_EXTRA_INT, ret); |
593 | + } catch (EShellExecException e) { |
594 | + return handleInstallFail(result, -1, e.getMessage()); |
595 | + } |
596 | + |
597 | + // we done. |
598 | + cleanUpdateCommand(); |
599 | VersionInfo v = new VersionInfo(pref, PREF_KEY_DOWNLOADED_VERSION); |
600 | - v.storeVersion(editor, PREF_KEY_INSTALLED_VERSION); |
601 | + v.storeVersion(pref.edit(), PREF_KEY_INSTALLED_VERSION); |
602 | mProgress = 100; |
603 | - result.putExtra(INSTALL_RESULT_EXTRA_INT, 0); |
604 | return result; |
605 | } |
606 | - |
607 | + |
608 | private Intent handleInstallFail(Intent i, int res, String failReason) { |
609 | i.putExtra(INSTALL_RESULT_EXTRA_INT, -1); |
610 | - i.putExtra(INSTALL_RESULT_EXTRA_STR, "Missing update command"); |
611 | - doUninstallUbuntu(i); |
612 | + i.putExtra(INSTALL_RESULT_EXTRA_STR, failReason); |
613 | + // we don't want to unstainll if we failed to install a update. |
614 | + // doUninstallUbuntu(i); |
615 | return i; |
616 | } |
617 | |
618 | @@ -516,9 +460,11 @@ |
619 | File updateCommand = new File(workingFolder, UPDATE_COMMAND); |
620 | Intent result = new Intent(VERSION_UPDATE); |
621 | boolean removeUserData = intent.getBooleanExtra(UNINSTALL_UBUNTU_EXTRA_REMOVE_USER_DATA, false); |
622 | + Log.d(TAG, "doUninstallUbuntu"); |
623 | |
624 | String format_cmd = null; |
625 | if (removeUserData) { |
626 | + Log.d(TAG, "removing user data."); |
627 | format_cmd = String.format("echo \"%s %s\n %s %s\" > %s\n", |
628 | COMMAND_FORMAT, PARTITION_DATA, |
629 | COMMAND_UMOUNT, PARTITION_SYSTEM, |
630 | @@ -532,41 +478,53 @@ |
631 | // 1. force unmount |
632 | // 2. restore android recovery partition, and deleted it. |
633 | // 3. delete system.img and SWAP.img. |
634 | - int r = executeSUCommands(result, "result", new String[]{ |
635 | - format_cmd, |
636 | - ("sh " + UPDATE_SCRIPT |
637 | - + " " + updateCommand.getAbsolutePath() |
638 | - + " " + getFilesDir().toString() + "\n"), |
639 | - String.format("./%s -b %s/%s %s\n", |
640 | - ANDROID_BOOTMGR, |
641 | - getFilesDir().toString(), |
642 | - ANDROID_REOCVERY_IMG, |
643 | - Utils.getRecoveryPartitionPath()), |
644 | - (String.format("rm -f %s/%s\n", getFilesDir().toString(), ANDROID_REOCVERY_IMG)), |
645 | - ("rm -rf /data/system.img\n"), |
646 | - ("rm -rf /data/SWAP.img\n"), |
647 | - } ); |
648 | - if (r == 0) { |
649 | - // delete installed version in preferences |
650 | - SharedPreferences pref = getSharedPreferences( SHARED_PREF, Context.MODE_PRIVATE); |
651 | - SharedPreferences.Editor editor = pref.edit(); |
652 | - editor.putString(PREF_KEY_UPDATE_COMMAND, ""); |
653 | - VersionInfo.storeEmptyVersion(editor, PREF_KEY_INSTALLED_VERSION); |
654 | - editor.commit(); |
655 | + try { |
656 | + int r = executeSUCommands( |
657 | + new String[]{ |
658 | + ("echo Uninstalling\n"), |
659 | + format_cmd, |
660 | + ("sh " + UPDATE_SCRIPT |
661 | + + " " + updateCommand.getAbsolutePath() |
662 | + + " " + getFilesDir().toString() + "\n"), |
663 | + String.format("%s -b %s/%s %s || true\n", |
664 | + ANDROID_BOOTMGR, |
665 | + getFilesDir().toString(), |
666 | + ANDROID_REOCVERY_IMG, |
667 | + Utils.getRecoveryPartitionPath()), |
668 | + (String.format("rm -f %s/%s || true\n", getFilesDir().toString(), ANDROID_REOCVERY_IMG)), |
669 | + ("rm -rf /data/system.img || true\n"), |
670 | + ("rm -rf /data/SWAP.img || true\n"), |
671 | + } ); |
672 | + |
673 | + if (r == 0) { |
674 | + // delete installed version in preferences |
675 | + cleanUpdateCommand(); |
676 | + VersionInfo.storeEmptyVersion( |
677 | + getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE).edit(), PREF_KEY_INSTALLED_VERSION); |
678 | + } |
679 | + result.putExtra("result", r); |
680 | + } catch (EShellExecException e) { |
681 | + result.putExtra("result", -1); |
682 | } |
683 | - result.putExtra("result", r); |
684 | + |
685 | return result; |
686 | } |
687 | |
688 | private Intent doDeleteUbuntuUserData(Intent intent) { |
689 | Intent result = new Intent(VERSION_UPDATE); |
690 | File workingFolder = new File(mRootOfWorkPath, TEMP_FOLDER); |
691 | - File updateCommand = new File(workingFolder, UPDATE_COMMAND); |
692 | - int r = executeSUCommands(result, "fail_description", new String[]{ |
693 | + File updateCommand = new File(workingFolder, UPDATE_COMMAND); |
694 | + |
695 | + try { |
696 | + int r = executeSUCommands(new String[]{ |
697 | String.format("echo \"%s %s\" > %s\n", COMMAND_FORMAT, PARTITION_DATA, UPDATE_COMMAND), |
698 | ("sh " + UPDATE_SCRIPT + " " + updateCommand.getAbsolutePath() + " " + getFilesDir().toString() + "\n") |
699 | - } ); |
700 | - result.putExtra("result", r); |
701 | + } ); |
702 | + result.putExtra("result", r); |
703 | + } catch (EShellExecException e) { |
704 | + result.putExtra("fail_description", e.getMessage()); |
705 | + result.putExtra("result", -1); |
706 | + } |
707 | return result; |
708 | } |
709 | |
710 | @@ -579,75 +537,83 @@ |
711 | // FIXME: in Android 4.4, we do not get power manager permission. |
712 | // try it with SU permissions |
713 | try { |
714 | - Process process = Runtime.getRuntime().exec("su", null, getFilesDir()); |
715 | - DataOutputStream os = new DataOutputStream(process.getOutputStream()); |
716 | - |
717 | - Utils.extractExecutableAsset(this, ANDROID_BOOTMGR, getFilesDir().toString(), true); |
718 | - // overwrite the recovery partition. |
719 | - os.writeBytes(String.format("%s/%s -b %s/%s %s\n", |
720 | - getFilesDir().getAbsolutePath(), |
721 | - ANDROID_BOOTMGR, |
722 | - getFilesDir().getAbsolutePath(), |
723 | - UBUNTU_BOOT_IMG, |
724 | - Utils.getRecoveryPartitionPath() |
725 | - )); |
726 | - os.writeBytes("reboot recovery\n"); |
727 | - os.flush(); |
728 | - try { |
729 | - process.waitFor(); |
730 | - if (process.exitValue() != 255) { |
731 | - Utils.showToast(this.getApplicationContext(), "Rebooting to Ubuntu"); |
732 | - } else { |
733 | - Utils.showToast(this.getApplicationContext(), "No permissions to reboot to recovery"); |
734 | - } |
735 | - } catch (InterruptedException ee) { |
736 | - Utils.showToast(this.getApplicationContext(), "No permissions to reboot to recovery"); |
737 | + int r = executeSUCommands(new String[] { |
738 | + String.format("%s -b %s/%s %s || true\n", |
739 | + ANDROID_BOOTMGR, |
740 | + getFilesDir().getAbsolutePath(), |
741 | + UBUNTU_BOOT_IMG, |
742 | + Utils.getRecoveryPartitionPath()), |
743 | + "reboot recovery\n" |
744 | + }); |
745 | + if(r != 255) { |
746 | + Utils.showToast(this.getApplicationContext(), "Rebooting to Ubuntu"); |
747 | + } else { |
748 | + Utils.showToast(this.getApplicationContext(), "No permissions to reboot to recovery"); |
749 | } |
750 | - } catch (IOException eee) { |
751 | - Utils.showToast(this.getApplicationContext(), "No permissions to reboot to recovery"); |
752 | + } catch (EShellExecException e1) { |
753 | + Utils.showToast(this.getApplicationContext(), "No permissions to reboot to recovery"); |
754 | } |
755 | } |
756 | } |
757 | |
758 | /** |
759 | - * |
760 | - * @param result intent to update with result text |
761 | - * @param resultExtraText |
762 | - * @param commands commands to execute |
763 | - * @return 0 for success and -1 for fail |
764 | + * This command exec commands in the working folder with supporting utils. |
765 | + * |
766 | + * @param commands commands to be executed |
767 | + * @return shell script exit value. |
768 | + * @throws EShellExecException |
769 | */ |
770 | - private int executeSUCommands(Intent result, String resultExtraText, String[] commands) { |
771 | + private int executeSUCommands(String[] commands) throws EShellExecException { |
772 | + int ret = 0; |
773 | File rootFolder = new File(mRootOfWorkPath); |
774 | File workingFolder = new File(rootFolder, TEMP_FOLDER); |
775 | + String workingFolderPath = workingFolder.getAbsolutePath(); |
776 | + |
777 | if (!workingFolder.exists() && !workingFolder.mkdir()) { |
778 | - result.putExtra(resultExtraText, "Failed to create working folder"); |
779 | - return -1; |
780 | + throw(new EShellExecException("Failed to create working folder")); |
781 | } |
782 | + |
783 | + broadcastProgress(0, "Extracting supporting files at " + workingFolder.getAbsolutePath()); |
784 | try { |
785 | - Utils.extractExecutableAsset(this, UPDATE_SCRIPT, workingFolder.toString(), true); |
786 | - Utils.extractExecutableAsset(this, ANDROID_LOOP_MOUNT, workingFolder.toString(), true); |
787 | - Utils.extractExecutableAsset(this, ANDROID_BOOTMGR, workingFolder.toString(), true); |
788 | + // extract utils into working folder. |
789 | + Utils.extractExecutableAsset(this, ANDROID_BOOTMGR, workingFolderPath, true); |
790 | + Utils.extractExecutableAsset(this, ANDROID_LOOP_MOUNT, workingFolderPath, true); |
791 | + Utils.extractExecutableAsset(this, ARCHIVE_MASTER_ASC, workingFolderPath, false); |
792 | + Utils.extractExecutableAsset(this, ARCHIVE_MASTER, workingFolderPath, false); |
793 | + Utils.extractExecutableAsset(this, BUSYBOX, workingFolderPath, true); |
794 | + Utils.extractExecutableAsset(this, GPG, workingFolderPath, true); |
795 | + Utils.extractExecutableAsset(this, TAR, workingFolderPath, true); |
796 | + Utils.extractExecutableAsset(this, UPDATE_SCRIPT, workingFolderPath, true); |
797 | + Utils.extractExecutableAsset(this, U_REBOOT_APP_ASC, workingFolderPath, false); |
798 | + Utils.extractExecutableAsset(this, U_REBOOT_APP, workingFolderPath, false); |
799 | + Utils.extractExecutableAsset(this, UPGRADECHECKER, workingFolderPath, true); |
800 | } catch (IOException e) { |
801 | - e.printStackTrace(); |
802 | - result.putExtra(resultExtraText, "Failed to extract supporting files"); |
803 | - return -1; |
804 | + throw(new EShellExecException("Failed to extract supporting utils")); |
805 | } |
806 | |
807 | - mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ubuntu-installing"); |
808 | + mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "shelling"); |
809 | try { |
810 | Process process = Runtime.getRuntime().exec("su", null, workingFolder); |
811 | DataOutputStream os = new DataOutputStream(process.getOutputStream()); |
812 | + // debug purpose. |
813 | + os.writeBytes("set -x\n"); |
814 | + |
815 | + os.writeBytes("echo \"SU granted\"\n"); |
816 | // make sure we are in work folder |
817 | - os.writeBytes("echo \"SU granted\"\n"); |
818 | - for (String c : commands) { |
819 | - Log.v(TAG, "Executing:" + c); |
820 | - // make sure we are always at working folder before running next command |
821 | - os.writeBytes(String.format("cd %s\n", workingFolder.getAbsolutePath())); |
822 | - os.writeBytes(c); |
823 | + os.writeBytes(String.format("cd %s\n", workingFolder.getAbsolutePath())); |
824 | + // setup search path for commands |
825 | + os.writeBytes(String.format("export PATH=%s:$PATH\n", workingFolder.getAbsolutePath())); |
826 | + |
827 | + for (String cmd : commands) { |
828 | + Log.d(TAG, "Executing: " + cmd + "\n"); |
829 | + os.writeBytes(cmd + "\n"); |
830 | + os.writeBytes("CMDSTATES=$?\n"); |
831 | } |
832 | + // clean up supporting utils. |
833 | os.writeBytes(String.format("rm -rf %s\n", workingFolder.getAbsolutePath())); |
834 | - os.writeBytes("exit\n"); |
835 | + os.writeBytes("exit $CMDSTATES\n"); |
836 | os.flush(); |
837 | + |
838 | int read = 0; |
839 | byte[] buff = new byte[4096]; |
840 | InputStream is = process.getInputStream(); |
841 | @@ -661,7 +627,7 @@ |
842 | break; |
843 | } |
844 | scriptExecuted = true; |
845 | - String seg = new String(buff,0,read); |
846 | + String seg = new String(buff, 0, read); |
847 | Log.i(TAG, "Script Output: " + seg); |
848 | broadcastProgress(-1, seg); |
849 | } |
850 | @@ -672,37 +638,44 @@ |
851 | } |
852 | scriptExecuted = true; |
853 | String seg = new String(buff,0,read); |
854 | + |
855 | + if (seg.startsWith("SWAP-file-missing")) { |
856 | + // this is signal that we will also install swap, adjust progress estimates |
857 | + mTotalSize += PROGRESS_MKSWAP_ADJUSTMENT + PROGRESS_SWAP_CREATION_ADJUSTMENT; |
858 | + } else { |
859 | + mProgress++; |
860 | + if (mTotalSize > 0 && mLastSignalledProgress < (mProgress * 100 / mTotalSize)) { |
861 | + // update and signal new progress |
862 | + mLastSignalledProgress = (int) (mProgress * 100 / mTotalSize); |
863 | + broadcastProgress(mLastSignalledProgress, null); |
864 | + } |
865 | + } |
866 | + |
867 | Log.i(TAG, "Stderr Output: " + seg); |
868 | } |
869 | try { |
870 | - int ret = process.exitValue(); |
871 | - Log.v(TAG, "Worker thread exited with: " + ret); |
872 | + ret = process.exitValue(); |
873 | + Log.d(TAG, "Worker thread exited with: " + ret); |
874 | // if script was not executed, then user did not granted SU permissions |
875 | - if (ret == 255 || !scriptExecuted ) { |
876 | - result.putExtra(resultExtraText, "Failed to get SU permissions"); |
877 | - return -1; |
878 | - } else if (ret != 0) { |
879 | - result.putExtra(resultExtraText, "Script failed"); |
880 | - return -1; |
881 | + if (!scriptExecuted) { |
882 | + throw new EShellExecException("Failed to get SU permissions"); |
883 | } |
884 | - running =false; |
885 | + running = false; |
886 | } catch (IllegalThreadStateException e) { |
887 | // still running, wait a bit |
888 | try { Thread.sleep(200); } catch(Exception ex) {} |
889 | } |
890 | } while (running); |
891 | - }catch (IOException e) { |
892 | - e.printStackTrace(); |
893 | - result.putExtra(resultExtraText, "Script execution exception"); |
894 | - return -1; |
895 | + } catch (IOException e) { |
896 | + throw(new EShellExecException("Script execution exception " + e.getMessage())); |
897 | } finally { |
898 | if (mWakeLock != null && mWakeLock.isHeld()) { |
899 | mWakeLock.release(); |
900 | } |
901 | } |
902 | - return 0; |
903 | + return ret; |
904 | } |
905 | - |
906 | + |
907 | private Intent doDownloadRelease(Intent intent) { |
908 | mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ufa-downloading"); |
909 | mIsCanceled = false; |
910 | @@ -780,7 +753,7 @@ |
911 | } |
912 | |
913 | // make sure release folder exists |
914 | - File release = new File(rootFolder,RELEASE_FOLDER); |
915 | + File release = new File(rootFolder, RELEASE_FOLDER); |
916 | release.mkdir(); |
917 | // download release |
918 | long time = System.currentTimeMillis(); |
919 | @@ -970,8 +943,11 @@ |
920 | } |
921 | } |
922 | // store update command |
923 | + setUpdateCommand(updateCommand.getAbsolutePath()); |
924 | + |
925 | + // updated downloaded information. |
926 | VersionInfo v = new VersionInfo(alias, jsonUrl, choosenRelease.description, choosenRelease.version, 0, releaseType); |
927 | - editor.putString(PREF_KEY_UPDATE_COMMAND, updateCommand.getAbsolutePath()); |
928 | + |
929 | editor.putInt(PREF_KEY_ESTIMATED_CHECKPOINTS, estimatedCheckCount); |
930 | v.storeVersion(editor, PREF_KEY_DOWNLOADED_VERSION); |
931 | mProgress = 100; |
932 | @@ -1071,40 +1047,38 @@ |
933 | return fileName; |
934 | } |
935 | |
936 | + |
937 | + private static boolean deleteDirectory(File path) { |
938 | + if( path.exists() ) { |
939 | + File[] files = path.listFiles(); |
940 | + for(int i=0; i<files.length; i++) { |
941 | + if(files[i].isDirectory()) { |
942 | + deleteDirectory(files[i]); |
943 | + } else { |
944 | + files[i].delete(); |
945 | + } |
946 | + } |
947 | + } |
948 | + return( path.delete() ); |
949 | + } |
950 | + |
951 | /** |
952 | - * |
953 | * @return null if success or error |
954 | */ |
955 | private String deleteRelease() { |
956 | // First delete old release if it exists |
957 | File rootFolder = new File(mRootOfWorkPath); |
958 | - File release = new File(rootFolder,RELEASE_FOLDER); |
959 | + File release = new File(rootFolder, RELEASE_FOLDER); |
960 | if (release.exists()) { |
961 | - try { |
962 | - String command = "rm -rf " + release.getAbsolutePath(); |
963 | - Process p = Runtime.getRuntime().exec(command , null, rootFolder); |
964 | - try { |
965 | - p.waitFor(); |
966 | - int r = p.exitValue(); |
967 | - if (r == 255) { |
968 | - return "failed to remove old download"; |
969 | - } |
970 | - } catch (InterruptedException e) { |
971 | - } |
972 | - }catch (IOException e) { |
973 | - e.printStackTrace(); |
974 | - Log.w(TAG, "failed to remove old download"); |
975 | - return "failed to remove old download"; |
976 | - } |
977 | - SharedPreferences pref = getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE); |
978 | - SharedPreferences.Editor editor = pref.edit(); |
979 | - editor.putString(PREF_KEY_UPDATE_COMMAND, ""); |
980 | - VersionInfo.storeEmptyVersion(editor, PREF_KEY_DOWNLOADED_VERSION); |
981 | - editor.commit(); |
982 | + deleteDirectory(release); |
983 | + // cleanup update command |
984 | + cleanUpdateCommand(); |
985 | + // clean up version number. |
986 | + VersionInfo.storeEmptyVersion(getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE).edit(), PREF_KEY_DOWNLOADED_VERSION); |
987 | } |
988 | return null; |
989 | } |
990 | - |
991 | + |
992 | private void broadcastInstallerState() { |
993 | Intent i = new Intent(SERVICE_STATE); |
994 | i.putExtra(SERVICE_STATE, mInstallerState.ordinal()); |
995 | @@ -1117,16 +1091,7 @@ |
996 | i.putExtra(SERVICE_STATE, mInstallerState.ordinal()); |
997 | sendBroadcast(i); |
998 | } |
999 | - |
1000 | - private void broadcastProgress(int val, String progress) { |
1001 | - Intent i = new Intent(PROGRESS); |
1002 | - i.putExtra(PROGRESS_EXTRA_INT, val); |
1003 | - if (progress!= null) { |
1004 | - i.putExtra(PROGRESS_EXTRA_TEXT, progress); |
1005 | - } |
1006 | - sendBroadcast(i); |
1007 | - } |
1008 | - |
1009 | + |
1010 | /** |
1011 | * Check whether storage free space is enough. |
1012 | * @param downloadSize: download size from json. 0 means file already downloaded. |
1013 | @@ -1161,6 +1126,7 @@ |
1014 | } |
1015 | return null; |
1016 | } |
1017 | + |
1018 | /** |
1019 | * Internal helper function to get current DOWNLOAD_VERSION even download is partial |
1020 | * @param context |
1021 | @@ -1186,6 +1152,7 @@ |
1022 | public static VersionInfo getInstalledVersion(Context c) { |
1023 | return getVersionWithPrefKey(c, PREF_KEY_INSTALLED_VERSION); |
1024 | } |
1025 | + |
1026 | public static boolean isUbuntuInstalled(Context c) { |
1027 | SharedPreferences pref = c.getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE); |
1028 | if (VersionInfo.hasValidVersion(pref, PREF_KEY_INSTALLED_VERSION)) { |
1029 | @@ -1194,9 +1161,111 @@ |
1030 | } |
1031 | return false; |
1032 | } |
1033 | + |
1034 | + /** |
1035 | + * check if update_command available for upgrade. |
1036 | + * |
1037 | + * @param c |
1038 | + * @return |
1039 | + */ |
1040 | + public static boolean isUpgradeable(Context c) { |
1041 | + String cmd = c.getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE).getString(PREF_KEY_UPDATE_COMMAND, ""); |
1042 | + return (cmd.startsWith("/cache/")); |
1043 | + } |
1044 | + |
1045 | + /** |
1046 | + * check if recovery command is exist on the system. |
1047 | + * system-image in Ubuntu Touch downloaded new version of image at /cache/recovery. |
1048 | + * This function check if there is a ubuntu_command file in /cache/recovery. |
1049 | + * |
1050 | + * The ubuntu_command will be renamed or removed after installation. |
1051 | + * |
1052 | + * @return if there is upgradeable images stored in /cache. |
1053 | + */ |
1054 | + public boolean findInstallCommand () { |
1055 | + String[] candidates = { |
1056 | + "/cache/recovery/ubuntu_command", |
1057 | + "/cache/ubunturecovery/ubuntu_command", |
1058 | + }; |
1059 | + boolean ret = false; |
1060 | + for(String command: candidates) { |
1061 | + if(new File("/cache").canRead()) { |
1062 | + // if we have permission, we can read /cache. |
1063 | + File cmd = new File(command); |
1064 | + if(cmd.exists() && cmd.isFile()) { |
1065 | + Log.d(TAG, "Found upgrade command - " + cmd.getAbsoluteFile().toString()); |
1066 | + // find the upgradeable file, stored into pref. |
1067 | + setUpdateCommand(cmd.getAbsolutePath()); |
1068 | + ret = true; |
1069 | + } |
1070 | + } else { |
1071 | + // check the file with su |
1072 | + File workingFolder = new File(mRootOfWorkPath + "/" + TEMP_FOLDER); |
1073 | + if (!workingFolder.exists() && !workingFolder.mkdir()) { |
1074 | + Log.e(TAG, "can not create working folder"); |
1075 | + ret = false; |
1076 | + } |
1077 | + try { |
1078 | + int r = executeSUCommands(new String[] { |
1079 | + String.format("%s %s\n", UPGRADECHECKER, command), |
1080 | + }); |
1081 | + if(r == 1) { |
1082 | + Log.d(TAG, "Found upgradeable file - " + command); |
1083 | + setUpdateCommand(command); |
1084 | + ret = true; |
1085 | + } |
1086 | + } catch (EShellExecException e) { |
1087 | + ret = false; |
1088 | + } |
1089 | + } |
1090 | + } |
1091 | + if(ret) { |
1092 | + // FIXME we don't know what's the version in /cache. |
1093 | + SharedPreferences pref = getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE); |
1094 | + VersionInfo v = new VersionInfo(pref, PREF_KEY_INSTALLED_VERSION); |
1095 | + v.storeVersion(pref.edit(), PREF_KEY_DOWNLOADED_VERSION); |
1096 | + } |
1097 | + return ret; |
1098 | + } |
1099 | + |
1100 | + /** |
1101 | + * set update command string stored in shared preferences. |
1102 | + * @file absolute file path of Ubuntu Command file. |
1103 | + */ |
1104 | + private String getUpdateCommand(){ |
1105 | + return getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE).getString(PREF_KEY_UPDATE_COMMAND, ""); |
1106 | + } |
1107 | + |
1108 | + /** |
1109 | + * set update command string stored in shared preferences. |
1110 | + * @file absolute file path of Ubuntu Command file. |
1111 | + */ |
1112 | + private void setUpdateCommand(String file){ |
1113 | + getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE). |
1114 | + edit(). |
1115 | + putString(PREF_KEY_UPDATE_COMMAND, file). |
1116 | + commit(); |
1117 | + } |
1118 | + |
1119 | + /** |
1120 | + * clean update command string stored in shared preferences. |
1121 | + */ |
1122 | + private void cleanUpdateCommand() { |
1123 | + this.setUpdateCommand(""); |
1124 | + } |
1125 | |
1126 | + private void broadcastProgress(int val, String progress) { |
1127 | + Intent i = new Intent(PROGRESS); |
1128 | + i.putExtra(PROGRESS_EXTRA_INT, val); |
1129 | + if (progress!= null) { |
1130 | + i.putExtra(PROGRESS_EXTRA_TEXT, progress); |
1131 | + } |
1132 | + sendBroadcast(i); |
1133 | + } |
1134 | + |
1135 | /** |
1136 | - * Check if there is downloaded release ready to install |
1137 | + * Check if there is downloaded release ready to install. |
1138 | + * If command file is not exist, reset downloaded version. |
1139 | * @param context |
1140 | * @return true if there is downloaded release ready to install |
1141 | */ |
1142 | @@ -1207,9 +1276,10 @@ |
1143 | if (versionInfo.getDownloadedSize() != 0) return false; |
1144 | |
1145 | String command = pref.getString(PREF_KEY_UPDATE_COMMAND, ""); |
1146 | + Log.d(TAG, "checkifReadyToInstall"); |
1147 | if (!command.equals("")) { |
1148 | - File f = new File(command); |
1149 | - if (f.exists()) { |
1150 | + if (new File(command).exists() || command.startsWith("/cache")) { |
1151 | + Log.d(TAG, "checkifReadyToInstall - found command file " + command); |
1152 | return true; |
1153 | } else { |
1154 | pref.edit().putString(PREF_KEY_UPDATE_COMMAND, "").commit(); |
1155 | |
1156 | === modified file 'src/com/canonical/ubuntu/installer/VersionInfo.java' |
1157 | --- src/com/canonical/ubuntu/installer/VersionInfo.java 2013-12-24 03:50:41 +0000 |
1158 | +++ src/com/canonical/ubuntu/installer/VersionInfo.java 2013-12-26 07:18:46 +0000 |
1159 | @@ -5,7 +5,6 @@ |
1160 | |
1161 | /** |
1162 | * Version holder |
1163 | - * |
1164 | */ |
1165 | public class VersionInfo { |
1166 | |
1167 | @@ -109,9 +108,6 @@ |
1168 | } |
1169 | |
1170 | public static boolean hasValidVersion(SharedPreferences sp, String set) { |
1171 | - int v = sp.getInt(set + VERSION, -1); |
1172 | - String s1 = sp.getString(set + ALIAS, ""); |
1173 | - String s2 = sp.getString(set + JSON, ""); |
1174 | return (-1 != sp.getInt(set + VERSION, -1)); |
1175 | } |
1176 |
line 35: why not move BUSYBOX=busybox up and
cd `${BUSYBOX} dirname $1`
line 104: what's "user's data" did you mean ?
user data that android create, user data that ubuntu user create or ?
line 335: will it better to name something like IS_UBUNTU_ UPGRADABLE
instead of UPGRADE_UBUNTU
line 425: can you add comment on the logic ? like
As working dir in /data, blalal
As working dir in /cache, blalal
line 1140: please also add comment like line 425. (or refer to it)
line 1082, "getApplication()." can be removed.
line 1034: UbuntuInstallSe rvice.isUpgrade able
check command file against /cache works in this case, but it
does not necessary means upgradable (it might means some
error on internal status keeping) since normal install
could have command file in /cache if the apk is installed
in ROM.
should we rename this function to something else ?
Maybe we should stop adding new code and try to document the state machine.
Maybe more re-factor then.