Updater: Implement leaf updates
Change-Id: I02a74807604851a977f5e16c692c21361c64f03a
diff --git a/README.md b/README.md
index ecb06f4..fd6eb6e 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@
Server requirements
-------------------
The app sends `GET` requests to the URL defined by the `updater_server_url`
-resource (or the `lineage.updater.uri` system property) and expects as response
+resource (or the `leaf.updater.uri` system property) and expects as response
a JSON with the following structure:
```json
{
@@ -18,7 +18,8 @@
"romtype": "nightly",
"size": 314572800,
"url": "https://example.com/ota-package.zip",
- "version": "15.1"
+ "version": "3.0",
+ "upgrade": "2.0"
}
]
}
@@ -31,6 +32,7 @@
The `size` attribute is the size of the update expressed in bytes.
The `url` attribute is the URL of the file to be downloaded.
The `version` attribute is the string to be compared with the `ro.lineage.build.version` property.
+The `upgrade` attribute is the minimum version supported for automatic upgrades to the current build.
Additional attributes are ignored.
diff --git a/app/src/main/java/org/lineageos/updater/misc/Constants.java b/app/src/main/java/org/lineageos/updater/misc/Constants.java
index 0aee507..49bad25 100644
--- a/app/src/main/java/org/lineageos/updater/misc/Constants.java
+++ b/app/src/main/java/org/lineageos/updater/misc/Constants.java
@@ -39,13 +39,14 @@
public static final String PROP_AB_DEVICE = "ro.build.ab_update";
public static final String PROP_BUILD_DATE = "ro.build.date.utc";
- public static final String PROP_BUILD_VERSION = "ro.lineage.build.version";
+ public static final String PROP_BUILD_VERSION = "ro.leaf.build.version";
public static final String PROP_BUILD_VERSION_INCREMENTAL = "ro.build.version.incremental";
- public static final String PROP_DEVICE = "ro.lineage.device";
+ public static final String PROP_DEVICE = "ro.product.device";
public static final String PROP_NEXT_DEVICE = "ro.updater.next_device";
- public static final String PROP_RELEASE_TYPE = "ro.lineage.releasetype";
- public static final String PROP_UPDATER_ALLOW_DOWNGRADING = "lineage.updater.allow_downgrading";
- public static final String PROP_UPDATER_URI = "lineage.updater.uri";
+ public static final String PROP_RELEASE_TYPE = "ro.leaf.releasetype";
+ public static final String PROP_FLAVOR = "ro.leaf.flavor";
+ public static final String PROP_UPDATER_ALLOW_DOWNGRADING = "leaf.updater.allow_downgrading";
+ public static final String PROP_UPDATER_URI = "leaf.updater.uri";
public static final String PREF_INSTALL_OLD_TIMESTAMP = "install_old_timestamp";
public static final String PREF_INSTALL_NEW_TIMESTAMP = "install_new_timestamp";
diff --git a/app/src/main/java/org/lineageos/updater/misc/Utils.java b/app/src/main/java/org/lineageos/updater/misc/Utils.java
index 3bdd509..b1e90b5 100644
--- a/app/src/main/java/org/lineageos/updater/misc/Utils.java
+++ b/app/src/main/java/org/lineageos/updater/misc/Utils.java
@@ -92,6 +92,8 @@
update.setFileSize(object.getLong("size"));
update.setDownloadUrl(object.getString("url"));
update.setVersion(object.getString("version"));
+ if (object.has("upgrade"))
+ update.setUpgradeMinVersion(object.getString("upgrade"));
return update;
}
@@ -114,7 +116,10 @@
public static boolean canInstall(UpdateBaseInfo update) {
return (SystemProperties.getBoolean(Constants.PROP_UPDATER_ALLOW_DOWNGRADING, false) ||
- update.getTimestamp() > SystemProperties.getLong(Constants.PROP_BUILD_DATE, 0));
+ update.getTimestamp() > SystemProperties.getLong(Constants.PROP_BUILD_DATE, 0)) &&
+ (update.getUpgradeMinVersion() == null || update.getUpgradeMinVersion().length() == 0 ||
+ Version.parse(SystemProperties.get(Constants.PROP_BUILD_VERSION))
+ .compareTo(Version.parse(update.getUpgradeMinVersion())) >= 0);
}
public static List<UpdateInfo> parseJson(File file, boolean compatibleOnly)
@@ -154,6 +159,7 @@
String device = SystemProperties.get(Constants.PROP_NEXT_DEVICE,
SystemProperties.get(Constants.PROP_DEVICE));
String type = SystemProperties.get(Constants.PROP_RELEASE_TYPE).toLowerCase(Locale.ROOT);
+ String flavor = SystemProperties.get(Constants.PROP_FLAVOR);
String serverUrl = SystemProperties.get(Constants.PROP_UPDATER_URI);
if (serverUrl.trim().isEmpty()) {
@@ -162,7 +168,8 @@
return serverUrl.replace("{device}", device)
.replace("{type}", type)
- .replace("{incr}", incrementalVersion);
+ .replace("{incr}", incrementalVersion)
+ .replace("{flavor}", flavor);
}
public static String getUpgradeBlockedURL(Context context) {
diff --git a/app/src/main/java/org/lineageos/updater/misc/Version.java b/app/src/main/java/org/lineageos/updater/misc/Version.java
new file mode 100644
index 0000000..2ce1b15
--- /dev/null
+++ b/app/src/main/java/org/lineageos/updater/misc/Version.java
@@ -0,0 +1,303 @@
+package org.lineageos.updater.misc;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A module's version string.
+ *
+ * <p> A version string has three components: The version number itself, an
+ * optional pre-release version, and an optional build version. Each
+ * component is a sequence of tokens; each token is either a non-negative
+ * integer or a string. Tokens are separated by the punctuation characters
+ * {@code '.'}, {@code '-'}, or {@code '+'}, or by transitions from a
+ * sequence of digits to a sequence of characters that are neither digits
+ * nor punctuation characters, or vice versa. Consecutive repeated
+ * punctuation characters are treated as a single punctuation character.
+ *
+ * <ul>
+ *
+ * <li> The <i>version number</i> is a sequence of tokens separated by
+ * {@code '.'} characters, terminated by the first {@code '-'} or {@code
+ * '+'} character. </li>
+ *
+ * <li> The <i>pre-release version</i> is a sequence of tokens separated
+ * by {@code '.'} or {@code '-'} characters, terminated by the first
+ * {@code '+'} character. </li>
+ *
+ * <li> The <i>build version</i> is a sequence of tokens separated by
+ * {@code '.'}, {@code '-'}, or {@code '+'} characters.
+ *
+ * </ul>
+ *
+ * <p> When comparing two version strings, the elements of their
+ * corresponding components are compared in pointwise fashion. If one
+ * component is longer than the other, but otherwise equal to it, then the
+ * first component is considered the greater of the two; otherwise, if two
+ * corresponding elements are integers then they are compared as such;
+ * otherwise, at least one of the elements is a string, so the other is
+ * converted into a string if it is an integer and the two are compared
+ * lexicographically. Trailing integer elements with the value zero are
+ * ignored.
+ *
+ * <p> Given two version strings, if their version numbers differ then the
+ * result of comparing them is the result of comparing their version
+ * numbers; otherwise, if one of them has a pre-release version but the
+ * other does not then the first is considered to precede the second,
+ * otherwise the result of comparing them is the result of comparing their
+ * pre-release versions; otherwise, the result of comparing them is the
+ * result of comparing their build versions.
+ *
+ * @see ModuleDescriptor#version()
+ * @since 9
+ */
+
+public final class Version implements Comparable<Version> {
+
+ private final String version;
+
+ // If Java had disjunctive types then we'd write List<Integer|String> here
+ //
+ private final List<Object> sequence;
+ private final List<Object> pre;
+ private final List<Object> build;
+
+ // Take a numeric token starting at position i
+ // Append it to the given list
+ // Return the index of the first character not taken
+ // Requires: s.charAt(i) is (decimal) numeric
+ //
+ private static int takeNumber(String s, int i, List<Object> acc) {
+ char c = s.charAt(i);
+ int d = (c - '0');
+ int n = s.length();
+ while (++i < n) {
+ c = s.charAt(i);
+ if (c >= '0' && c <= '9') {
+ d = d * 10 + (c - '0');
+ continue;
+ }
+ break;
+ }
+ acc.add(d);
+ return i;
+ }
+
+ // Take a string token starting at position i
+ // Append it to the given list
+ // Return the index of the first character not taken
+ // Requires: s.charAt(i) is not '.'
+ //
+ private static int takeString(String s, int i, List<Object> acc) {
+ int b = i;
+ int n = s.length();
+ while (++i < n) {
+ char c = s.charAt(i);
+ if (c != '.' && c != '-' && c != '+' && !(c >= '0' && c <= '9'))
+ continue;
+ break;
+ }
+ acc.add(s.substring(b, i));
+ return i;
+ }
+
+ // Syntax: tok+ ( '-' tok+)? ( '+' tok+)?
+ // First token string is sequence, second is pre, third is build
+ // Tokens are separated by '.' or '-', or by changes between alpha & numeric
+ // Numeric tokens are compared as decimal integers
+ // Non-numeric tokens are compared lexicographically
+ // A version with a non-empty pre is less than a version with same seq but no pre
+ // Tokens in build may contain '-' and '+'
+ //
+ private Version(String v) {
+
+ if (v == null)
+ throw new IllegalArgumentException("Null version string");
+ int n = v.length();
+ if (n == 0)
+ throw new IllegalArgumentException("Empty version string");
+
+ int i = 0;
+ char c = v.charAt(i);
+ if (!(c >= '0' && c <= '9'))
+ throw new IllegalArgumentException(v
+ + ": Version string does not start"
+ + " with a number");
+
+ List<Object> sequence = new ArrayList<>(4);
+ List<Object> pre = new ArrayList<>(2);
+ List<Object> build = new ArrayList<>(2);
+
+ i = takeNumber(v, i, sequence);
+
+ while (i < n) {
+ c = v.charAt(i);
+ if (c == '.') {
+ i++;
+ continue;
+ }
+ if (c == '-' || c == '+') {
+ i++;
+ break;
+ }
+ if (c >= '0' && c <= '9')
+ i = takeNumber(v, i, sequence);
+ else
+ i = takeString(v, i, sequence);
+ }
+
+ if (c == '-' && i >= n)
+ throw new IllegalArgumentException(v + ": Empty pre-release");
+
+ while (i < n) {
+ c = v.charAt(i);
+ if (c == '.' || c == '-') {
+ i++;
+ continue;
+ }
+ if (c == '+') {
+ i++;
+ break;
+ }
+ if (c >= '0' && c <= '9')
+ i = takeNumber(v, i, pre);
+ else
+ i = takeString(v, i, pre);
+ }
+
+ if (c == '+' && i >= n)
+ throw new IllegalArgumentException(v + ": Empty pre-release");
+
+ while (i < n) {
+ c = v.charAt(i);
+ if (c == '.' || c == '-' || c == '+') {
+ i++;
+ continue;
+ }
+ if (c >= '0' && c <= '9')
+ i = takeNumber(v, i, build);
+ else
+ i = takeString(v, i, build);
+ }
+
+ this.version = v;
+ this.sequence = sequence;
+ this.pre = pre;
+ this.build = build;
+ }
+
+ /**
+ * Parses the given string as a version string.
+ *
+ * @param v The string to parse
+ * @return The resulting {@code Version}
+ * @throws IllegalArgumentException If {@code v} is {@code null}, an empty string, or cannot be
+ * parsed as a version string
+ */
+ public static Version parse(String v) {
+ return new Version(v);
+ }
+
+ @SuppressWarnings("unchecked")
+ private int cmp(Object o1, Object o2) {
+ return ((Comparable) o1).compareTo(o2);
+ }
+
+ private int compareTokens(List<Object> ts1, List<Object> ts2) {
+ int n = Math.min(ts1.size(), ts2.size());
+ for (int i = 0; i < n; i++) {
+ Object o1 = ts1.get(i);
+ Object o2 = ts2.get(i);
+ if ((o1 instanceof Integer && o2 instanceof Integer)
+ || (o1 instanceof String && o2 instanceof String)) {
+ int c = cmp(o1, o2);
+ if (c == 0)
+ continue;
+ return c;
+ }
+ // Types differ, so convert number to string form
+ int c = o1.toString().compareTo(o2.toString());
+ if (c == 0)
+ continue;
+ return c;
+ }
+ List<Object> rest = ts1.size() > ts2.size() ? ts1 : ts2;
+ int e = rest.size();
+ for (int i = n; i < e; i++) {
+ Object o = rest.get(i);
+ if (o instanceof Integer && ((Integer) o) == 0)
+ continue;
+ return ts1.size() - ts2.size();
+ }
+ return 0;
+ }
+
+ /**
+ * Compares this module version to another module version. Module
+ * versions are compared as described in the class description.
+ *
+ * @param that The module version to compare
+ * @return A negative integer, zero, or a positive integer as this
+ * module version is less than, equal to, or greater than the
+ * given module version
+ */
+ @Override
+ public int compareTo(Version that) {
+ int c = compareTokens(this.sequence, that.sequence);
+ if (c != 0) return c;
+ if (this.pre.isEmpty()) {
+ if (!that.pre.isEmpty()) return +1;
+ } else {
+ if (that.pre.isEmpty()) return -1;
+ }
+ c = compareTokens(this.pre, that.pre);
+ if (c != 0) return c;
+ return compareTokens(this.build, that.build);
+ }
+
+ /**
+ * Tests this module version for equality with the given object.
+ *
+ * <p> If the given object is not a {@code Version} then this method
+ * returns {@code false}. Two module version are equal if their
+ * corresponding components are equal. </p>
+ *
+ * <p> This method satisfies the general contract of the {@link
+ * java.lang.Object#equals(Object) Object.equals} method. </p>
+ *
+ * @param ob the object to which this object is to be compared
+ * @return {@code true} if, and only if, the given object is a module
+ * reference that is equal to this module reference
+ */
+ @Override
+ public boolean equals(Object ob) {
+ if (!(ob instanceof Version))
+ return false;
+ return compareTo((Version) ob) == 0;
+ }
+
+ /**
+ * Computes a hash code for this module version.
+ *
+ * <p> The hash code is based upon the components of the version and
+ * satisfies the general contract of the {@link Object#hashCode
+ * Object.hashCode} method. </p>
+ *
+ * @return The hash-code value for this module version
+ */
+ @Override
+ public int hashCode() {
+ return version.hashCode();
+ }
+
+ /**
+ * Returns the string from which this version was parsed.
+ *
+ * @return The string from which this version was parsed.
+ */
+ @Override
+ public String toString() {
+ return version;
+ }
+
+}
diff --git a/app/src/main/java/org/lineageos/updater/model/UpdateBase.java b/app/src/main/java/org/lineageos/updater/model/UpdateBase.java
index 8fcf09c..2074159 100644
--- a/app/src/main/java/org/lineageos/updater/model/UpdateBase.java
+++ b/app/src/main/java/org/lineageos/updater/model/UpdateBase.java
@@ -24,6 +24,7 @@
private String mType;
private String mVersion;
private long mFileSize;
+ private String mUpgradeMinVersion;
public UpdateBase() {
}
@@ -36,6 +37,7 @@
mType = update.getType();
mVersion = update.getVersion();
mFileSize = update.getFileSize();
+ mUpgradeMinVersion = update.getUpgradeMinVersion();
}
@Override
@@ -100,4 +102,13 @@
public void setFileSize(long fileSize) {
mFileSize = fileSize;
}
+
+ @Override
+ public String getUpgradeMinVersion() {
+ return mUpgradeMinVersion;
+ }
+
+ public void setUpgradeMinVersion(String upgradeMinVersion) {
+ mUpgradeMinVersion = upgradeMinVersion;
+ }
}
diff --git a/app/src/main/java/org/lineageos/updater/model/UpdateBaseInfo.java b/app/src/main/java/org/lineageos/updater/model/UpdateBaseInfo.java
index 2041582..417d470 100644
--- a/app/src/main/java/org/lineageos/updater/model/UpdateBaseInfo.java
+++ b/app/src/main/java/org/lineageos/updater/model/UpdateBaseInfo.java
@@ -29,4 +29,6 @@
String getDownloadUrl();
long getFileSize();
+
+ String getUpgradeMinVersion();
}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index c02fe1c..83b6a3c 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -20,7 +20,7 @@
<!-- Directory where the update files will be downloaded and stored.
WARNING: The application can and will delete any unknown file. -->
- <string name="download_path" translatable="false">/data/lineageos_updates/</string>
+ <string name="download_path" translatable="false">/data/ota_package/</string>
<!-- Directory where the downloads will be exported to.
The path is relative to the root of the external storage.-->
@@ -32,7 +32,7 @@
{type} - Build type
{incr} - Incremental version
-->
- <string name="updater_server_url" translatable="false">https://download.lineageos.org/api/v1/{device}/{type}/{incr}</string>
+ <string name="updater_server_url" translatable="false">https://get.leafos.org/ota/{device}/{flavor}/{incr}</string>
<string name="verification_failed_notification">Verification failed</string>
<string name="verifying_download_notification">Verifying update</string>
@@ -147,7 +147,7 @@
<string name="blocked_update_dialog_title">Update blocked</string>
<string name="blocked_update_dialog_message">This update cannot be installed using the updater app. Please read <xliff:g id="info_url">%1$s</xliff:g> for more information.</string>
- <string name="blocked_update_info_url" translatable="false">http://wiki.lineageos.org/devices/<xliff:g id="device_name">%1$s</xliff:g>/upgrade</string>
+ <string name="blocked_update_info_url" translatable="false">http://leafos.org/wiki/device/<xliff:g id="device_name">%1$s</xliff:g>/upgrade</string>
<string name="export_channel_title">Export completion</string>
<string name="new_updates_channel_title">New updates</string>
diff --git a/push-update.sh b/push-update.sh
index d92aacc..3383d83 100755
--- a/push-update.sh
+++ b/push-update.sh
@@ -1,6 +1,6 @@
#!/bin/sh
-updates_dir=/data/lineageos_updates
+updates_dir=/data/ota_package
if [ ! -f "$1" ]; then
echo "Usage: $0 ZIP [UNVERIFIED]"