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