Marc Poppleton
Marc Poppleton

Reputation: 108

Android ResolveInfo Parcelable not completely unparceled

I'm stumbling on what I would have expected to be an easy task, that is writing a ResolveInfo object to a Parcel and then creating a ResolveInfo from this Parcel. Sadly it is not as my code crashes and throws a java.lang.IllegalStateException.

Here is my code

fun failingFunction(resolveInfo:ResolveInfo){
    Log.d(TAG,"original label ${resolveInfo.loadLabel(this.packageManager)}")
    val p = Parcel.obtain()
    resolveInfo.writeToParcel(p, 0)
    val copy = ResolveInfo.CREATOR.createFromParcel(p)
    Log.d(TAG,"copy label ${copy.loadLabel(this.packageManager)}") // throws java.lang.IllegalStateException: Missing ComponentInfo!
}

Looking at the source code of the ResolveInfo class the exception is thrown when none of the ResolveInfo ActivityInfo, ServiceInfo or ProviderInfo attributes are present. I therefore added logs to check if these were missing.

fun failingFunction(resolveInfo:ResolveInfo){
    Log.d(TAG,"original activity info ${resolveInfo.activityInfo}")
    Log.d(TAG,"original service info ${resolveInfo.serviceInfo}")
    Log.d(TAG,"original provider info ${resolveInfo.providerInfo}")
    Log.d(TAG,"original label ${resolveInfo.loadLabel(this.packageManager)}")
    val p = Parcel.obtain()
    resolveInfo.writeToParcel(p, 0)
    val copy = ResolveInfo.CREATOR.createFromParcel(p)
    Log.d(TAG,"copy activity info ${copy.activityInfo}")
    Log.d(TAG,"copy service info ${copy.serviceInfo}")
    Log.d(TAG,"copy provider info ${copy.providerInfo}")
    Log.d(TAG,"copy label ${copy.loadLabel(this.packageManager)}")
}

My original ResolveInfo object has an ActivityInfo but my copy doesn't. ActivityInfo implements Parcelable so I don't quite understand why it isn't written in the Parcel. According to the source of ResolveInfo it should be written in the parcel and read from it:

public void writeToParcel(Parcel dest, int parcelableFlags) {
    if (activityInfo != null) {
        dest.writeInt(1);
        activityInfo.writeToParcel(dest, parcelableFlags);
    } else if (serviceInfo != null) {
        dest.writeInt(2);
        serviceInfo.writeToParcel(dest, parcelableFlags);
    } else if (providerInfo != null) {
        dest.writeInt(3);
        providerInfo.writeToParcel(dest, parcelableFlags);
    } else {
        dest.writeInt(0);
    }
    if (filter != null) {
        dest.writeInt(1);
        filter.writeToParcel(dest, parcelableFlags);
    } else {
        dest.writeInt(0);
    }
    dest.writeInt(priority);
    dest.writeInt(preferredOrder);
    dest.writeInt(match);
    dest.writeInt(specificIndex);
    dest.writeInt(labelRes);
    TextUtils.writeToParcel(nonLocalizedLabel, dest, parcelableFlags);
    dest.writeInt(icon);
    dest.writeString(resolvePackageName);
    dest.writeInt(targetUserId);
    dest.writeInt(system ? 1 : 0);
    dest.writeInt(noResourceId ? 1 : 0);
    dest.writeInt(iconResourceId);
    dest.writeInt(handleAllWebDataURI ? 1 : 0);
    dest.writeInt(isInstantAppAvailable ? 1 : 0);
}

private ResolveInfo(Parcel source) {
    activityInfo = null;
    serviceInfo = null;
    providerInfo = null;
    switch (source.readInt()) {
        case 1:
            activityInfo = ActivityInfo.CREATOR.createFromParcel(source);
            break;
        case 2:
            serviceInfo = ServiceInfo.CREATOR.createFromParcel(source);
            break;
        case 3:
            providerInfo = ProviderInfo.CREATOR.createFromParcel(source);
            break;
        default:
            Slog.w(TAG, "Missing ComponentInfo!");
            break;
    }
    if (source.readInt() != 0) {
        filter = IntentFilter.CREATOR.createFromParcel(source);
    }
    priority = source.readInt();
    preferredOrder = source.readInt();
    match = source.readInt();
    specificIndex = source.readInt();
    labelRes = source.readInt();
    nonLocalizedLabel
            = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
    icon = source.readInt();
    resolvePackageName = source.readString();
    targetUserId = source.readInt();
    system = source.readInt() != 0;
    noResourceId = source.readInt() != 0;
    iconResourceId = source.readInt();
    handleAllWebDataURI = source.readInt() != 0;
    instantAppAvailable = isInstantAppAvailable = source.readInt() != 0;
}

Clue anyone as to why this nested Parcelable isn't unparceled properly? Am I missing useful flags I should use?

Thanks,

Upvotes: 0

Views: 232

Answers (1)

Marc Poppleton
Marc Poppleton

Reputation: 108

Ok so I've found how to solve the problem, it had nothing to do with the Parcelable Creator methods but with the way I was using the Parcel. Once you're done writing in the Parcel you have to call setDataPosition with 0 as arguments value.

My code is now the following:

fun failingFunction(resolveInfo:ResolveInfo){
    Log.d(TAG,"original label ${resolveInfo.loadLabel(this.packageManager)}")
    val p = Parcel.obtain()
    resolveInfo.writeToParcel(p, 0)
    p.setDataPosition(0)
    val copy = ResolveInfo.CREATOR.createFromParcel(p)
    Log.d(TAG,"copy label ${copy.loadLabel(this.packageManager)}") // no longer throws java.lang.IllegalStateException
}

Upvotes: 0

Related Questions