ver software por pasar para pantalla otro duplicar conectar como celular app aplicaciones android android-intent android-contentprovider

software - ¿Compartir a través de Seekable Pipe o Stream con otra aplicación de Android?



software para conectar android a pc (3)

Has planteado una combinación realmente difícil de requisitos.

Veamos sus ideas para soluciones:

Las posibles ideas sugeridas incluyen:

  • Una forma de reconfigurar createPipe () que da como resultado una tubería que se puede buscar

  • Una forma de usar un FileDescriptor basado en sockets que da como resultado un tubo que se puede buscar

  • Algún tipo de disco RAM u otra cosa que se siente como un archivo para el resto de Android pero no es persistente

El primero no funcionará Este problema es que la primitiva de tubería implementada por el SO es fundamentalmente no buscable. El motivo es la búsqueda de soporte que requeriría que el sistema operativo amortigüe los "contenidos" de tuberías completos ... hasta que se cierre el final de lectura. Eso no se puede implementar ... a menos que ponga un límite en la cantidad de datos que se pueden enviar a través del conducto.

El segundo tampoco funcionará, por más o menos la misma razón. Los sockets de nivel de sistema operativo no son buscables.

En un nivel, la idea final (un sistema de archivos RAM) funciona, módulo que dicha capacidad es compatible con el sistema operativo Android. (Un archivo Ramfs es buscable, después de todo.) Sin embargo, una secuencia de archivos no es un conducto. En particular, el comportamiento con respecto al final del archivo es diferente para una secuencia de archivos y una canalización. Y obtener una secuencia de archivos para que se vea como una transmisión de tubería desde la perspectiva del lector implicaría algún código especial en ese lado. (El problema es similar al problema de ejecutar tail -f en un archivo de registro ...)

Lamentablemente, no creo que haya otra forma de obtener un descriptor de archivo que se comporte como un conducto con respecto al final del archivo y también es posible buscarlo ... menos que modificar radicalmente el sistema operativo.

Si pudieras cambiar la aplicación que está leyendo de la transmisión, podrías evitar esto. Esto está excluido por el hecho de que fd necesita ser leído y buscado por QuickOffice que (supongo) no puede modificar. (Pero si puede cambiar la aplicación, hay formas de hacer que esto funcione ...)

Por cierto, creo que tendrías algunos problemas con estos requisitos en Linux o Windows. Y no son específicos de Java.

ACTUALIZAR

