What is the use of flags in Parcelable?
Asked Answered
L

2

17

I have been writing Parcelables to Parcel without any focus on flags field, which is a parameter in the method signature and it has worked fine but I have run into an implementation where I can no longer ignore them:

public static <K extends Parcelable, V extends Parcelable> void write(Parcel dest,
                                                    Map<K, V> map, int flags) {
        if (map == null) {
            dest.writeInt(-1);
        } else {
            Set<Map.Entry<K, V>> entrySet = map.entrySet();
            dest.writeInt(entrySet.size());
            for (Map.Entry<K, V> entry : entrySet) {
                dest.writeParcelable(entry.getKey(), flags);
                dest.writeParcelable(entry.getValue(), flags);
            }
        }
    }

This is a Map to/from Parcelable utility I have written and I am wondering if the flags should be passed as it is to both Key as well as the Value while writing them or should pass 0 for Key and flags for Value.

I read the definition of what a flag is in the docs:

PARCELABLE_WRITE_RETURN_VALUE

added in API level 1

int PARCELABLE_WRITE_RETURN_VALUE

Flag for use with writeToParcel(Parcel, int): the object being written is a return value, that is the result of a function such as "Parcelable someFunction()", "void someFunction(out Parcelable)", or "void someFunction(inout Parcelable)". Some implementations may want to release resources at this point.

Constant Value: 1 (0x00000001)

But am unable to comprehend it. Could anyone explain in simple terms what a Parcelable flag is and how it should be used?

Lamblike answered 26/5, 2017 at 13:45 Comment(4)
"a return value, that is the result of a function" is pretty self explanatoryParks
@cricket_007 : I have a void method?? But that is a static method? What is that got to do with anything? If you referred to the method, entry.getKey() then, it does return a Parcelable. Shouldn't the flag be 1?Lamblike
void write is your method, entry.getKey() is defined in the Map interface, I don't understand your pointParks
You only have two options, so how many did you try before posting the question?Parks
F
8

The only currently existing flag (PARCELABLE_WRITE_RETURN_VALUE) is intended for use in AIDL interfaces. It is supposed to hint certain kinds of Parcelable objects, that they are being returned from IPC method, so their associated resources can be released. Fot instance, ContentProvider internally contains AIDL method like this:

ParcelFileDescriptor openFile(String path, int flags);

When you override openFile in a custom ContentProvider, your method returns an open ParcelFileDescriptor… You aren't closing it yourself, and it is not automatically closed during interprocess transfer either (passing descriptors between processes does not imply closing them in Linux). But the descriptor is not leaked! Instead the ParcelFileDescriptor closes itself when written to Parcel:

@Override
public void writeToParcel(Parcel out, int flags) {
    if (mWrapped != null) {
        try {
            mWrapped.writeToParcel(out, flags);
        } finally {
            releaseResources();
        }
    } else {
        if (mCommFd != null) {
            out.writeInt(1);
            out.writeFileDescriptor(mFd);
            out.writeFileDescriptor(mCommFd);
        } else {
            out.writeInt(0);
            out.writeFileDescriptor(mFd);
        }
        if ((flags & PARCELABLE_WRITE_RETURN_VALUE) != 0 && !mClosed) {
            // Not a real close, so emit no status
            closeWithStatus(Status.SILENCE, null);
        }
    }
}

Since ParcelFileDescriptor is just ordinary class, using facilities of Binder/Parcel to pass FileDescriptor between processes, you can imagine existence of similar classes, that hold onto native resources (memory, file descriptors) and conditionally release them when returned from openFile-like methods.

Likewise, other flags could be used to propagate similar conditional behavior deeply down Parcelable matryoshka. Unfortunately, Android developers haven't defined reasonable rules for introducing such custom flags (unlike e.g. IBinder#FIRST_CALL_TRANSACTION and IBinder#LAST_CALL_TRANSACTION), and AIDL is not widely used in practice outside of Android internals, so I am not aware of any examples of such flags.

Firebrick answered 26/5, 2017 at 14:30 Comment(3)
Okay, tell me if I have understood it correctly. The open() method results in a IPC and on the other side(another process), a FileDescriptor is created to be ultimately returned to us and because the FileDescriptor might have been opened on that side, the check just closes before sending it to us. Also, closing a resource just before sending through IPC call is a very specific case and I need to look it up only during IPC probably.Lamblike
@pulp_fiction Yes, you got it all right. Also, this answer is only roughly true, because the descriptor is not actually "closed before sending" — it is duplicated (inside writeFileDescriptor), original is "closed" (not really, since descriptors are reference-counted), then the dupe is truly closed (presumably during transfer, contradicting my earlier statement, that descriptors aren't closed during transfer). All of that madness is hidden behind ParcelFileDescriptor abstraction, making it look like "closing before sending". What were they thinking…Firebrick
@pulp_fiction TL; DR: don't carelessly store FileDescriptors in things, or else Dianne Hackborn will come and kill your dog. There is a handy method just for that purpose (to determine if Parcel contains file descriptors).Firebrick
P
0

You can only provide flag zero or one.

You have a void method, so you're not returning a Parcelable from a function nor do you have a parameter that's Parcelable, as the documentation says, therefore the flag should be zero.

Parks answered 26/5, 2017 at 13:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.