blob: c5a7f9556201d9b5f62134680974d8cd8ee901df [file] [log] [blame]
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -07001/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.example.android.systemupdatersample.ui;
18
19import android.app.Activity;
20import android.app.AlertDialog;
Zhomart Mukhamejanov238beb72018-05-09 16:25:40 -070021import android.graphics.Color;
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -070022import android.os.Build;
23import android.os.Bundle;
24import android.os.UpdateEngine;
25import android.os.UpdateEngineCallback;
26import android.util.Log;
27import android.view.View;
28import android.widget.ArrayAdapter;
29import android.widget.Button;
30import android.widget.ProgressBar;
31import android.widget.Spinner;
32import android.widget.TextView;
33import android.widget.Toast;
34
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -070035import com.example.android.systemupdatersample.PayloadSpec;
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -070036import com.example.android.systemupdatersample.R;
37import com.example.android.systemupdatersample.UpdateConfig;
Zhomart Mukhamejanov0dd5a832018-04-23 11:38:54 -070038import com.example.android.systemupdatersample.services.PrepareStreamingService;
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -070039import com.example.android.systemupdatersample.util.PayloadSpecs;
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -070040import com.example.android.systemupdatersample.util.UpdateConfigs;
41import com.example.android.systemupdatersample.util.UpdateEngineErrorCodes;
Zhomart Mukhamejanov238beb72018-05-09 16:25:40 -070042import com.example.android.systemupdatersample.util.UpdateEngineProperties;
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -070043import com.example.android.systemupdatersample.util.UpdateEngineStatuses;
44
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -070045import java.io.IOException;
Zhomart Mukhamejanov6aa5fb02018-05-09 14:28:49 -070046import java.util.ArrayList;
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -070047import java.util.List;
Zhomart Mukhamejanov238beb72018-05-09 16:25:40 -070048import java.util.concurrent.atomic.AtomicBoolean;
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -070049import java.util.concurrent.atomic.AtomicInteger;
50
51/**
52 * UI for SystemUpdaterSample app.
53 */
54public class MainActivity extends Activity {
55
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -070056 private static final String TAG = "MainActivity";
57
Zhomart Mukhamejanov6aa5fb02018-05-09 14:28:49 -070058 /** HTTP Header: User-Agent; it will be sent to the server when streaming the payload. */
59 private static final String HTTP_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
60 + "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36";
61
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -070062 private TextView mTextViewBuild;
63 private Spinner mSpinnerConfigs;
64 private TextView mTextViewConfigsDirHint;
65 private Button mButtonReload;
66 private Button mButtonApplyConfig;
67 private Button mButtonStop;
68 private Button mButtonReset;
69 private ProgressBar mProgressBar;
70 private TextView mTextViewStatus;
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -070071 private TextView mTextViewCompletion;
Zhomart Mukhamejanov238beb72018-05-09 16:25:40 -070072 private TextView mTextViewUpdateInfo;
73 private Button mButtonSwitchSlot;
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -070074
75 private List<UpdateConfig> mConfigs;
76 private AtomicInteger mUpdateEngineStatus =
77 new AtomicInteger(UpdateEngine.UpdateStatusConstants.IDLE);
Zhomart Mukhamejanov238beb72018-05-09 16:25:40 -070078 private PayloadSpec mLastPayloadSpec;
79 private AtomicBoolean mManualSwitchSlotRequired = new AtomicBoolean(true);
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -070080
81 /**
82 * Listen to {@code update_engine} events.
83 */
84 private UpdateEngineCallbackImpl mUpdateEngineCallback = new UpdateEngineCallbackImpl();
85
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -070086 private final UpdateEngine mUpdateEngine = new UpdateEngine();
87
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -070088 @Override
89 protected void onCreate(Bundle savedInstanceState) {
90 super.onCreate(savedInstanceState);
91 setContentView(R.layout.activity_main);
92
93 this.mTextViewBuild = findViewById(R.id.textViewBuild);
94 this.mSpinnerConfigs = findViewById(R.id.spinnerConfigs);
95 this.mTextViewConfigsDirHint = findViewById(R.id.textViewConfigsDirHint);
96 this.mButtonReload = findViewById(R.id.buttonReload);
97 this.mButtonApplyConfig = findViewById(R.id.buttonApplyConfig);
98 this.mButtonStop = findViewById(R.id.buttonStop);
99 this.mButtonReset = findViewById(R.id.buttonReset);
100 this.mProgressBar = findViewById(R.id.progressBar);
101 this.mTextViewStatus = findViewById(R.id.textViewStatus);
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700102 this.mTextViewCompletion = findViewById(R.id.textViewCompletion);
Zhomart Mukhamejanov238beb72018-05-09 16:25:40 -0700103 this.mTextViewUpdateInfo = findViewById(R.id.textViewUpdateInfo);
104 this.mButtonSwitchSlot = findViewById(R.id.buttonSwitchSlot);
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700105
106 this.mTextViewConfigsDirHint.setText(UpdateConfigs.getConfigsRoot(this));
107
108 uiReset();
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700109 loadUpdateConfigs();
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700110
111 this.mUpdateEngine.bind(mUpdateEngineCallback);
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700112 }
113
114 @Override
115 protected void onDestroy() {
116 this.mUpdateEngine.unbind();
117 super.onDestroy();
118 }
119
120 /**
121 * reload button is clicked
122 */
123 public void onReloadClick(View view) {
124 loadUpdateConfigs();
125 }
126
127 /**
128 * view config button is clicked
129 */
130 public void onViewConfigClick(View view) {
131 UpdateConfig config = mConfigs.get(mSpinnerConfigs.getSelectedItemPosition());
132 new AlertDialog.Builder(this)
133 .setTitle(config.getName())
134 .setMessage(config.getRawJson())
135 .setPositiveButton(R.string.close, (dialog, id) -> dialog.dismiss())
136 .show();
137 }
138
139 /**
140 * apply config button is clicked
141 */
142 public void onApplyConfigClick(View view) {
143 new AlertDialog.Builder(this)
144 .setTitle("Apply Update")
145 .setMessage("Do you really want to apply this update?")
146 .setIcon(android.R.drawable.ic_dialog_alert)
147 .setPositiveButton(android.R.string.ok, (dialog, whichButton) -> {
148 uiSetUpdating();
149 applyUpdate(getSelectedConfig());
150 })
151 .setNegativeButton(android.R.string.cancel, null)
152 .show();
153 }
154
155 /**
156 * stop button clicked
157 */
158 public void onStopClick(View view) {
159 new AlertDialog.Builder(this)
160 .setTitle("Stop Update")
161 .setMessage("Do you really want to cancel running update?")
162 .setIcon(android.R.drawable.ic_dialog_alert)
163 .setPositiveButton(android.R.string.ok, (dialog, whichButton) -> {
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700164 stopRunningUpdate();
165 })
166 .setNegativeButton(android.R.string.cancel, null).show();
167 }
168
169 /**
170 * reset button clicked
171 */
172 public void onResetClick(View view) {
173 new AlertDialog.Builder(this)
174 .setTitle("Reset Update")
175 .setMessage("Do you really want to cancel running update"
176 + " and restore old version?")
177 .setIcon(android.R.drawable.ic_dialog_alert)
178 .setPositiveButton(android.R.string.ok, (dialog, whichButton) -> {
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700179 resetUpdate();
180 })
181 .setNegativeButton(android.R.string.cancel, null).show();
182 }
183
184 /**
Zhomart Mukhamejanov238beb72018-05-09 16:25:40 -0700185 * switch slot button clicked
186 */
187 public void onSwitchSlotClick(View view) {
188 setSwitchSlotOnReboot();
189 }
190
191 /**
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700192 * Invoked when anything changes. The value of {@code status} will
193 * be one of the values from {@link UpdateEngine.UpdateStatusConstants},
194 * and {@code percent} will be from {@code 0.0} to {@code 1.0}.
195 */
196 private void onStatusUpdate(int status, float percent) {
197 mProgressBar.setProgress((int) (100 * percent));
198 if (mUpdateEngineStatus.get() != status) {
199 mUpdateEngineStatus.set(status);
200 runOnUiThread(() -> {
201 Log.e("UpdateEngine", "StatusUpdate - status="
202 + UpdateEngineStatuses.getStatusText(status)
203 + "/" + status);
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700204 Toast.makeText(this, "Update Status changed", Toast.LENGTH_LONG)
205 .show();
Zhomart Mukhamejanov238beb72018-05-09 16:25:40 -0700206 if (status == UpdateEngine.UpdateStatusConstants.IDLE) {
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700207 Log.d(TAG, "status changed, resetting ui");
208 uiReset();
Zhomart Mukhamejanov238beb72018-05-09 16:25:40 -0700209 } else {
210 Log.d(TAG, "status changed, setting ui to updating mode");
211 uiSetUpdating();
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700212 }
Zhomart Mukhamejanov238beb72018-05-09 16:25:40 -0700213 setUiStatus(status);
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700214 });
215 }
216 }
217
218 /**
219 * Invoked when the payload has been applied, whether successfully or
220 * unsuccessfully. The value of {@code errorCode} will be one of the
221 * values from {@link UpdateEngine.ErrorCodeConstants}.
222 */
223 private void onPayloadApplicationComplete(int errorCode) {
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700224 final String state = UpdateEngineErrorCodes.isUpdateSucceeded(errorCode)
225 ? "SUCCESS"
226 : "FAILURE";
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700227 runOnUiThread(() -> {
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700228 Log.i("UpdateEngine",
229 "Completed - errorCode="
230 + UpdateEngineErrorCodes.getCodeName(errorCode) + "/" + errorCode
231 + " " + state);
232 Toast.makeText(this, "Update completed", Toast.LENGTH_LONG).show();
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700233 setUiCompletion(errorCode);
Zhomart Mukhamejanov238beb72018-05-09 16:25:40 -0700234 if (errorCode == UpdateEngineErrorCodes.UPDATED_BUT_NOT_ACTIVE) {
235 // if update was successfully applied.
236 if (mManualSwitchSlotRequired.get()) {
237 // Show "Switch Slot" button.
238 uiShowSwitchSlotInfo();
239 }
240 }
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700241 });
242 }
243
244 /** resets ui */
245 private void uiReset() {
246 mTextViewBuild.setText(Build.DISPLAY);
247 mSpinnerConfigs.setEnabled(true);
248 mButtonReload.setEnabled(true);
249 mButtonApplyConfig.setEnabled(true);
250 mButtonStop.setEnabled(false);
251 mButtonReset.setEnabled(false);
252 mProgressBar.setProgress(0);
253 mProgressBar.setEnabled(false);
254 mProgressBar.setVisibility(ProgressBar.INVISIBLE);
255 mTextViewStatus.setText(R.string.unknown);
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700256 mTextViewCompletion.setText(R.string.unknown);
Zhomart Mukhamejanov238beb72018-05-09 16:25:40 -0700257 uiHideSwitchSlotInfo();
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700258 }
259
260 /** sets ui updating mode */
261 private void uiSetUpdating() {
262 mTextViewBuild.setText(Build.DISPLAY);
263 mSpinnerConfigs.setEnabled(false);
264 mButtonReload.setEnabled(false);
265 mButtonApplyConfig.setEnabled(false);
266 mButtonStop.setEnabled(true);
267 mProgressBar.setEnabled(true);
268 mButtonReset.setEnabled(true);
269 mProgressBar.setVisibility(ProgressBar.VISIBLE);
270 }
271
Zhomart Mukhamejanov238beb72018-05-09 16:25:40 -0700272 private void uiShowSwitchSlotInfo() {
273 mButtonSwitchSlot.setEnabled(true);
274 mTextViewUpdateInfo.setTextColor(Color.parseColor("#777777"));
275 }
276
277 private void uiHideSwitchSlotInfo() {
278 mTextViewUpdateInfo.setTextColor(Color.parseColor("#AAAAAA"));
279 mButtonSwitchSlot.setEnabled(false);
280 }
281
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700282 /**
283 * loads json configurations from configs dir that is defined in {@link UpdateConfigs}.
284 */
285 private void loadUpdateConfigs() {
286 mConfigs = UpdateConfigs.getUpdateConfigs(this);
287 loadConfigsToSpinner(mConfigs);
288 }
289
290 /**
291 * @param status update engine status code
292 */
293 private void setUiStatus(int status) {
294 String statusText = UpdateEngineStatuses.getStatusText(status);
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700295 mTextViewStatus.setText(statusText + "/" + status);
296 }
297
298 /**
299 * @param errorCode update engine error code
300 */
301 private void setUiCompletion(int errorCode) {
302 final String state = UpdateEngineErrorCodes.isUpdateSucceeded(errorCode)
303 ? "SUCCESS"
304 : "FAILURE";
305 String errorText = UpdateEngineErrorCodes.getCodeName(errorCode);
306 mTextViewCompletion.setText(state + " " + errorText + "/" + errorCode);
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700307 }
308
309 private void loadConfigsToSpinner(List<UpdateConfig> configs) {
310 String[] spinnerArray = UpdateConfigs.configsToNames(configs);
311 ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<>(this,
312 android.R.layout.simple_spinner_item,
313 spinnerArray);
314 spinnerArrayAdapter.setDropDownViewResource(android.R.layout
315 .simple_spinner_dropdown_item);
316 mSpinnerConfigs.setAdapter(spinnerArrayAdapter);
317 }
318
319 private UpdateConfig getSelectedConfig() {
320 return mConfigs.get(mSpinnerConfigs.getSelectedItemPosition());
321 }
322
323 /**
324 * Applies the given update
325 */
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700326 private void applyUpdate(final UpdateConfig config) {
Zhomart Mukhamejanov238beb72018-05-09 16:25:40 -0700327 List<String> extraProperties = new ArrayList<>();
328
329 if (!config.getAbConfig().getForceSwitchSlot()) {
330 // Disable switch slot on reboot, which is enabled by default.
331 // User will enable it manually by clicking "Switch Slot" button on the screen.
332 extraProperties.add(UpdateEngineProperties.PROPERTY_DISABLE_SWITCH_SLOT_ON_REBOOT);
333 mManualSwitchSlotRequired.set(true);
334 } else {
335 mManualSwitchSlotRequired.set(false);
336 }
337
Zhomart Mukhamejanov963e3ee2018-04-26 21:07:05 -0700338 if (config.getInstallType() == UpdateConfig.AB_INSTALL_TYPE_NON_STREAMING) {
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700339 PayloadSpec payload;
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700340 try {
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700341 payload = PayloadSpecs.forNonStreaming(config.getUpdatePackageFile());
342 } catch (IOException e) {
343 Log.e(TAG, "Error creating payload spec", e);
344 Toast.makeText(this, "Error creating payload spec", Toast.LENGTH_LONG)
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700345 .show();
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700346 return;
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700347 }
Zhomart Mukhamejanov238beb72018-05-09 16:25:40 -0700348 updateEngineApplyPayload(payload, extraProperties);
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700349 } else {
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700350 Log.d(TAG, "Starting PrepareStreamingService");
Zhomart Mukhamejanov0dd5a832018-04-23 11:38:54 -0700351 PrepareStreamingService.startService(this, config, (code, payloadSpec) -> {
352 if (code == PrepareStreamingService.RESULT_CODE_SUCCESS) {
Zhomart Mukhamejanov6aa5fb02018-05-09 14:28:49 -0700353 extraProperties.add("USER_AGENT=" + HTTP_USER_AGENT);
354 config.getStreamingMetadata()
355 .getAuthorization()
356 .ifPresent(s -> extraProperties.add("AUTHORIZATION=" + s));
357 updateEngineApplyPayload(payloadSpec, extraProperties);
Zhomart Mukhamejanov0dd5a832018-04-23 11:38:54 -0700358 } else {
359 Log.e(TAG, "PrepareStreamingService failed, result code is " + code);
360 Toast.makeText(
361 MainActivity.this,
362 "PrepareStreamingService failed, result code is " + code,
363 Toast.LENGTH_LONG).show();
364 }
365 });
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700366 }
367 }
368
369 /**
370 * Applies given payload.
371 *
372 * UpdateEngine works asynchronously. This method doesn't wait until
373 * end of the update.
Zhomart Mukhamejanov6aa5fb02018-05-09 14:28:49 -0700374 *
375 * @param payloadSpec contains url, offset and size to {@code PAYLOAD_BINARY_FILE_NAME}
376 * @param extraProperties additional properties to pass to {@link UpdateEngine#applyPayload}
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700377 */
Zhomart Mukhamejanov6aa5fb02018-05-09 14:28:49 -0700378 private void updateEngineApplyPayload(PayloadSpec payloadSpec, List<String> extraProperties) {
Zhomart Mukhamejanov238beb72018-05-09 16:25:40 -0700379 mLastPayloadSpec = payloadSpec;
380
Zhomart Mukhamejanov6aa5fb02018-05-09 14:28:49 -0700381 ArrayList<String> properties = new ArrayList<>(payloadSpec.getProperties());
382 if (extraProperties != null) {
383 properties.addAll(extraProperties);
384 }
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700385 try {
386 mUpdateEngine.applyPayload(
387 payloadSpec.getUrl(),
388 payloadSpec.getOffset(),
389 payloadSpec.getSize(),
Zhomart Mukhamejanov6aa5fb02018-05-09 14:28:49 -0700390 properties.toArray(new String[0]));
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700391 } catch (Exception e) {
392 Log.e(TAG, "UpdateEngine failed to apply the update", e);
393 Toast.makeText(
394 this,
395 "UpdateEngine failed to apply the update",
396 Toast.LENGTH_LONG).show();
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700397 }
398 }
399
400 /**
Zhomart Mukhamejanov238beb72018-05-09 16:25:40 -0700401 * Sets the new slot that has the updated partitions as the active slot,
402 * which device will boot into next time.
403 * This method is only supposed to be called after the payload is applied.
404 *
405 * Invoking {@link UpdateEngine#applyPayload} with the same payload url, offset, size
406 * and payload metadata headers doesn't trigger new update. It can be used to just switch
407 * active A/B slot.
408 *
409 * {@link UpdateEngine#applyPayload} might take several seconds to finish, and it will
410 * invoke callbacks {@link this#onStatusUpdate} and {@link this#onPayloadApplicationComplete)}.
411 */
412 private void setSwitchSlotOnReboot() {
413 Log.d(TAG, "setSwitchSlotOnReboot invoked");
414 List<String> extraProperties = new ArrayList<>();
415 // PROPERTY_SKIP_POST_INSTALL should be passed on to skip post-installation hooks.
416 extraProperties.add(UpdateEngineProperties.PROPERTY_SKIP_POST_INSTALL);
417 // It sets property SWITCH_SLOT_ON_REBOOT=1 by default.
418 // HTTP headers are not required, UpdateEngine is not expected to stream payload.
419 updateEngineApplyPayload(mLastPayloadSpec, extraProperties);
420 uiHideSwitchSlotInfo();
421 }
422
423 /**
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700424 * Requests update engine to stop any ongoing update. If an update has been applied,
425 * leave it as is.
426 */
427 private void stopRunningUpdate() {
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700428 try {
429 mUpdateEngine.cancel();
430 } catch (Exception e) {
431 Log.w(TAG, "UpdateEngine failed to stop the ongoing update", e);
432 }
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700433 }
434
435 /**
436 * Resets update engine to IDLE state. Requests to cancel any onging update, or to revert if an
437 * update has been applied.
438 */
439 private void resetUpdate() {
Zhomart Mukhamejanovf7a70382018-05-02 20:37:12 -0700440 try {
441 mUpdateEngine.resetStatus();
442 } catch (Exception e) {
443 Log.w(TAG, "UpdateEngine failed to reset the update", e);
444 }
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700445 }
446
447 /**
Zhomart Mukhamejanovda7e2372018-05-01 12:50:55 -0700448 * Helper class to delegate {@code update_engine} callbacks to MainActivity
Zhomart Mukhamejanovf4d280c2018-04-17 13:20:22 -0700449 */
450 class UpdateEngineCallbackImpl extends UpdateEngineCallback {
451 @Override
452 public void onStatusUpdate(int status, float percent) {
453 MainActivity.this.onStatusUpdate(status, percent);
454 }
455
456 @Override
457 public void onPayloadApplicationComplete(int errorCode) {
458 MainActivity.this.onPayloadApplicationComplete(errorCode);
459 }
460 }
461
462}