[guardian-dev] InputStreams across Services

Abel Luck abel at guardianproject.info
Fri Dec 6 11:18:23 EST 2013


Abel Luck:> Mark Murphy:
>> On Wed, Dec 4, 2013, at 10:36, Abel Luck wrote:
>>> We're collaborating with Dominik on an OpenPGP Crypto Provider API, which
>>> is a generic PGP
>>> AIDL interface that a provider app will implement in a remote background
>>> service.
>>
>> I am coming into this completely cold and have zero experience with
>> OpenPGP other than knowing its name and general role. Hence, imagine a
>> grain of salt approximately 25cm on a side, and take that grain of salt
>> with my input... :-)
>>
>>> Our question is: how should we pass InputStreams across the service
>>> interface?
>>>
>>> See the short example outlined here [0],  and the "Open Questions"
>>> section below it.
>>>
>>> Our main questions have to to with the "ParcelFileDescriptor". I assumed
>>> it was some sort
>>> of magic Input/OuputStream wrapper for fds, byte[], etc. But I don't
>>> think that's the case.
>>>
>>> Also, importantly, I'm not sure we can pass ParcelFileDescriptor in a
>>> service method.
>>>
>>> I saw your ContentProvider/Pipe example, but how would you use that
>>> across an AIDL Service?
>>
>> Off the cuff, I would have expected your proposed use of
>> ParcelFileDescriptor to have worked. The analysis in the SO question you
>> link to suggests otherwise. I would have gone about it the same way Flow
>> did.
>>
>> Personally, I have never tried to pass a stream across an AIDL
>> interface. I avoid service binding like the plague, let alone AIDL.
>> Yours seems like a legit use case for it, though I would recommend
>> documenting the heck out of using it, to deal with configuration changes
>> and all that fun stuff that service binding makes challenging.
>>
>> Here are a couple of possible workarounds, off the top of my head:
>>
>> - oneway List<ParcelFileDescriptor> sign(in IOpenPgpCallback callback);
>>
>> This takes Flow at his(?) word that the service can return
>> ParcelFileDescriptor objects successfully. In this case, rather than
>> having the client dictate the ParcelFileDescriptors to use, the service
>> does. The List<ParcelFileDescriptor> would be a two-element ArrayList,
>> with one being the input and one being the output
>>
>> - oneway Uri sign(in Uri input, in IOpenPgpCallback callback);
>>
>> In this case, the client supplies a Uri to a ContentProvider-served
>> stream that provides the input, following the recipe you've seen (though
>> probably using a variant that employs permissions better). The service
>> returns a Uri to a ContentProvider-served stream that provides the
>> output.
>>
>> Yet another possibility is to poke through the innards of Android and
>> try to determine how ContentProvider, ContentResolver, and streams work.
>> At the end of the day, that should be using Binder for IPC and a
>> ParcelFileDescriptor. It may be that the net is that the service is
>> returning the ParcelFileDescriptor all of the time, which is why that
>> works.
>>
>> Let me know if you need additional help.
>>
>
>
> Alrighty. I've created a little test project to test these assumptions.
>
>      https://github.com/abeluck/android-streams-ipc
>
> So far it just tests a variation of Flow's two techniques:
>
> 1. Passing PFDs in method as parameters
> 2. Returning PFD from a method
>
> Re #1: Data flow Client <--> Service. The client is responsible for all the PFDs.
>
> Reading from a PersonalFlotationDevice passed as a param, works, but writing to one seems
> to write into a black hole. However, the thread in question isn't completing, so I think
> something isn't getting close()d properly, but I can't see what :S
>
> Good news is, I didn't encounter the error Flow did [0].

Ok! I fixed test #1.

The Tl;DR for peeps joining us right now is that this will work

(excerpt from an aidl interface definition)
void sendInputStreams(in ParcelFileDescriptor input, in ParcelFileDescriptor output);

I didn't encounter Flows problems, but I don't know what Android version he was using.
Further testing is required to make sure this works as expected across all versions.

Here is the interesting bits:

* Client-side: send the PFDs, one loaded with input [0], the other to be read from later
* Service-side: read from input, write to output [1]
* Made possible by TransferThread that creates the PFDs, a pipe at the OS level, and
shuttles the bytes across [2]

The problem from before was that I was missing the output.close() client-side after
sending the pfds (InputStreamClientFragment.java:136). Because it was never closed, the
TransferThread's read() blocked.

QUESTIONS: Why does this fd need closing? It is closed service-side
(InputStreamService.java:84). Is it not the same FD? Did it get dup()ed somewhere? Is
there a race condition if the service hasn't finished writing but the client calls close
(I couldn't produce this)?

~abel

[0]:
https://github.com/abeluck/android-streams-ipc/blob/master/RemoteClient/src/com/commonsware/android/advservice/client/InputStreamClientFragment.java#L102
[1]:
https://github.com/abeluck/android-streams-ipc/blob/master/RemoteService/src/com/commonsware/android/advservice/InputStreamService.java#L60
[2]:
https://github.com/abeluck/android-streams-ipc/blob/master/RemoteClient/src/com/commonsware/android/advservice/ParcelFileDescriptorUtil.java



More information about the Guardian-dev mailing list