From 601c85103ecb04eeab435b36b56da5a96df618c9 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Mon, 27 Feb 2017 14:23:14 +0100 Subject: [PATCH] fields as instance vars: icon, mirrors for Repo; antiFeatures for Apk This adds support for the index fields: icon, mirrors, and antiFeatures. icon and mirrors are for Repo, they've been around a while on the server side, but just never used on the client side. For Apk, this adds a new per-APK antiFeatures field so that each APK can be individually marked. For example, when tracking is added or removed, vulnerabilities are discovered and fixed, etc. These fields will be ignored when using the v0 index.xml format, they will be used by the upcoming index-v1 format: !422 --- .../java/org/fdroid/fdroid/RepoUpdater.java | 19 +++++++++++--- .../org/fdroid/fdroid/RepoXMLHandler.java | 11 ++++++-- .../main/java/org/fdroid/fdroid/data/Apk.java | 9 +++++++ .../java/org/fdroid/fdroid/data/DBHelper.java | 26 ++++++++++++++++++- .../java/org/fdroid/fdroid/data/Repo.java | 18 +++++++++++++ .../java/org/fdroid/fdroid/data/Schema.java | 8 ++++-- .../org/fdroid/fdroid/mock/RepoDetails.java | 7 ++++- 7 files changed, 89 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/org/fdroid/fdroid/RepoUpdater.java b/app/src/main/java/org/fdroid/fdroid/RepoUpdater.java index 3c47c485f..a29899c89 100644 --- a/app/src/main/java/org/fdroid/fdroid/RepoUpdater.java +++ b/app/src/main/java/org/fdroid/fdroid/RepoUpdater.java @@ -58,6 +58,7 @@ import java.security.CodeSigner; import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.util.ArrayList; +import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.jar.JarEntry; @@ -195,9 +196,11 @@ public class RepoUpdater { private RepoXMLHandler.IndexReceiver createIndexReceiver() { return new RepoXMLHandler.IndexReceiver() { @Override - public void receiveRepo(String name, String description, String signingCert, int maxAge, int version, long timestamp) { + public void receiveRepo(String name, String description, String signingCert, int maxAge, + int version, long timestamp, String icon, String[] mirrors) { signingCertFromIndexXml = signingCert; - repoDetailsToSave = prepareRepoDetailsForSaving(name, description, maxAge, version, timestamp); + repoDetailsToSave = prepareRepoDetailsForSaving(name, description, maxAge, version, + timestamp, icon, mirrors); } @Override @@ -294,7 +297,9 @@ public class RepoUpdater { * Update tracking data for the repo represented by this instance (index version, etag, * description, human-readable name, etc. */ - private ContentValues prepareRepoDetailsForSaving(String name, String description, int maxAge, int version, long timestamp) { + private ContentValues prepareRepoDetailsForSaving(String name, String description, int maxAge, + int version, long timestamp, String icon, + String[] mirrors) { ContentValues values = new ContentValues(); values.put(RepoTable.Cols.LAST_UPDATED, Utils.formatTime(new Date(), "")); @@ -329,6 +334,14 @@ public class RepoUpdater { // timestamp. values.put(RepoTable.Cols.TIMESTAMP, timestamp); + if (icon != null && !icon.equals(repo.icon)) { + values.put(RepoTable.Cols.ICON, icon); + } + + if (mirrors != null && mirrors.length > 0 && !Arrays.equals(mirrors, repo.mirrors)) { + values.put(RepoTable.Cols.MIRRORS, Utils.serializeCommaSeparatedString(mirrors)); + } + return values; } diff --git a/app/src/main/java/org/fdroid/fdroid/RepoXMLHandler.java b/app/src/main/java/org/fdroid/fdroid/RepoXMLHandler.java index e822348c7..345b0d890 100644 --- a/app/src/main/java/org/fdroid/fdroid/RepoXMLHandler.java +++ b/app/src/main/java/org/fdroid/fdroid/RepoXMLHandler.java @@ -59,6 +59,8 @@ public class RepoXMLHandler extends DefaultHandler { private long repoTimestamp; private String repoDescription; private String repoName; + private String repoIcon; + private final ArrayList repoMirrors = new ArrayList<>(); /** * Set of requested permissions per package/APK @@ -73,7 +75,8 @@ public class RepoXMLHandler extends DefaultHandler { private final StringBuilder curchars = new StringBuilder(); public interface IndexReceiver { - void receiveRepo(String name, String description, String signingCert, int maxage, int version, long timestamp); + void receiveRepo(String name, String description, String signingCert, int maxage, int version, + long timestamp, String icon, String[] mirrors); void receiveApp(App app, List packages); @@ -258,6 +261,8 @@ public class RepoXMLHandler extends DefaultHandler { } } else if ("description".equals(localName)) { repoDescription = cleanWhiteSpace(str); + } else if ("mirror".equals(localName)) { + repoMirrors.add(str); } } @@ -312,7 +317,8 @@ public class RepoXMLHandler extends DefaultHandler { } private void onRepoParsed() { - receiver.receiveRepo(repoName, repoDescription, repoSigningCert, repoMaxAge, repoVersion, repoTimestamp); + receiver.receiveRepo(repoName, repoDescription, repoSigningCert, repoMaxAge, repoVersion, + repoTimestamp, repoIcon, repoMirrors.toArray(new String[repoMirrors.size()])); } private void onRepoPushRequestParsed(RepoPushRequest repoPushRequest) { @@ -331,6 +337,7 @@ public class RepoXMLHandler extends DefaultHandler { repoName = cleanWhiteSpace(attributes.getValue("", "name")); repoDescription = cleanWhiteSpace(attributes.getValue("", "description")); repoTimestamp = parseLong(attributes.getValue("", "timestamp"), 0); + repoIcon = attributes.getValue("", "icon"); } else if (RepoPushRequest.INSTALL.equals(localName) || RepoPushRequest.UNINSTALL.equals(localName)) { if (repo.pushRequests == Repo.PUSH_REQUEST_ACCEPT_ALWAYS) { diff --git a/app/src/main/java/org/fdroid/fdroid/data/Apk.java b/app/src/main/java/org/fdroid/fdroid/data/Apk.java index d00250966..d1f75ff89 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/Apk.java +++ b/app/src/main/java/org/fdroid/fdroid/data/Apk.java @@ -2,6 +2,7 @@ package org.fdroid.fdroid.data; import android.annotation.TargetApi; import android.content.ContentValues; +import android.content.pm.PackageInfo; import android.database.Cursor; import android.os.Build; import android.os.Parcel; @@ -73,6 +74,8 @@ public class Apk extends ValueObject implements Comparable, Parcelable { public String repoAddress; public String[] incompatibleReasons; + public String[] antiFeatures; + /** * The numeric primary key of the Metadata table, which is used to join apks. */ @@ -203,6 +206,9 @@ public class Apk extends ValueObject implements Comparable, Parcelable { case Cols.Repo.ADDRESS: repoAddress = cursor.getString(i); break; + case Cols.ANTI_FEATURES: + antiFeatures = Utils.parseCommaSeparatedString(cursor.getString(i)); + break; } } } @@ -303,6 +309,7 @@ public class Apk extends ValueObject implements Comparable, Parcelable { values.put(Cols.FEATURES, Utils.serializeCommaSeparatedString(features)); values.put(Cols.NATIVE_CODE, Utils.serializeCommaSeparatedString(nativecode)); values.put(Cols.INCOMPATIBLE_REASONS, Utils.serializeCommaSeparatedString(incompatibleReasons)); + values.put(Cols.ANTI_FEATURES, Utils.serializeCommaSeparatedString(antiFeatures)); values.put(Cols.IS_COMPATIBLE, compatible ? 1 : 0); return values; } @@ -349,6 +356,7 @@ public class Apk extends ValueObject implements Comparable, Parcelable { dest.writeInt(this.repoVersion); dest.writeString(this.repoAddress); dest.writeStringArray(this.incompatibleReasons); + dest.writeStringArray(this.antiFeatures); dest.writeLong(this.appId); } @@ -380,6 +388,7 @@ public class Apk extends ValueObject implements Comparable, Parcelable { this.repoVersion = in.readInt(); this.repoAddress = in.readString(); this.incompatibleReasons = in.createStringArray(); + this.antiFeatures = in.createStringArray(); this.appId = in.readLong(); } diff --git a/app/src/main/java/org/fdroid/fdroid/data/DBHelper.java b/app/src/main/java/org/fdroid/fdroid/data/DBHelper.java index 14c10abb5..76a4f9312 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/DBHelper.java +++ b/app/src/main/java/org/fdroid/fdroid/data/DBHelper.java @@ -76,6 +76,8 @@ class DBHelper extends SQLiteOpenHelper { + RepoTable.Cols.USERNAME + " string, " + RepoTable.Cols.PASSWORD + " string," + RepoTable.Cols.TIMESTAMP + " integer not null default 0, " + + RepoTable.Cols.ICON + " string, " + + RepoTable.Cols.MIRRORS + " string, " + RepoTable.Cols.PUSH_REQUESTS + " integer not null default " + Repo.PUSH_REQUEST_IGNORE + ");"; @@ -104,6 +106,7 @@ class DBHelper extends SQLiteOpenHelper { + ApkTable.Cols.ADDED_DATE + " string, " + ApkTable.Cols.IS_COMPATIBLE + " int not null, " + ApkTable.Cols.INCOMPATIBLE_REASONS + " text, " + + ApkTable.Cols.ANTI_FEATURES + " string, " + "PRIMARY KEY (" + ApkTable.Cols.APP_ID + ", " + ApkTable.Cols.VERSION_CODE + ", " + ApkTable.Cols.REPO_ID + ")" + ");"; @@ -178,7 +181,7 @@ class DBHelper extends SQLiteOpenHelper { + InstalledAppTable.Cols.HASH + " TEXT NOT NULL" + " );"; - protected static final int DB_VERSION = 65; + protected static final int DB_VERSION = 66; private final Context context; @@ -258,6 +261,27 @@ class DBHelper extends SQLiteOpenHelper { migrateToPackageTable(db, oldVersion); addObbFiles(db, oldVersion); addCategoryTables(db, oldVersion); + addIndexV1Fields(db, oldVersion); + } + + private void addIndexV1Fields(SQLiteDatabase db, int oldVersion) { + if (oldVersion >= 66) { + return; + } + if (!columnExists(db, Schema.RepoTable.NAME, RepoTable.Cols.ICON)) { + Utils.debugLog(TAG, "Adding " + RepoTable.Cols.ICON + " field to " + RepoTable.NAME + " table in db."); + db.execSQL("alter table " + RepoTable.NAME + " add column " + RepoTable.Cols.ICON + " string;"); + } + + if (!columnExists(db, RepoTable.NAME, RepoTable.Cols.MIRRORS)) { + Utils.debugLog(TAG, "Adding " + RepoTable.Cols.MIRRORS + " field to " + RepoTable.NAME + " table in db."); + db.execSQL("alter table " + RepoTable.NAME + " add column " + RepoTable.Cols.MIRRORS + " string;"); + } + + if (!columnExists(db, ApkTable.NAME, ApkTable.Cols.ANTI_FEATURES)) { + Utils.debugLog(TAG, "Adding " + ApkTable.Cols.ANTI_FEATURES + " field to " + ApkTable.NAME + " table in db."); + db.execSQL("alter table " + ApkTable.NAME + " add column " + ApkTable.Cols.ANTI_FEATURES + " string;"); + } } /** diff --git a/app/src/main/java/org/fdroid/fdroid/data/Repo.java b/app/src/main/java/org/fdroid/fdroid/data/Repo.java index fa8289bdf..589c5d0ce 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/Repo.java +++ b/app/src/main/java/org/fdroid/fdroid/data/Repo.java @@ -47,6 +47,7 @@ public class Repo extends ValueObject { public String address; public String name; public String description; + public String icon; /** index version, i.e. what fdroidserver built it - 0 if not specified */ public int version; public boolean inuse; @@ -71,6 +72,9 @@ public class Repo extends ValueObject { /** When the signed repo index was generated, used to protect against replay attacks */ public long timestamp; + /** Official mirrors of this repo, considered automatically interchangeable */ + public String[] mirrors; + /** How to treat push requests included in this repo's index XML */ public int pushRequests = PUSH_REQUEST_IGNORE; @@ -131,6 +135,12 @@ public class Repo extends ValueObject { case Cols.TIMESTAMP: timestamp = cursor.getLong(i); break; + case Cols.ICON: + icon = cursor.getString(i); + break; + case Cols.MIRRORS: + mirrors = Utils.parseCommaSeparatedString(cursor.getString(i)); + break; case Cols.PUSH_REQUESTS: pushRequests = cursor.getInt(i); break; @@ -257,6 +267,14 @@ public class Repo extends ValueObject { timestamp = toInt(values.getAsInteger(Cols.TIMESTAMP)); } + if (values.containsKey(Cols.ICON)) { + icon = values.getAsString(Cols.ICON); + } + + if (values.containsKey(Cols.MIRRORS)) { + mirrors = Utils.parseCommaSeparatedString(values.getAsString(Cols.MIRRORS)); + } + if (values.containsKey(Cols.PUSH_REQUESTS)) { pushRequests = toInt(values.getAsInteger(Cols.PUSH_REQUESTS)); } diff --git a/app/src/main/java/org/fdroid/fdroid/data/Schema.java b/app/src/main/java/org/fdroid/fdroid/data/Schema.java index 54c44c407..6c0491fba 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/Schema.java +++ b/app/src/main/java/org/fdroid/fdroid/data/Schema.java @@ -240,6 +240,7 @@ public interface Schema { String ADDED_DATE = "added"; String IS_COMPATIBLE = "compatible"; String INCOMPATIBLE_REASONS = "incompatibleReasons"; + String ANTI_FEATURES = "antiFeatures"; interface Repo { String VERSION = "repoVersion"; @@ -258,7 +259,7 @@ public interface Schema { SIZE, SIGNATURE, SOURCE_NAME, MIN_SDK_VERSION, TARGET_SDK_VERSION, MAX_SDK_VERSION, OBB_MAIN_FILE, OBB_MAIN_FILE_SHA256, OBB_PATCH_FILE, OBB_PATCH_FILE_SHA256, REQUESTED_PERMISSIONS, FEATURES, NATIVE_CODE, HASH_TYPE, ADDED_DATE, - IS_COMPATIBLE, INCOMPATIBLE_REASONS, + IS_COMPATIBLE, INCOMPATIBLE_REASONS, ANTI_FEATURES, }; /** @@ -270,6 +271,7 @@ public interface Schema { OBB_MAIN_FILE, OBB_MAIN_FILE_SHA256, OBB_PATCH_FILE, OBB_PATCH_FILE_SHA256, REQUESTED_PERMISSIONS, FEATURES, NATIVE_CODE, HASH_TYPE, ADDED_DATE, IS_COMPATIBLE, Repo.VERSION, Repo.ADDRESS, INCOMPATIBLE_REASONS, + ANTI_FEATURES, }; } } @@ -295,12 +297,14 @@ public interface Schema { String USERNAME = "username"; String PASSWORD = "password"; String TIMESTAMP = "timestamp"; + String ICON = "icon"; + String MIRRORS = "mirrors"; String PUSH_REQUESTS = "pushRequests"; String[] ALL = { _ID, ADDRESS, NAME, DESCRIPTION, IN_USE, PRIORITY, SIGNING_CERT, FINGERPRINT, MAX_AGE, LAST_UPDATED, LAST_ETAG, VERSION, IS_SWAP, - USERNAME, PASSWORD, TIMESTAMP, PUSH_REQUESTS, + USERNAME, PASSWORD, TIMESTAMP, ICON, MIRRORS, PUSH_REQUESTS, }; } } diff --git a/app/src/testShared/java/org/fdroid/fdroid/mock/RepoDetails.java b/app/src/testShared/java/org/fdroid/fdroid/mock/RepoDetails.java index 4371bbaa4..5d8428a15 100644 --- a/app/src/testShared/java/org/fdroid/fdroid/mock/RepoDetails.java +++ b/app/src/testShared/java/org/fdroid/fdroid/mock/RepoDetails.java @@ -31,19 +31,24 @@ public class RepoDetails implements RepoXMLHandler.IndexReceiver { public int maxAge; public int version; public long timestamp; + public String icon; + public String[] mirrors; public List apks = new ArrayList<>(); public List apps = new ArrayList<>(); public List repoPushRequestList = new ArrayList<>(); @Override - public void receiveRepo(String name, String description, String signingCert, int maxage, int version, long timestamp) { + public void receiveRepo(String name, String description, String signingCert, int maxage, + int version, long timestamp, String icon, String[] mirrors) { this.name = name; this.description = description; this.signingCert = signingCert; this.maxAge = maxage; this.version = version; this.timestamp = timestamp; + this.icon = icon; + this.mirrors = mirrors; } @Override