Transferir InputStream a otro servicio (a través de límites de proceso) con ParcelFileDescriptor.createPipe () falla con "EBADF (número de archivo incorrecto)"

Quiero "enviar" un InputStream de un servicio de Android a otro servicio que se ejecuta dentro de un proceso diferente utilizando ParcelFileDescriptor.createPipe() , un flujo de flujo de secuencia a secuencia y un ParcelFileDescriptor, que representa el lado de lectura del canal, que es Dado al otro servicio con medios de Binder IPC.

Código de envío (proceso A)

Quiero enviar un InputStream dado al servicio de recepción:

 public sendInputStream() { InputStream is = ...; // that's the stream for process/service B ParcelFileDescriptor pdf = ParcelFileDescriptorUtil.pipeFrom(is); inputStreamService.inputStream(pdf); } 

El ParcelFileDescriptorUtil es una clase auxiliar, con un clásico java.io. Stream-to-stream copy Hilo:

 public class ParcelFileDescriptorUtil { public static ParcelFileDescriptor pipeFrom(InputStream inputStream) throws IOException { ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe(); ParcelFileDescriptor readSide = pipe[0]; ParcelFileDescriptor writeSide = pipe[1]; // start the transfer thread new TransferThread(inputStream, new ParcelFileDescriptor.AutoCloseOutputStream(writeSide)).start(); return readSide; } static class TransferThread extends Thread { final InputStream mIn; final OutputStream mOut; TransferThread(InputStream in, OutputStream out) { super("ParcelFileDescriptor Transfer Thread"); mIn = in; mOut = out; setDaemon(true); } @Override public void run() { byte[] buf = new byte[1024]; int len; try { while ((len = mIn.read(buf)) > 0) { mOut.write(buf, 0, len); } mOut.flush(); // just to be safe } catch (IOException e) { LOG.e("TransferThread", e); } finally { try { mIn.close(); } catch (IOException e) { } try { mOut.close(); } catch (IOException e) { } } } } } 

Código de Servicio de Recepción (Proceso B)

El servicio de recepción .aidl :

 package org.exmaple; interface IInputStreamService { void inputStream(in ParcelFileDescriptor pfd); } 

El servicio receptor, llamado por el Proceso A:

 public class InputStreamService extends Service { @Override public IBinder onBind(Intent intent) { return mBinder; } private final IInputStreamService.Stub mBinder = new IInputStreamService.Stub() { @Override public void inputStream(ParcelFileDescriptor pfd) throws RemoteException { InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(pfd); OutputStream os = ...; int len; byte[] buf = new byte[1024]; try { while ((len = is.read(buf)) > 0) { os.write(buf, 0, len); } } catch (IOException e) { // this catches the exception shown below } } }; 

Pero in.read() en inputStream() siempre lanza una IOException

 java.io.IOException: read failed: EBADF (Bad file number) at libcore.io.IoBridge.read(IoBridge.java:442) at java.io.FileInputStream.read(FileInputStream.java:179) at java.io.InputStream.read(InputStream.java:163) 

Parece que el errdo de EBADF está establecido por read() cuando el descriptor de archivo está cerrado. Pero no sé qué lo está causando y cómo solucionarlo.

Y sí, sé que un ConentProvider también sería una posibilidad. Pero, ¿no debería funcionar también con mi enfoque? ¿Hay otras maneras de entregar un flujo de InputStream a un servicio diferente en Android?

En una nota lateral: CommonsWare creó un proyecto similar usando un ContentProvider (relacionadas SO preguntas 1 , 2 ). Es donde obtuve la mayoría de las ideas para mi enfoque de

Parece que la causa era el ParcelFileDescriptor siendo un argumento del método de servicio. Si el servicio devuelve ParcelFileDescriptor funciona como se esperaba.

Servicio de envío (proceso A)

 public sendInputStream() { InputStream is = ...; // that's the stream for process/service B ParcelFileDescriptor pfd = inputStreamService.inputStream(); OutputStream os = new ParcelFileDescriptor.AutoCloseOutputStream(pfd); int len; byte[] buf = new byte[1024]; try { while ((len = is.read(buf)) > 0) { os.write(buf, 0, len); } catch (IOException e) { } finally { try { is.close(); } catch (IOException e1) {} try { os.close(); ] catch (IOException e1) {} } } 

Código de Servicio de Recepción (Proceso B)

El servicio de recepción .aidl :

 package org.exmaple; interface IInputStreamService { ParcelFileDescriptor inputStream(); } 

El servicio receptor, llamado por el Proceso A:

 public class InputStreamService extends Service { @Override public IBinder onBind(Intent intent) { return mBinder; } private final IInputStreamService.Stub mBinder = new IInputStreamService.Stub() { @Override public void ParcelFileDescriptor inputStream() throws RemoteException { // one can read the contents of the Processes A's InputStream // from the following OutputStream OutputStream os = ...; ParcelFileDescriptor pfd = ParcelFileDescriptorUtil.pipeTo(os); return pfd; } }; 

El ParcelFileDescriptorUtil es una clase auxiliar, con un clásico java.io. Secuencia-a-corriente copia Hilo. Ahora tenemos que usar el método pipeTo() .

 public class ParcelFileDescriptorUtil { public static ParcelFileDescriptor pipeTo(OutputStream outputStream) throws IOException { ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe(); ParcelFileDescriptor readSide = pipe[0]; ParcelFileDescriptor writeSide = pipe[1]; // start the transfer thread new TransferThread(new ParcelFileDescriptor.AutoCloseInputStream(readSide), outputStream).start(); return writeSide; } static class TransferThread extends Thread { final InputStream mIn; final OutputStream mOut; TransferThread(InputStream in, OutputStream out) { super("ParcelFileDescriptor Transfer Thread"); mIn = in; mOut = out; setDaemon(true); } @Override public void run() { byte[] buf = new byte[1024]; int len; try { while ((len = mIn.read(buf)) > 0) { mOut.write(buf, 0, len); } mOut.flush(); // just to be safe } catch (IOException e) { LOG.e("TransferThread", e); } finally { try { mIn.close(); } catch (IOException e) { } try { mOut.close(); } catch (IOException e) { } } } } } 

Esto le permite transferir InputStreams a través de los límites del proceso, un inconveniente es que hay algún tiempo de CPU involucrado en las copias de flujo a flujo.

  • La mejor manera de realizar la comunicación entre actividades en una aplicación Android TabHost
  • Android IPC, servicio no obtener instanciado
  • Usar SharedPreferences en modo multiproceso
  • Cada actividad en Android es un proceso o una aplicación es un proceso
  • ¿Por qué es posible escribir una matriz booleana en un paquete pero no en un booleano?
  • Android IPC y diferencias de ContentProvider
  • ¿Cómo llamar a un servicio remoto Android (IPC) desde un servicio Widget / local?
  • ¿Por qué AIDL / Messenger está enlazado a un Servicio?
  • Implementar seguridad a nivel de firma en los servicios de Android con más de una firma permitida
  • Java.io.IOException: write failed: EPIPE (Broken pipe)
  • La vinculación al servicio que ya fue iniciada por BroadcastReceiver crea una nueva instancia de servicio
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.