Ha habido varios comentarios interesantes sobre esto, y quiero abordar algunos aquí:

  1. El OP ha explicado el caso de uso que está motivando su pregunta. Básicamente, quiere un esquema en el que los datos que pasan por el "canal" entre las aplicaciones no sean vulnerables en el caso de que el dispositivo del usuario sea robado (o confiscado) mientras las aplicaciones se están ejecutando.

    ¿Es eso posible?

    • En teoría, no. Si uno postula un alto grado de sofisticación técnica (y técnicas que el público puede desconocer ...) entonces los "tipos malos" podrían entrar en el sistema operativo y leer los datos de la memoria compartida mientras el "canal" permanecía activo.

    • Dudo que tales ataques sean (actualmente) posibles en la práctica.

    • Sin embargo, incluso si suponemos que el "canal" no escribe nada en "disco", aún podría haber rastros del canal en la memoria: por ej.

      • una RAM todavía montada o segmentos de memoria compartida aún activos, o

      • restos de RAM / memoria compartida anterior.

      En teoría, estos datos podrían, en teoría, recuperarse, siempre que el "chico malo" no apague o reinicie el dispositivo.

  2. Se ha sugerido que el ashmem podría usarse en este contexto:

    • El problema de que no haya API públicas de Java podría abordarse (escribiendo API de terceros, por ejemplo)

    • El verdadero obstáculo es la necesidad de una API de transmisión. Según los documentos "ashmem", tienen una API similar a un archivo. Pero creo que eso solo significa que se ajustan al modelo de "descriptor de archivo". Estos FD se pueden pasar de una aplicación a otra (a través de fork / exec), y se usa "ioctl" para operarlos. Pero no hay indicios de que implementen "leer" y "escribir" ... y mucho menos "buscar".

    • Ahora, probablemente podría implementar una secuencia de lectura / escritura / búsqueda sobre ashmem, usando librerías nativas y Java en ambos extremos del canal. Pero ambas aplicaciones necesitarían estar "al tanto" de este proceso, probablemente al nivel de proporcionar opciones de línea de comando para configurar el canal.

    Estos problemas también se aplican al shmem antiguo ... excepto que la configuración del canal es probablemente más difícil.

  3. La otra opción potencial es usar una RAM fs.

    • Esto es más fácil de implementar. Los archivos en los RAM se comportarán como archivos "normales"; cuando lo abre una aplicación, obtiene un descriptor de archivo que puede leerse, escribirse y buscarse ... dependiendo de cómo se haya abierto. Y (creo) deberías poder pasar un FD buscable para un archivo RAMfs a través de un fork / exec.

    • El problema es que RAM RAM necesita ser "montado" por el sistema operativo para poder usarlo. Mientras está montado, otra aplicación (privilegiada) también puede abrir y leer archivos. Y el sistema operativo no le permitirá desmontar las RAM mientras que alguna aplicación tiene archivos fds abiertos para los archivos RAM.

    • Existe un esquema (hipotético) que mitiga parcialmente lo anterior.

      1. La aplicación de origen crea y monta una RAM "privada".
      2. La aplicación de origen crea / abre el archivo para lectura / escritura y luego lo desvincula.
      3. La aplicación de origen escribe el archivo usando el fd desde el abierto.
      4. La aplicación de origen bifurca / ejecuta la aplicación de receptor, pasando el fd.
      5. La aplicación de receptor lee desde el (creo) aún buscado Fd, buscando según sea necesario.
      6. Cuando la aplicación de origen se da cuenta de que el proceso de aplicación de sumidero (hijo) ha salido, desmonta y destruye las RAMfs.

      Esto no requeriría modificar la aplicación de lectura (sumidero).

      Sin embargo, una tercera aplicación (privilegiada) podría entrar potencialmente en las RAM, ubicar el archivo no enlazado en la memoria y leerlo.

Sin embargo, una vez revisado todo lo anterior, la solución más práctica sigue siendo modificar la aplicación de lectura (receptor) para leer todo el flujo de entrada en un byte[] , luego abrir un ByteArrayInputStream en los datos almacenados en el búfer. La aplicación principal puede buscarla y restablecerla a voluntad.

Muchas acciones Intent , como ACTION_VIEW , toman un ACTION_VIEW apunta al contenido sobre el que se debe realizar la acción. Si el contenido está respaldado por un archivo, ya sea que Uri apunte directamente al archivo, o a un ContentProvider sirve el archivo ( ver FileProvider ), esto generalmente funciona.

Hay escenarios en los que los desarrolladores no desean que el contenido resida en un archivo para compartirlo con otras aplicaciones. Un escenario común es para el cifrado: los datos descifrados deben residir en la memoria RAM, no en el disco, para minimizar el riesgo de que alguien consiga los datos descifrados.

Mi solución clásica para compartir desde la RAM es usar ParcelFileDescriptor y createPipe() . Sin embargo, cuando la actividad que responde a ACTION_VIEW (o lo que sea) obtiene un InputStream en ese conducto, la transmisión resultante es limitada en comparación con las transmisiones que se obtienen cuando ContentProvider está ContentProvider contenido de un archivo. Por ejemplo, esta aplicación de muestra funciona bien con Adobe Reader y bloquea QuickOffice.

Basándome en preguntas past related , mi suposición es que createPipe() realmente está creando una tubería y que las tuberías no son buscables . Los clientes que intentan "rebobinar" o "avanzar rápidamente" se topan con problemas como resultado.

