Merge branch 'fix-installer-NPE' into 'master'

Make App and Apk parcelable and fix related installer NPEs

Installations fails currently due to
```
  885                    ACRA  E  Caused by: java.lang.NullPointerException
  885                    ACRA  E  at org.fdroid.fdroid.installer.InstallManagerService.getAppName(InstallManagerService.java:327)
  885                    ACRA  E  at org.fdroid.fdroid.installer.InstallManagerService.createNotificationBuilder(InstallManagerService.java:318)
  885                    ACRA  E  at org.fdroid.fdroid.installer.InstallManagerService.onStartCommand(InstallManagerService.java:158)
  885                    ACRA  E  at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:2039)
  885                    ACRA  E  ... 10 more
```

This bug has been introduced in https://gitlab.com/fdroid/fdroidclient/merge_requests/359 where the packageName has been removed from the toContentValues() method.

The usage of ContentValues to send App/Apk objects to services was an hack in my opinion.
Thus, this PRs introduces proper parceling of App and Apk classes.

@pserwylo @eighthave @mvdan 

See merge request !362
This commit is contained in:
Peter Serwylo 2016-07-26 21:27:53 +00:00
commit f4de353900
4 changed files with 176 additions and 18 deletions

View File

@ -4,6 +4,7 @@ import android.annotation.TargetApi;
import android.content.ContentValues; import android.content.ContentValues;
import android.database.Cursor; import android.database.Cursor;
import android.os.Build; import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.Utils;
@ -13,7 +14,7 @@ import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.HashSet; import java.util.HashSet;
public class Apk extends ValueObject implements Comparable<Apk> { public class Apk extends ValueObject implements Comparable<Apk>, Parcelable {
// Using only byte-range keeps it only 8-bits in the SQLite database // Using only byte-range keeps it only 8-bits in the SQLite database
public static final int SDK_VERSION_MAX_VALUE = Byte.MAX_VALUE; public static final int SDK_VERSION_MAX_VALUE = Byte.MAX_VALUE;
@ -69,10 +70,6 @@ public class Apk extends ValueObject implements Comparable<Apk> {
public Apk() { public Apk() {
} }
public Apk(Parcelable parcelable) {
this(new ContentValuesCursor((ContentValues) parcelable));
}
public Apk(Cursor cursor) { public Apk(Cursor cursor) {
checkCursorPosition(cursor); checkCursorPosition(cursor);
@ -231,4 +228,74 @@ public class Apk extends ValueObject implements Comparable<Apk> {
return Integer.compare(versionCode, apk.versionCode); return Integer.compare(versionCode, apk.versionCode);
} }
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.packageName);
dest.writeString(this.versionName);
dest.writeInt(this.versionCode);
dest.writeInt(this.size);
dest.writeLong(this.repo);
dest.writeString(this.hash);
dest.writeString(this.hashType);
dest.writeInt(this.minSdkVersion);
dest.writeInt(this.targetSdkVersion);
dest.writeInt(this.maxSdkVersion);
dest.writeLong(this.added != null ? this.added.getTime() : -1);
dest.writeStringArray(this.permissions);
dest.writeStringArray(this.features);
dest.writeStringArray(this.nativecode);
dest.writeString(this.sig);
dest.writeByte(this.compatible ? (byte) 1 : (byte) 0);
dest.writeString(this.apkName);
dest.writeSerializable(this.installedFile);
dest.writeString(this.srcname);
dest.writeInt(this.repoVersion);
dest.writeString(this.repoAddress);
dest.writeStringArray(this.incompatibleReasons);
dest.writeLong(this.appId);
}
protected Apk(Parcel in) {
this.packageName = in.readString();
this.versionName = in.readString();
this.versionCode = in.readInt();
this.size = in.readInt();
this.repo = in.readLong();
this.hash = in.readString();
this.hashType = in.readString();
this.minSdkVersion = in.readInt();
this.targetSdkVersion = in.readInt();
this.maxSdkVersion = in.readInt();
long tmpAdded = in.readLong();
this.added = tmpAdded == -1 ? null : new Date(tmpAdded);
this.permissions = in.createStringArray();
this.features = in.createStringArray();
this.nativecode = in.createStringArray();
this.sig = in.readString();
this.compatible = in.readByte() != 0;
this.apkName = in.readString();
this.installedFile = (SanitizedFile) in.readSerializable();
this.srcname = in.readString();
this.repoVersion = in.readInt();
this.repoAddress = in.readString();
this.incompatibleReasons = in.createStringArray();
this.appId = in.readLong();
}
public static final Parcelable.Creator<Apk> CREATOR = new Parcelable.Creator<Apk>() {
@Override
public Apk createFromParcel(Parcel source) {
return new Apk(source);
}
@Override
public Apk[] newArray(int size) {
return new Apk[size];
}
};
} }

