@@ -174,10 +174,12 @@ public ParcelFileDescriptor getInputStream(String token) {
174174 return null ;
175175 }
176176 try {
177- if (state .inputPipe == null ) {
178- state .inputPipe = ParcelFileDescriptor .createPipe ();
177+ synchronized (state ) {
178+ if (state .inputPipe == null ) {
179+ state .inputPipe = ParcelFileDescriptor .createPipe ();
180+ }
181+ return state .inputPipe [0 ]; // read end
179182 }
180- return state .inputPipe [0 ]; // read end
181183 } catch (IOException e ) {
182184 Log .e (TAG , "getInputStream: failed to create pipe for channel " + token , e );
183185 return null ;
@@ -199,11 +201,13 @@ public ParcelFileDescriptor getOutputStream(String token) {
199201 return null ;
200202 }
201203 try {
202- if (state .outputPipe == null ) {
203- state .outputPipe = ParcelFileDescriptor .createPipe ();
204- startOutputForwarder (state );
204+ synchronized (state ) {
205+ if (state .outputPipe == null ) {
206+ state .outputPipe = ParcelFileDescriptor .createPipe ();
207+ startOutputForwarder (state );
208+ }
209+ return state .outputPipe [1 ]; // write end for caller
205210 }
206- return state .outputPipe [1 ]; // write end for caller
207211 } catch (IOException e ) {
208212 Log .e (TAG , "getOutputStream: failed to create pipe for channel " + token , e );
209213 return null ;
@@ -222,11 +226,19 @@ public boolean writeInputToFd(String token, ParcelFileDescriptor fd) {
222226 ChannelState state = stateForToken (token );
223227 if (state == null ) {
224228 Log .w (TAG , "writeInputToFd: unknown channel " + token );
229+ if (fd != null ) {
230+ try {
231+ fd .close ();
232+ } catch (IOException ignored ) {
233+ }
234+ }
225235 return false ;
226236 }
227237 try {
228- if (state .inputPipe == null ) {
229- state .inputPipe = ParcelFileDescriptor .createPipe ();
238+ synchronized (state ) {
239+ if (state .inputPipe == null ) {
240+ state .inputPipe = ParcelFileDescriptor .createPipe ();
241+ }
230242 }
231243 final ParcelFileDescriptor readEnd = state .inputPipe [0 ];
232244 new Thread (() -> {
@@ -244,6 +256,12 @@ public boolean writeInputToFd(String token, ParcelFileDescriptor fd) {
244256 return true ;
245257 } catch (IOException e ) {
246258 Log .e (TAG , "writeInputToFd: failed to create pipe" , e );
259+ if (fd != null ) {
260+ try {
261+ fd .close ();
262+ } catch (IOException ignored ) {
263+ }
264+ }
247265 return false ;
248266 }
249267 }
@@ -263,12 +281,20 @@ public boolean readOutputFromFd(String token, ParcelFileDescriptor fd,
263281 ChannelState state = stateForToken (token );
264282 if (state == null ) {
265283 Log .w (TAG , "readOutputFromFd: unknown channel " + token );
284+ if (fd != null ) {
285+ try {
286+ fd .close ();
287+ } catch (IOException ignored ) {
288+ }
289+ }
266290 return false ;
267291 }
268292 try {
269- if (state .outputPipe == null ) {
270- state .outputPipe = ParcelFileDescriptor .createPipe ();
271- startOutputForwarder (state );
293+ synchronized (state ) {
294+ if (state .outputPipe == null ) {
295+ state .outputPipe = ParcelFileDescriptor .createPipe ();
296+ startOutputForwarder (state );
297+ }
272298 }
273299 final ParcelFileDescriptor writeEnd = state .outputPipe [1 ];
274300 new Thread (() -> {
@@ -298,6 +324,12 @@ public boolean readOutputFromFd(String token, ParcelFileDescriptor fd,
298324 return true ;
299325 } catch (IOException e ) {
300326 Log .e (TAG , "readOutputFromFd: failed to create pipe" , e );
327+ if (fd != null ) {
328+ try {
329+ fd .close ();
330+ } catch (IOException ignored ) {
331+ }
332+ }
301333 return false ;
302334 }
303335 }
@@ -436,42 +468,52 @@ private void handleIncomingData(ChannelDataRequest data) {
436468 Log .w (TAG , "handleIncomingData: unknown channelId " + channelId );
437469 return ;
438470 }
439- try {
440- if (state .inputPipe == null ) {
441- state .inputPipe = ParcelFileDescriptor .createPipe ();
442- // Open a single OutputStream over the write-end PFD and keep it alive
443- // across all chunks. Wrapping the PFD rather than its raw FileDescriptor
444- // ensures the FD is NOT closed when the stream would otherwise be closed.
445- state .inputPipeWriter = new ParcelFileDescriptor .AutoCloseOutputStream (
446- state .inputPipe [1 ]);
447- }
448- if (data .payload != null && data .payload .size () > 0 ) {
449- try {
450- state .inputPipeWriter .write (data .payload .toByteArray ());
451- } catch (IOException e ) {
452- // Reset on write error so the next message re-creates the pipe
453- try { state .inputPipeWriter .close (); } catch (IOException ignored ) { }
454- state .inputPipeWriter = null ;
455- state .inputPipe = null ;
456- Log .e (TAG , "handleIncomingData: write error for channel " + channelId , e );
457- return ;
471+ synchronized (state ) {
472+ try {
473+ if (state .inputPipe == null ) {
474+ state .inputPipe = ParcelFileDescriptor .createPipe ();
475+ // Open a single OutputStream over the write-end PFD and keep it alive
476+ // across all chunks. Wrapping the PFD rather than its raw FileDescriptor
477+ // ensures the FD is NOT closed when the stream would otherwise be closed.
478+ state .inputPipeWriter = new ParcelFileDescriptor .AutoCloseOutputStream (
479+ state .inputPipe [1 ]);
458480 }
459- }
460- if (Boolean .TRUE .equals (data .finalMessage )) {
461- // Peer has finished sending; close the write-end to signal EOF to the reader.
462- // The read-end (inputPipe[0]) is kept alive so the app can drain remaining data;
463- // it will be released when the channel itself is closed via state.close().
464- if (state .inputPipeWriter != null ) {
465- try { state .inputPipeWriter .close (); } catch (IOException ignored ) { }
466- state .inputPipeWriter = null ;
481+ if (data .payload != null && data .payload .size () > 0 ) {
482+ try {
483+ state .inputPipeWriter .write (data .payload .toByteArray ());
484+ } catch (IOException e ) {
485+ // Reset on write error so the next message re-creates the pipe
486+ try { state .inputPipeWriter .close (); } catch (IOException ignored ) { }
487+ if (state .inputPipe != null ) {
488+ if (state .inputPipe [0 ] != null ) {
489+ try { state .inputPipe [0 ].close (); } catch (IOException ignored ) { }
490+ }
491+ if (state .inputPipe [1 ] != null ) {
492+ try { state .inputPipe [1 ].close (); } catch (IOException ignored ) { }
493+ }
494+ }
495+ state .inputPipeWriter = null ;
496+ state .inputPipe = null ;
497+ Log .e (TAG , "handleIncomingData: write error for channel " + channelId , e );
498+ return ;
499+ }
467500 }
468- if (state .inputPipe != null ) {
469- state .inputPipe [1 ] = null ; // write-end closed by inputPipeWriter above
501+ if (Boolean .TRUE .equals (data .finalMessage )) {
502+ // Peer has finished sending; close the write-end to signal EOF to the reader.
503+ // The read-end (inputPipe[0]) is kept alive so the app can drain remaining data;
504+ // it will be released when the channel itself is closed via state.close().
505+ if (state .inputPipeWriter != null ) {
506+ try { state .inputPipeWriter .close (); } catch (IOException ignored ) { }
507+ state .inputPipeWriter = null ;
508+ }
509+ if (state .inputPipe != null ) {
510+ state .inputPipe [1 ] = null ; // write-end closed by inputPipeWriter above
511+ }
512+ dispatchChannelEvent (state , EVENT_TYPE_INPUT_CLOSED , 0 , 0 );
470513 }
471- dispatchChannelEvent (state , EVENT_TYPE_INPUT_CLOSED , 0 , 0 );
514+ } catch (IOException e ) {
515+ Log .e (TAG , "handleIncomingData: pipe creation failed for channel " + channelId , e );
472516 }
473- } catch (IOException e ) {
474- Log .e (TAG , "handleIncomingData: pipe creation failed for channel " + channelId , e );
475517 }
476518 }
477519
0 commit comments