Estoy buscando una solución confiable para compartir contenido en memoria con una aplicación de terceros que supera esta limitación. Específicamente:

  • Tiene que usar una sintaxis de Uri que las aplicaciones cliente ACTION_VIEW cumplir (es decir, los implementadores de ACTION_VIEW ); soluciones que implican algo obtuso que las aplicaciones del cliente es poco probable que reconozcan (p. ej., aprobar tal y tal a través de un Intent adicional) no califican

  • Los datos que se compartirán no se pueden escribir en un archivo como parte del intercambio (por supuesto, la aplicación del cliente podría terminar guardando los bytes recibidos en el disco, pero ignoremos ese riesgo por el momento)

  • Lo ideal es que no involucre a la aplicación que busca compartir los datos que abren un ServerSocket o que de otro modo agravan los riesgos de seguridad

Las posibles ideas sugeridas incluyen:

  • Una forma de reconfigurar createPipe() que da como resultado una tubería que se puede buscar

  • Una forma de usar un FileDescriptor basado en FileDescriptor que da como resultado un tubo que se puede buscar

  • Algún tipo de disco RAM u otra cosa que se siente como un archivo para el resto de Android pero no es persistente

Una crítica clave, si se quiere, de una solución de trabajo es si puedo obtener un PDF servido desde la RAM que QuickOffice puede leer.

¿Alguna sugerencia?

¡Gracias!


He estado experimentando con el código @josias. Encontré que algunas de las llamadas a la query(...) _data una proyección de _data . Incluir los datos para esa columna y establecer la longitud real significa que se pueden abrir más tipos de archivos en más aplicaciones. _data siempre _data incluso cuando no está en la proyección aprobada permite abrir aún más tipos de archivos.

Aquí es a lo que terminé:

private static final String[] PROJECTION = {OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE, "_data"}; @Override public Cursor query(Uri url, String[] projection, String selection, String[] selectionArgs, String sort) { byte[] data = getData(mSourcePath, url); final MatrixCursor cursor = new MatrixCursor(PROJECTION, 1); cursor.newRow() .add(url.getLastPathSegment()) .add(data.length) .add(data); return cursor; }


No es una solución general para su problema, pero abrir un PDF en QuickOffice me funciona con el siguiente código (basado en su muestra):

@Override public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException { try { byte[] data = getData(uri); long size = data.length; ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe(); new TransferThread(new ByteArrayInputStream(data), new AutoCloseOutputStream(pipe[1])).start(); return new AssetFileDescriptor(pipe[0], 0, size); } catch (IOException e) { e.printStackTrace(); } return null; }; private byte[] getData(Uri uri) throws IOException { AssetManager assets = getContext().getResources().getAssets(); InputStream is = assets.open(uri.getLastPathSegment()); ByteArrayOutputStream os = new ByteArrayOutputStream(); copy(is, os); return os.toByteArray(); } private void copy(InputStream in, OutputStream out) throws IOException { byte[] buf = new byte[1024]; int len; while ((len = in.read(buf)) > 0) { out.write(buf, 0, len); } in.close(); out.flush(); out.close(); } @Override public Cursor query(Uri url, String[] projection, String selection, String[] selectionArgs, String sort) { if (projection == null) { projection = new String[] { OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE }; } String[] cols = new String[projection.length]; Object[] values = new Object[projection.length]; int i = 0; for (String col : projection) { if (OpenableColumns.DISPLAY_NAME.equals(col)) { cols[i] = OpenableColumns.DISPLAY_NAME; values[i++] = url.getLastPathSegment(); } else if (OpenableColumns.SIZE.equals(col)) { cols[i] = OpenableColumns.SIZE; values[i++] = AssetFileDescriptor.UNKNOWN_LENGTH; } } cols = copyOf(cols, i); values = copyOf(values, i); final MatrixCursor cursor = new MatrixCursor(cols, 1); cursor.addRow(values); return cursor; } private String[] copyOf(String[] original, int newLength) { final String[] result = new String[newLength]; System.arraycopy(original, 0, result, 0, newLength); return result; } private Object[] copyOf(Object[] original, int newLength) { final Object[] result = new Object[newLength]; System.arraycopy(original, 0, result, 0, newLength); return result; }