android - puede - ¿Podemos instalar un APK desde un ContentProvider?
no puedo instalar apk descargadas (4)
Estoy trabajando en una biblioteca para permitir que las aplicaciones se actualicen automáticamente , para aquellas que se están distribuyendo fuera de Android Market.
Mi plan original era incluir el código que descargaría el archivo APK en el almacenamiento interno y luego lo instalaría desde allí a través de un ContentProvider
y un content://
Uri
. Sin embargo, cuando lo intenté, el sistema del instalador volcó una advertencia "Skipping dir:" a LogCat y no lo instaló. Una vez que cambié a la descarga del APK al almacenamiento externo y ACTION_VIEW
un file://
Uri
con el instalador ACTION_VIEW
Intent
, funcionó.
El mensaje "Skipping dir:" parece estar registrado por parsePackage()
en PackageParser
, que parece asumir que está trabajando con un File
. Eso sugeriría que no podemos usar el content://
valores de Uri
.
¿Alguien ha utilizado con éxito ACTION_VIEW
en una application/vnd.android.package-archive
Intent
con un content://
Uri
? Si es así, ¿hubo algún truco específico en la configuración del ContentProvider
que lo hizo funcionar?
¡Gracias!
Estoy de acuerdo con el análisis de Jules y agregaría precisiones concretas:
En PackageInstallerActivity
, que es llamada por ACTION_VIEW en un apk, hay esto en el método onCreate()
:
315 String apkPath = mPackageURI.getPath();
316 File apkFile = new File(apkPath);
Y antes de eso, este método de PackageUtil
se llama:
73 public static PackageParser.Package getPackageInfo(Uri packageURI) {
74 final String archiveFilePath = packageURI.getPath();
75 PackageParser packageParser = new PackageParser(archiveFilePath);
76 File sourceFile = new File(archiveFilePath);
77 DisplayMetrics metrics = new DisplayMetrics();
78 metrics.setToDefaults();
79 return packageParser.parsePackage(sourceFile, archiveFilePath, metrics, 0);
80 }
Todo esto tiende a confirmar que el PackageManager espera solo archivos Uris.
El registro que tiene, Skipping dir:
se encuentra en packageParser.parsePackage
, que prueba si la ruta dada en el Uri es un archivo o no.
La documentación para ACTION_INSTALL_PACKAGE es incorrecta. También solo aceptará archivos.
Por lo tanto, mi única sugerencia sería crear una copia del archivo en el área de archivos de las aplicaciones, hacer que sea legible para todo el mundo y limpiar los archivos que queden en una fecha posterior.
Respuesta incorrecta anterior : en 4.0 y versiones superiores hay un ACTION_INSTALL_PACKAGE que acepta un contenido: // URI ( JavaDoc ), pero, antes de eso, está limitado a la instalación a través de ACTION_VIEW que asume que el URI aprobado es un archivo: / / URI.
Supongo que esto no es posible, ya que la API de Java no parece permitirlo. El openFile()
ContentProvider devuelve un ParcelFileDescriptor
, desde el cual puede obtener un java.io.FileDescriptor
. Luego puede usar este FileDescriptor
para abrir un FileInputStream
o un FileOutputStream
. Desafortunadamente, no puede usarlo para abrir un RandomAccessFile
(a pesar de que RandomAccessFile
funciona en los descriptores de la misma manera que el resto, el constructor que necesitaría simplemente falta en la API).
Como los archivos APK son archivos ZIP, que deben leerse desordenados (hay que buscar hasta el final para encontrar el directorio de archivos), asumo que la implementación de la instalación requerirá un RandomAccessFile
, por lo que no habría sido posible respaldar caso estás tratando de implementar.
Tengo esto en una de mis aplicaciones, que me permite acceder al almacenamiento local (las preferencias del usuario se pueden seleccionar antes de que lo intentes;))
import java.io.*;
import android.content.*;
import android.database.*;
import android.net.*;
import android.os.*;
import android.preference.PreferenceManager;
import android.util.Log;
public class LocalFileContentProvider extends ContentProvider {
private static final String URI_PREFIX = "content://your.content.provider.as.per.manifest";
public static String constructUri(String url) {
Uri uri = Uri.parse(url);
return uri.isAbsolute() ? url : URI_PREFIX + url;
}
@Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
SharedPreferences app_preferences = PreferenceManager.getDefaultSharedPreferences(getContext());
boolean allowLocal = app_preferences.getBoolean("allowLocalFiles", false);
if (allowLocal) {
try {
File file = new File(uri.getPath());
if (file.isDirectory())
return null;
ParcelFileDescriptor parcel = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
return parcel;
} catch (Exception e) {
return null;
}
} else {
return null;
}
}
@Override
public boolean onCreate() {
return true;
}
@Override
public int delete(Uri uri, String s, String[] as) {
throw new UnsupportedOperationException("Not supported by this provider");
}
@Override
public String getType(Uri uri) {
throw new UnsupportedOperationException("Not supported by this provider");
}
@Override
public Uri insert(Uri uri, ContentValues contentvalues) {
throw new UnsupportedOperationException("Not supported by this provider");
}
@Override
public Cursor query(Uri uri, String[] as, String s, String[] as1, String s1) {
throw new UnsupportedOperationException("Not supported by this provider");
}
@Override
public int update(Uri uri, ContentValues contentvalues, String s, String[] as) {
throw new UnsupportedOperationException("Not supported by this provider");
}
}
Mi manifiesto contiene
<provider android:name="it.automated.android.kiosk.se.LocalFileContentProvider"
android:authorities="it.automated" />
y luego para iniciar la instalación (o acción)
Intent viewIntent = new Intent(Intent.ACTION_VIEW);
viewIntent.setDataAndType(Uri.parse(url), mimeType);