From 963e3eeb003093b3934dab7f1c73a4c46d75321c Mon Sep 17 00:00:00 2001 From: Zhomart Mukhamejanov Date: Thu, 26 Apr 2018 21:07:05 -0700 Subject: updater_sample: Improve UpdateConfig UpdateConfig: - constant names changed - added parsing streaming metadata - added InnerFile to describe a file in zip Android.mk - added guava tests fixed Test: using junit4 Change-Id: Ibe3c8a3bde20259b0eea9a79aca4b22ed7b048f4 Signed-off-by: Zhomart Mukhamejanov --- .../android/systemupdatersample/UpdateConfig.java | 107 +++++++++++++++------ .../systemupdatersample/ui/MainActivity.java | 2 +- .../systemupdatersample/util/UpdateConfigs.java | 2 + 3 files changed, 80 insertions(+), 31 deletions(-) (limited to 'updater_sample/src/com/example/android/systemupdatersample') diff --git a/updater_sample/src/com/example/android/systemupdatersample/UpdateConfig.java b/updater_sample/src/com/example/android/systemupdatersample/UpdateConfig.java index cbee18fcb..23510e426 100644 --- a/updater_sample/src/com/example/android/systemupdatersample/UpdateConfig.java +++ b/updater_sample/src/com/example/android/systemupdatersample/UpdateConfig.java @@ -19,6 +19,7 @@ package com.example.android.systemupdatersample; import android.os.Parcel; import android.os.Parcelable; +import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -26,13 +27,13 @@ import java.io.File; import java.io.Serializable; /** - * UpdateConfig describes an update. It will be parsed from JSON, which is intended to + * An update description. It will be parsed from JSON, which is intended to * be sent from server to the update app, but in this sample app it will be stored on the device. */ public class UpdateConfig implements Parcelable { - public static final int TYPE_NON_STREAMING = 0; - public static final int TYPE_STREAMING = 1; + public static final int AB_INSTALL_TYPE_NON_STREAMING = 0; + public static final int AB_INSTALL_TYPE_STREAMING = 1; public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { @@ -54,18 +55,30 @@ public class UpdateConfig implements Parcelable { JSONObject o = new JSONObject(json); c.mName = o.getString("name"); c.mUrl = o.getString("url"); - if (TYPE_NON_STREAMING_JSON.equals(o.getString("type"))) { - c.mInstallType = TYPE_NON_STREAMING; - } else if (TYPE_STREAMING_JSON.equals(o.getString("type"))) { - c.mInstallType = TYPE_STREAMING; - } else { - throw new JSONException("Invalid type, expected either " - + "NON_STREAMING or STREAMING, got " + o.getString("type")); + switch (o.getString("ab_install_type")) { + case AB_INSTALL_TYPE_NON_STREAMING_JSON: + c.mAbInstallType = AB_INSTALL_TYPE_NON_STREAMING; + break; + case AB_INSTALL_TYPE_STREAMING_JSON: + c.mAbInstallType = AB_INSTALL_TYPE_STREAMING; + break; + default: + throw new JSONException("Invalid type, expected either " + + "NON_STREAMING or STREAMING, got " + o.getString("ab_install_type")); } - if (o.has("metadata")) { - c.mMetadata = new Metadata( - o.getJSONObject("metadata").getInt("offset"), - o.getJSONObject("metadata").getInt("size")); + if (c.mAbInstallType == AB_INSTALL_TYPE_STREAMING) { + JSONObject meta = o.getJSONObject("ab_streaming_metadata"); + JSONArray propertyFilesJson = meta.getJSONArray("property_files"); + InnerFile[] propertyFiles = + new InnerFile[propertyFilesJson.length()]; + for (int i = 0; i < propertyFilesJson.length(); i++) { + JSONObject p = propertyFilesJson.getJSONObject(i); + propertyFiles[i] = new InnerFile( + p.getString("filename"), + p.getLong("offset"), + p.getLong("size")); + } + c.mAbStreamingMetadata = new StreamingMetadata(propertyFiles); } c.mRawJson = json; return c; @@ -74,8 +87,8 @@ public class UpdateConfig implements Parcelable { /** * these strings are represent types in JSON config files */ - private static final String TYPE_NON_STREAMING_JSON = "NON_STREAMING"; - private static final String TYPE_STREAMING_JSON = "STREAMING"; + private static final String AB_INSTALL_TYPE_NON_STREAMING_JSON = "NON_STREAMING"; + private static final String AB_INSTALL_TYPE_STREAMING_JSON = "STREAMING"; /** name will be visible on UI */ private String mName; @@ -84,10 +97,10 @@ public class UpdateConfig implements Parcelable { private String mUrl; /** non-streaming (first saves locally) OR streaming (on the fly) */ - private int mInstallType; + private int mAbInstallType; /** metadata is required only for streaming update */ - private Metadata mMetadata; + private StreamingMetadata mAbStreamingMetadata; private String mRawJson; @@ -97,15 +110,15 @@ public class UpdateConfig implements Parcelable { protected UpdateConfig(Parcel in) { this.mName = in.readString(); this.mUrl = in.readString(); - this.mInstallType = in.readInt(); - this.mMetadata = (Metadata) in.readSerializable(); + this.mAbInstallType = in.readInt(); + this.mAbStreamingMetadata = (StreamingMetadata) in.readSerializable(); this.mRawJson = in.readString(); } public UpdateConfig(String name, String url, int installType) { this.mName = name; this.mUrl = url; - this.mInstallType = installType; + this.mAbInstallType = installType; } public String getName() { @@ -121,16 +134,18 @@ public class UpdateConfig implements Parcelable { } public int getInstallType() { - return mInstallType; + return mAbInstallType; + } + + public StreamingMetadata getStreamingMetadata() { + return mAbStreamingMetadata; } /** - * "url" must be the file located on the device. - * * @return File object for given url */ public File getUpdatePackageFile() { - if (mInstallType != TYPE_NON_STREAMING) { + if (mAbInstallType != AB_INSTALL_TYPE_NON_STREAMING) { throw new RuntimeException("Expected non-streaming install type"); } if (!mUrl.startsWith("file://")) { @@ -148,29 +163,60 @@ public class UpdateConfig implements Parcelable { public void writeToParcel(Parcel dest, int flags) { dest.writeString(mName); dest.writeString(mUrl); - dest.writeInt(mInstallType); - dest.writeSerializable(mMetadata); + dest.writeInt(mAbInstallType); + dest.writeSerializable(mAbStreamingMetadata); dest.writeString(mRawJson); } /** - * Metadata for STREAMING update + * Metadata for streaming A/B update. */ - public static class Metadata implements Serializable { + public static class StreamingMetadata implements Serializable { private static final long serialVersionUID = 31042L; + /** defines beginning of update data in archive */ + private InnerFile[] mPropertyFiles; + + public StreamingMetadata() { + mPropertyFiles = new InnerFile[0]; + } + + public StreamingMetadata(InnerFile[] propertyFiles) { + this.mPropertyFiles = propertyFiles; + } + + public InnerFile[] getPropertyFiles() { + return mPropertyFiles; + } + } + + /** + * Description of a file in an OTA package zip file. + */ + public static class InnerFile implements Serializable { + + private static final long serialVersionUID = 31043L; + + /** filename in an archive */ + private String mFilename; + /** defines beginning of update data in archive */ private long mOffset; /** size of the update data in archive */ private long mSize; - public Metadata(long offset, long size) { + public InnerFile(String filename, long offset, long size) { + this.mFilename = filename; this.mOffset = offset; this.mSize = size; } + public String getFilename() { + return mFilename; + } + public long getOffset() { return mOffset; } @@ -178,6 +224,7 @@ public class UpdateConfig implements Parcelable { public long getSize() { return mSize; } + } } diff --git a/updater_sample/src/com/example/android/systemupdatersample/ui/MainActivity.java b/updater_sample/src/com/example/android/systemupdatersample/ui/MainActivity.java index 72e1b2469..8507a9e84 100644 --- a/updater_sample/src/com/example/android/systemupdatersample/ui/MainActivity.java +++ b/updater_sample/src/com/example/android/systemupdatersample/ui/MainActivity.java @@ -260,7 +260,7 @@ public class MainActivity extends Activity { * Applies the given update */ private void applyUpdate(UpdateConfig config) { - if (config.getInstallType() == UpdateConfig.TYPE_NON_STREAMING) { + if (config.getInstallType() == UpdateConfig.AB_INSTALL_TYPE_NON_STREAMING) { AbNonStreamingUpdate update = new AbNonStreamingUpdate(mUpdateEngine, config); try { update.execute(); diff --git a/updater_sample/src/com/example/android/systemupdatersample/util/UpdateConfigs.java b/updater_sample/src/com/example/android/systemupdatersample/util/UpdateConfigs.java index 089f8b2f2..71d4df8ab 100644 --- a/updater_sample/src/com/example/android/systemupdatersample/util/UpdateConfigs.java +++ b/updater_sample/src/com/example/android/systemupdatersample/util/UpdateConfigs.java @@ -17,6 +17,7 @@ package com.example.android.systemupdatersample.util; import android.content.Context; +import android.util.Log; import com.example.android.systemupdatersample.UpdateConfig; @@ -70,6 +71,7 @@ public final class UpdateConfigs { StandardCharsets.UTF_8); configs.add(UpdateConfig.fromJson(json)); } catch (Exception e) { + Log.e("UpdateConfigs", "Can't read/parse config file " + f.getName(), e); throw new RuntimeException( "Can't read/parse config file " + f.getName(), e); } -- cgit v1.2.3 From 93535dd033024f9fb9413dba56bb5fa49abcba0b Mon Sep 17 00:00:00 2001 From: Zhomart Mukhamejanov Date: Thu, 26 Apr 2018 16:10:12 -0700 Subject: updater_sample: add FileDownloader Test: unit tests Change-Id: I10933e7172d7ebc34c7cf5e4274625d7b8399246 Signed-off-by: Zhomart Mukhamejanov --- .../systemupdatersample/util/FileDownloader.java | 93 ++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 updater_sample/src/com/example/android/systemupdatersample/util/FileDownloader.java (limited to 'updater_sample/src/com/example/android/systemupdatersample') diff --git a/updater_sample/src/com/example/android/systemupdatersample/util/FileDownloader.java b/updater_sample/src/com/example/android/systemupdatersample/util/FileDownloader.java new file mode 100644 index 000000000..806f17351 --- /dev/null +++ b/updater_sample/src/com/example/android/systemupdatersample/util/FileDownloader.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.systemupdatersample.util; + +import android.util.Log; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URL; +import java.net.URLConnection; + +/** + * Downloads chunk of a file from given url using {@code offset} and {@code size}, + * and saves to a given location. + * + * In real-life application this helper class should download from HTTP Server, + * but in this sample app it will only download from a local file. + */ +public final class FileDownloader { + + private String mUrl; + private long mOffset; + private long mSize; + private File mOut; + + public FileDownloader(String url, long offset, long size, File out) { + this.mUrl = url; + this.mOffset = offset; + this.mSize = size; + this.mOut = out; + } + + /** + * Downloads the file with given offset and size. + */ + public void download() throws IOException { + Log.d("FileDownloader", "downloading " + mOut.getName() + + " from " + mUrl + + " to " + mOut.getAbsolutePath()); + + URL url = new URL(mUrl); + URLConnection connection = url.openConnection(); + connection.connect(); + + // download the file + try (InputStream input = connection.getInputStream()) { + try (OutputStream output = new FileOutputStream(mOut)) { + long skipped = input.skip(mOffset); + if (skipped != mOffset) { + throw new IOException("Can't download file " + + mUrl + + " with given offset " + + mOffset); + } + byte[] data = new byte[4096]; + long total = 0; + while (total < mSize) { + int needToRead = (int) Math.min(4096, mSize - total); + int count = input.read(data, 0, needToRead); + if (count <= 0) { + break; + } + output.write(data, 0, count); + total += count; + } + if (total != mSize) { + throw new IOException("Can't download file " + + mUrl + + " with given size " + + mSize); + } + } + } + } + +} -- cgit v1.2.3 From da7e23759660ebf76a184e4c4d981f11ef9e2653 Mon Sep 17 00:00:00 2001 From: Zhomart Mukhamejanov Date: Tue, 1 May 2018 12:50:55 -0700 Subject: updater_sample: Add streaming to PayloadSpec PayloadSpec - add streaming generator and tests - fix sample.json - fix tests - rename PackagePropertyFiles to PackageFiles, it has info not only about property files, and new name is shorter Bug: 77148467 Test: `mmma -j bootable/recovery/updater_sample` Change-Id: I9c1206c07c37183f13d3c25940f12981ca85b1b4 Signed-off-by: Zhomart Mukhamejanov --- .../systemupdatersample/ui/MainActivity.java | 2 +- .../systemupdatersample/util/PackageFiles.java | 59 ++++++++++++++++++++++ .../util/PackagePropertyFiles.java | 42 --------------- .../systemupdatersample/util/PayloadSpecs.java | 24 ++++++--- .../util/UpdateEngineErrorCodes.java | 1 + .../util/UpdateEngineStatuses.java | 2 +- 6 files changed, 80 insertions(+), 50 deletions(-) create mode 100644 updater_sample/src/com/example/android/systemupdatersample/util/PackageFiles.java delete mode 100644 updater_sample/src/com/example/android/systemupdatersample/util/PackagePropertyFiles.java (limited to 'updater_sample/src/com/example/android/systemupdatersample') diff --git a/updater_sample/src/com/example/android/systemupdatersample/ui/MainActivity.java b/updater_sample/src/com/example/android/systemupdatersample/ui/MainActivity.java index 8507a9e84..9f1e5d170 100644 --- a/updater_sample/src/com/example/android/systemupdatersample/ui/MainActivity.java +++ b/updater_sample/src/com/example/android/systemupdatersample/ui/MainActivity.java @@ -297,7 +297,7 @@ public class MainActivity extends Activity { } /** - * Helper class to delegate UpdateEngine callbacks to MainActivity + * Helper class to delegate {@code update_engine} callbacks to MainActivity */ class UpdateEngineCallbackImpl extends UpdateEngineCallback { @Override diff --git a/updater_sample/src/com/example/android/systemupdatersample/util/PackageFiles.java b/updater_sample/src/com/example/android/systemupdatersample/util/PackageFiles.java new file mode 100644 index 000000000..b485234ea --- /dev/null +++ b/updater_sample/src/com/example/android/systemupdatersample/util/PackageFiles.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.systemupdatersample.util; + +/** Utility class for an OTA package. */ +public final class PackageFiles { + + /** + * Directory used to perform updates. + */ + public static final String OTA_PACKAGE_DIR = "/data/ota_package"; + + /** + * update payload, it will be passed to {@code UpdateEngine#applyPayload}. + */ + public static final String PAYLOAD_BINARY_FILE_NAME = "payload.bin"; + + /** + * Currently, when calling {@code UpdateEngine#applyPayload} to perform actions + * that don't require network access (e.g. change slot), update_engine still + * talks to the server to download/verify file. + * {@code update_engine} might throw error when rebooting if {@code UpdateEngine#applyPayload} + * is not supplied right headers and tokens. + * This behavior might change in future android versions. + * + * To avoid extra network request in {@code update_engine}, this file has to be + * downloaded and put in {@code OTA_PACKAGE_DIR}. + */ + public static final String PAYLOAD_METADATA_FILE_NAME = "payload_metadata.bin"; + + public static final String PAYLOAD_PROPERTIES_FILE_NAME = "payload_properties.txt"; + + /** The zip entry in an A/B OTA package, which will be used by update_verifier. */ + public static final String CARE_MAP_FILE_NAME = "care_map.txt"; + + public static final String METADATA_FILE_NAME = "metadata"; + + /** + * The zip file that claims the compatibility of the update package to check against the Android + * framework to ensure that the package can be installed on the device. + */ + public static final String COMPATIBILITY_ZIP_FILE_NAME = "compatibility.zip"; + + private PackageFiles() {} +} diff --git a/updater_sample/src/com/example/android/systemupdatersample/util/PackagePropertyFiles.java b/updater_sample/src/com/example/android/systemupdatersample/util/PackagePropertyFiles.java deleted file mode 100644 index 3988b5928..000000000 --- a/updater_sample/src/com/example/android/systemupdatersample/util/PackagePropertyFiles.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.android.systemupdatersample.util; - -/** Utility class for property files in a package. */ -public final class PackagePropertyFiles { - - public static final String PAYLOAD_BINARY_FILE_NAME = "payload.bin"; - - public static final String PAYLOAD_HEADER_FILE_NAME = "payload_header.bin"; - - public static final String PAYLOAD_METADATA_FILE_NAME = "payload_metadata.bin"; - - public static final String PAYLOAD_PROPERTIES_FILE_NAME = "payload_properties.txt"; - - /** The zip entry in an A/B OTA package, which will be used by update_verifier. */ - public static final String CARE_MAP_FILE_NAME = "care_map.txt"; - - public static final String METADATA_FILE_NAME = "metadata"; - - /** - * The zip file that claims the compatibility of the update package to check against the Android - * framework to ensure that the package can be installed on the device. - */ - public static final String COMPATIBILITY_ZIP_FILE_NAME = "compatibility.zip"; - - private PackagePropertyFiles() {} -} diff --git a/updater_sample/src/com/example/android/systemupdatersample/util/PayloadSpecs.java b/updater_sample/src/com/example/android/systemupdatersample/util/PayloadSpecs.java index 43c8d75e2..4db448a31 100644 --- a/updater_sample/src/com/example/android/systemupdatersample/util/PayloadSpecs.java +++ b/updater_sample/src/com/example/android/systemupdatersample/util/PayloadSpecs.java @@ -16,9 +16,6 @@ package com.example.android.systemupdatersample.util; -import android.annotation.TargetApi; -import android.os.Build; - import com.example.android.systemupdatersample.PayloadSpec; import java.io.BufferedReader; @@ -26,6 +23,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.nio.file.Files; import java.util.ArrayList; import java.util.Arrays; import java.util.Enumeration; @@ -34,7 +32,6 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipFile; /** The helper class that creates {@link PayloadSpec}. */ -@TargetApi(Build.VERSION_CODES.N) public final class PayloadSpecs { /** @@ -68,14 +65,14 @@ public final class PayloadSpecs { } long length = entry.getCompressedSize(); - if (PackagePropertyFiles.PAYLOAD_BINARY_FILE_NAME.equals(name)) { + if (PackageFiles.PAYLOAD_BINARY_FILE_NAME.equals(name)) { if (entry.getMethod() != ZipEntry.STORED) { throw new IOException("Invalid compression method."); } payloadFound = true; payloadOffset = offset; payloadSize = length; - } else if (PackagePropertyFiles.PAYLOAD_PROPERTIES_FILE_NAME.equals(name)) { + } else if (PackageFiles.PAYLOAD_PROPERTIES_FILE_NAME.equals(name)) { InputStream inputStream = zip.getInputStream(entry); if (inputStream != null) { BufferedReader br = new BufferedReader(new InputStreamReader(inputStream)); @@ -100,6 +97,21 @@ public final class PayloadSpecs { .build(); } + /** + * Creates a {@link PayloadSpec} for streaming update. + */ + public static PayloadSpec forStreaming(String updateUrl, + long offset, + long size, + File propertiesFile) throws IOException { + return PayloadSpec.newBuilder() + .url(updateUrl) + .offset(offset) + .size(size) + .properties(Files.readAllLines(propertiesFile.toPath())) + .build(); + } + /** * Converts an {@link PayloadSpec} to a string. */ diff --git a/updater_sample/src/com/example/android/systemupdatersample/util/UpdateEngineErrorCodes.java b/updater_sample/src/com/example/android/systemupdatersample/util/UpdateEngineErrorCodes.java index e63da6298..6d319c5af 100644 --- a/updater_sample/src/com/example/android/systemupdatersample/util/UpdateEngineErrorCodes.java +++ b/updater_sample/src/com/example/android/systemupdatersample/util/UpdateEngineErrorCodes.java @@ -50,6 +50,7 @@ public final class UpdateEngineErrorCodes { CODE_TO_NAME_MAP.put(10, "PAYLOAD_HASH_MISMATCH_ERROR"); CODE_TO_NAME_MAP.put(11, "PAYLOAD_SIZE_MISMATCH_ERROR"); CODE_TO_NAME_MAP.put(12, "DOWNLOAD_PAYLOAD_VERIFICATION_ERROR"); + CODE_TO_NAME_MAP.put(15, "NEW_ROOTFS_VERIFICATION_ERROR"); CODE_TO_NAME_MAP.put(20, "DOWNLOAD_STATE_INITIALIZATION_ERROR"); CODE_TO_NAME_MAP.put(48, "USER_CANCELLED"); CODE_TO_NAME_MAP.put(52, "UPDATED_BUT_NOT_ACTIVE"); diff --git a/updater_sample/src/com/example/android/systemupdatersample/util/UpdateEngineStatuses.java b/updater_sample/src/com/example/android/systemupdatersample/util/UpdateEngineStatuses.java index 6203b201a..a96f19d84 100644 --- a/updater_sample/src/com/example/android/systemupdatersample/util/UpdateEngineStatuses.java +++ b/updater_sample/src/com/example/android/systemupdatersample/util/UpdateEngineStatuses.java @@ -20,7 +20,7 @@ import android.util.SparseArray; /** * Helper class to work with update_engine's error codes. - * Many error codes are defined in {@link UpdateEngine.UpdateStatusConstants}, + * Many error codes are defined in {@code UpdateEngine.UpdateStatusConstants}, * but you can find more in system/update_engine/common/error_code.h. */ public final class UpdateEngineStatuses { -- cgit v1.2.3 From f7a70388ee22e5971ea19caef5208c1da2282ee4 Mon Sep 17 00:00:00 2001 From: Zhomart Mukhamejanov Date: Wed, 2 May 2018 20:37:12 -0700 Subject: updater_sample: update ui and README, clean-up - ui: add text view for latest completion (error) code - update README.md - update MainActivity.java - remove AbNonStreamingUpdate Test: mmma bootable/recovery/updater_sample Change-Id: Ie9bb64211c57d536036b04f13896e4937c392b6e Signed-off-by: Zhomart Mukhamejanov --- .../android/systemupdatersample/PayloadSpec.java | 5 +- .../systemupdatersample/ui/MainActivity.java | 102 +++++++++++++++------ .../updates/AbNonStreamingUpdate.java | 52 ----------- .../systemupdatersample/util/FileDownloader.java | 2 +- 4 files changed, 81 insertions(+), 80 deletions(-) delete mode 100644 updater_sample/src/com/example/android/systemupdatersample/updates/AbNonStreamingUpdate.java (limited to 'updater_sample/src/com/example/android/systemupdatersample') diff --git a/updater_sample/src/com/example/android/systemupdatersample/PayloadSpec.java b/updater_sample/src/com/example/android/systemupdatersample/PayloadSpec.java index 90c5637ea..ce8833883 100644 --- a/updater_sample/src/com/example/android/systemupdatersample/PayloadSpec.java +++ b/updater_sample/src/com/example/android/systemupdatersample/PayloadSpec.java @@ -18,12 +18,15 @@ package com.example.android.systemupdatersample; import android.os.UpdateEngine; +import java.io.Serializable; import java.util.List; /** * Payload that will be given to {@link UpdateEngine#applyPayload)}. */ -public class PayloadSpec { +public class PayloadSpec implements Serializable { + + private static final long serialVersionUID = 41043L; /** * Creates a payload spec {@link Builder} diff --git a/updater_sample/src/com/example/android/systemupdatersample/ui/MainActivity.java b/updater_sample/src/com/example/android/systemupdatersample/ui/MainActivity.java index 9f1e5d170..d6a6ce3f5 100644 --- a/updater_sample/src/com/example/android/systemupdatersample/ui/MainActivity.java +++ b/updater_sample/src/com/example/android/systemupdatersample/ui/MainActivity.java @@ -31,13 +31,15 @@ import android.widget.Spinner; import android.widget.TextView; import android.widget.Toast; +import com.example.android.systemupdatersample.PayloadSpec; import com.example.android.systemupdatersample.R; import com.example.android.systemupdatersample.UpdateConfig; -import com.example.android.systemupdatersample.updates.AbNonStreamingUpdate; +import com.example.android.systemupdatersample.util.PayloadSpecs; import com.example.android.systemupdatersample.util.UpdateConfigs; import com.example.android.systemupdatersample.util.UpdateEngineErrorCodes; import com.example.android.systemupdatersample.util.UpdateEngineStatuses; +import java.io.IOException; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; @@ -46,6 +48,8 @@ import java.util.concurrent.atomic.AtomicInteger; */ public class MainActivity extends Activity { + private static final String TAG = "MainActivity"; + private TextView mTextViewBuild; private Spinner mSpinnerConfigs; private TextView mTextViewConfigsDirHint; @@ -55,17 +59,19 @@ public class MainActivity extends Activity { private Button mButtonReset; private ProgressBar mProgressBar; private TextView mTextViewStatus; + private TextView mTextViewCompletion; private List mConfigs; private AtomicInteger mUpdateEngineStatus = new AtomicInteger(UpdateEngine.UpdateStatusConstants.IDLE); - private UpdateEngine mUpdateEngine = new UpdateEngine(); /** * Listen to {@code update_engine} events. */ private UpdateEngineCallbackImpl mUpdateEngineCallback = new UpdateEngineCallbackImpl(); + private final UpdateEngine mUpdateEngine = new UpdateEngine(); + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -80,14 +86,14 @@ public class MainActivity extends Activity { this.mButtonReset = findViewById(R.id.buttonReset); this.mProgressBar = findViewById(R.id.progressBar); this.mTextViewStatus = findViewById(R.id.textViewStatus); - - this.mUpdateEngine.bind(mUpdateEngineCallback); + this.mTextViewCompletion = findViewById(R.id.textViewCompletion); this.mTextViewConfigsDirHint.setText(UpdateConfigs.getConfigsRoot(this)); uiReset(); - loadUpdateConfigs(); + + this.mUpdateEngine.bind(mUpdateEngineCallback); } @Override @@ -140,7 +146,6 @@ public class MainActivity extends Activity { .setMessage("Do you really want to cancel running update?") .setIcon(android.R.drawable.ic_dialog_alert) .setPositiveButton(android.R.string.ok, (dialog, whichButton) -> { - uiReset(); stopRunningUpdate(); }) .setNegativeButton(android.R.string.cancel, null).show(); @@ -156,7 +161,6 @@ public class MainActivity extends Activity { + " and restore old version?") .setIcon(android.R.drawable.ic_dialog_alert) .setPositiveButton(android.R.string.ok, (dialog, whichButton) -> { - uiReset(); resetUpdate(); }) .setNegativeButton(android.R.string.cancel, null).show(); @@ -178,6 +182,13 @@ public class MainActivity extends Activity { setUiStatus(status); Toast.makeText(this, "Update Status changed", Toast.LENGTH_LONG) .show(); + if (status != UpdateEngine.UpdateStatusConstants.IDLE) { + Log.d(TAG, "status changed, setting ui to updating mode"); + uiSetUpdating(); + } else { + Log.d(TAG, "status changed, resetting ui"); + uiReset(); + } }); } } @@ -188,15 +199,16 @@ public class MainActivity extends Activity { * values from {@link UpdateEngine.ErrorCodeConstants}. */ private void onPayloadApplicationComplete(int errorCode) { + final String state = UpdateEngineErrorCodes.isUpdateSucceeded(errorCode) + ? "SUCCESS" + : "FAILURE"; runOnUiThread(() -> { - final String state = UpdateEngineErrorCodes.isUpdateSucceeded(errorCode) - ? "SUCCESS" - : "FAILURE"; Log.i("UpdateEngine", "Completed - errorCode=" + UpdateEngineErrorCodes.getCodeName(errorCode) + "/" + errorCode + " " + state); Toast.makeText(this, "Update completed", Toast.LENGTH_LONG).show(); + setUiCompletion(errorCode); }); } @@ -212,6 +224,7 @@ public class MainActivity extends Activity { mProgressBar.setEnabled(false); mProgressBar.setVisibility(ProgressBar.INVISIBLE); mTextViewStatus.setText(R.string.unknown); + mTextViewCompletion.setText(R.string.unknown); } /** sets ui updating mode */ @@ -239,7 +252,18 @@ public class MainActivity extends Activity { */ private void setUiStatus(int status) { String statusText = UpdateEngineStatuses.getStatusText(status); - mTextViewStatus.setText(statusText); + mTextViewStatus.setText(statusText + "/" + status); + } + + /** + * @param errorCode update engine error code + */ + private void setUiCompletion(int errorCode) { + final String state = UpdateEngineErrorCodes.isUpdateSucceeded(errorCode) + ? "SUCCESS" + : "FAILURE"; + String errorText = UpdateEngineErrorCodes.getCodeName(errorCode); + mTextViewCompletion.setText(state + " " + errorText + "/" + errorCode); } private void loadConfigsToSpinner(List configs) { @@ -259,19 +283,42 @@ public class MainActivity extends Activity { /** * Applies the given update */ - private void applyUpdate(UpdateConfig config) { + private void applyUpdate(final UpdateConfig config) { if (config.getInstallType() == UpdateConfig.AB_INSTALL_TYPE_NON_STREAMING) { - AbNonStreamingUpdate update = new AbNonStreamingUpdate(mUpdateEngine, config); + PayloadSpec payload; try { - update.execute(); - } catch (Exception e) { - Log.e("MainActivity", "Error applying the update", e); - Toast.makeText(this, "Error applying the update", Toast.LENGTH_SHORT) + payload = PayloadSpecs.forNonStreaming(config.getUpdatePackageFile()); + } catch (IOException e) { + Log.e(TAG, "Error creating payload spec", e); + Toast.makeText(this, "Error creating payload spec", Toast.LENGTH_LONG) .show(); + return; } + updateEngineApplyPayload(payload); } else { - Toast.makeText(this, "Streaming is not implemented", Toast.LENGTH_SHORT) - .show(); + Log.d(TAG, "Starting PrepareStreamingService"); + } + } + + /** + * Applies given payload. + * + * UpdateEngine works asynchronously. This method doesn't wait until + * end of the update. + */ + private void updateEngineApplyPayload(PayloadSpec payloadSpec) { + try { + mUpdateEngine.applyPayload( + payloadSpec.getUrl(), + payloadSpec.getOffset(), + payloadSpec.getSize(), + payloadSpec.getProperties().toArray(new String[0])); + } catch (Exception e) { + Log.e(TAG, "UpdateEngine failed to apply the update", e); + Toast.makeText( + this, + "UpdateEngine failed to apply the update", + Toast.LENGTH_LONG).show(); } } @@ -280,10 +327,11 @@ public class MainActivity extends Activity { * leave it as is. */ private void stopRunningUpdate() { - Toast.makeText(this, - "stopRunningUpdate is not implemented", - Toast.LENGTH_SHORT).show(); - + try { + mUpdateEngine.cancel(); + } catch (Exception e) { + Log.w(TAG, "UpdateEngine failed to stop the ongoing update", e); + } } /** @@ -291,9 +339,11 @@ public class MainActivity extends Activity { * update has been applied. */ private void resetUpdate() { - Toast.makeText(this, - "resetUpdate is not implemented", - Toast.LENGTH_SHORT).show(); + try { + mUpdateEngine.resetStatus(); + } catch (Exception e) { + Log.w(TAG, "UpdateEngine failed to reset the update", e); + } } /** diff --git a/updater_sample/src/com/example/android/systemupdatersample/updates/AbNonStreamingUpdate.java b/updater_sample/src/com/example/android/systemupdatersample/updates/AbNonStreamingUpdate.java deleted file mode 100644 index 1b91a1ac3..000000000 --- a/updater_sample/src/com/example/android/systemupdatersample/updates/AbNonStreamingUpdate.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.android.systemupdatersample.updates; - -import android.os.UpdateEngine; - -import com.example.android.systemupdatersample.PayloadSpec; -import com.example.android.systemupdatersample.UpdateConfig; -import com.example.android.systemupdatersample.util.PayloadSpecs; - -/** - * Applies A/B (seamless) non-streaming update. - */ -public class AbNonStreamingUpdate { - - private final UpdateEngine mUpdateEngine; - private final UpdateConfig mUpdateConfig; - - public AbNonStreamingUpdate(UpdateEngine updateEngine, UpdateConfig config) { - this.mUpdateEngine = updateEngine; - this.mUpdateConfig = config; - } - - /** - * Start applying the update. This method doesn't wait until end of the update. - * {@code update_engine} works asynchronously. - */ - public void execute() throws Exception { - PayloadSpec payload = PayloadSpecs.forNonStreaming(mUpdateConfig.getUpdatePackageFile()); - - mUpdateEngine.applyPayload( - payload.getUrl(), - payload.getOffset(), - payload.getSize(), - payload.getProperties().toArray(new String[0])); - } - -} diff --git a/updater_sample/src/com/example/android/systemupdatersample/util/FileDownloader.java b/updater_sample/src/com/example/android/systemupdatersample/util/FileDownloader.java index 806f17351..5c1d71117 100644 --- a/updater_sample/src/com/example/android/systemupdatersample/util/FileDownloader.java +++ b/updater_sample/src/com/example/android/systemupdatersample/util/FileDownloader.java @@ -40,7 +40,7 @@ public final class FileDownloader { private long mSize; private File mOut; - public FileDownloader(String url, long offset, long size, File out) { + public FileDownloader(String url, long offset, long size, File out) { this.mUrl = url; this.mOffset = offset; this.mSize = size; -- cgit v1.2.3