View File

@ -9,6 +9,7 @@ import android.content.pm.PackageManager;
import android.content.res.AssetManager; import android.content.res.AssetManager;
import android.content.res.XmlResourceParser; import android.content.res.XmlResourceParser;
import android.database.Cursor; import android.database.Cursor;
import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
@ -34,7 +35,7 @@ import java.util.regex.Pattern;
import org.fdroid.fdroid.data.Schema.AppTable.Cols; import org.fdroid.fdroid.data.Schema.AppTable.Cols;
public class App extends ValueObject implements Comparable<App> { public class App extends ValueObject implements Comparable<App>, Parcelable {
private static final String TAG = "App"; private static final String TAG = "App";
@ -144,10 +145,6 @@ public class App extends ValueObject implements Comparable<App> {
public App() { public App() {
} }
public App(Parcelable parcelable) {
this(new ContentValuesCursor((ContentValues) parcelable));
}
public App(Cursor cursor) { public App(Cursor cursor) {
checkCursorPosition(cursor); checkCursorPosition(cursor);
@ -560,4 +557,100 @@ public class App extends ValueObject implements Comparable<App> {
public long getId() { public long getId() {
return id; return id;
} }
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeByte(this.compatible ? (byte) 1 : (byte) 0);
dest.writeString(this.packageName);
dest.writeString(this.name);
dest.writeString(this.summary);
dest.writeString(this.icon);
dest.writeString(this.description);
dest.writeString(this.license);
dest.writeString(this.author);
dest.writeString(this.email);
dest.writeString(this.webURL);
dest.writeString(this.trackerURL);
dest.writeString(this.sourceURL);
dest.writeString(this.changelogURL);
dest.writeString(this.donateURL);
dest.writeString(this.bitcoinAddr);
dest.writeString(this.litecoinAddr);
dest.writeString(this.flattrID);
dest.writeString(this.upstreamVersionName);
dest.writeInt(this.upstreamVersionCode);
dest.writeString(this.suggestedVersionName);
dest.writeInt(this.suggestedVersionCode);
dest.writeLong(this.added != null ? this.added.getTime() : -1);
dest.writeLong(this.lastUpdated != null ? this.lastUpdated.getTime() : -1);
dest.writeStringArray(this.categories);
dest.writeStringArray(this.antiFeatures);
dest.writeStringArray(this.requirements);
dest.writeByte(this.ignoreAllUpdates ? (byte) 1 : (byte) 0);
dest.writeInt(this.ignoreThisUpdate);
dest.writeString(this.iconUrl);
dest.writeString(this.iconUrlLarge);
dest.writeString(this.installedVersionName);
dest.writeInt(this.installedVersionCode);
dest.writeParcelable(this.installedApk, flags);
dest.writeString(this.installedSig);
dest.writeLong(this.id);
}
protected App(Parcel in) {
this.compatible = in.readByte() != 0;
this.packageName = in.readString();
this.name = in.readString();
this.summary = in.readString();
this.icon = in.readString();
this.description = in.readString();
this.license = in.readString();
this.author = in.readString();
this.email = in.readString();
this.webURL = in.readString();
this.trackerURL = in.readString();
this.sourceURL = in.readString();
this.changelogURL = in.readString();
this.donateURL = in.readString();
this.bitcoinAddr = in.readString();
this.litecoinAddr = in.readString();
this.flattrID = in.readString();
this.upstreamVersionName = in.readString();
this.upstreamVersionCode = in.readInt();
this.suggestedVersionName = in.readString();
this.suggestedVersionCode = in.readInt();
long tmpAdded = in.readLong();
this.added = tmpAdded == -1 ? null : new Date(tmpAdded);
long tmpLastUpdated = in.readLong();
this.lastUpdated = tmpLastUpdated == -1 ? null : new Date(tmpLastUpdated);
this.categories = in.createStringArray();
this.antiFeatures = in.createStringArray();
this.requirements = in.createStringArray();
this.ignoreAllUpdates = in.readByte() != 0;
this.ignoreThisUpdate = in.readInt();
this.iconUrl = in.readString();
this.iconUrlLarge = in.readString();
this.installedVersionName = in.readString();
this.installedVersionCode = in.readInt();
this.installedApk = in.readParcelable(Apk.class.getClassLoader());
this.installedSig = in.readString();
this.id = in.readLong();
}
public static final Parcelable.Creator<App> CREATOR = new Parcelable.Creator<App>() {
@Override
public App createFromParcel(Parcel source) {
return new App(source);
}
@Override
public App[] newArray(int size) {
return new App[size];
}
};
} }

View File

@ -151,8 +151,8 @@ public class InstallManagerService extends Service {
return START_NOT_STICKY; return START_NOT_STICKY;
} }
App app = new App(intent.getParcelableExtra(EXTRA_APP)); App app = intent.getParcelableExtra(EXTRA_APP);
Apk apk = new Apk(intent.getParcelableExtra(EXTRA_APK)); Apk apk = intent.getParcelableExtra(EXTRA_APK);
addToActive(urlString, app, apk); addToActive(urlString, app, apk);
NotificationCompat.Builder builder = createNotificationBuilder(urlString, apk); NotificationCompat.Builder builder = createNotificationBuilder(urlString, apk);
@ -454,8 +454,8 @@ public class InstallManagerService extends Service {
Intent intent = new Intent(context, InstallManagerService.class); Intent intent = new Intent(context, InstallManagerService.class);
intent.setAction(ACTION_INSTALL); intent.setAction(ACTION_INSTALL);
intent.setData(Uri.parse(urlString)); intent.setData(Uri.parse(urlString));
intent.putExtra(EXTRA_APP, app.toContentValues()); intent.putExtra(EXTRA_APP, app);
intent.putExtra(EXTRA_APK, apk.toContentValues()); intent.putExtra(EXTRA_APK, apk);
context.startService(intent); context.startService(intent);
} }

View File

@ -23,7 +23,6 @@ import android.app.IntentService;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.os.Parcelable;
import org.fdroid.fdroid.data.Apk; import org.fdroid.fdroid.data.Apk;
@ -52,8 +51,7 @@ public class InstallerService extends IntentService {
@Override @Override
protected void onHandleIntent(Intent intent) { protected void onHandleIntent(Intent intent) {
Parcelable apkParcel = intent.getParcelableExtra(Installer.EXTRA_APK); Apk apk = intent.getParcelableExtra(Installer.EXTRA_APK);
Apk apk = apkParcel == null ? null : new Apk(apkParcel);
Installer installer = InstallerFactory.create(this, apk); Installer installer = InstallerFactory.create(this, apk);
@ -80,7 +78,7 @@ public class InstallerService extends IntentService {
intent.setAction(ACTION_INSTALL); intent.setAction(ACTION_INSTALL);
intent.setData(localApkUri); intent.setData(localApkUri);
intent.putExtra(Installer.EXTRA_DOWNLOAD_URI, downloadUri); intent.putExtra(Installer.EXTRA_DOWNLOAD_URI, downloadUri);
intent.putExtra(Installer.EXTRA_APK, apk.toContentValues()); intent.putExtra(Installer.EXTRA_APK, apk);
context.startService(intent); context.startService(intent);
} }