instalar/desinstalar APK mediante programación (PackageManager vs Intents)

Resuelto Håvard Geithus asked hace 54 años • 10 respuestas

Mi aplicación instala otras aplicaciones y necesita realizar un seguimiento de las aplicaciones que ha instalado. Por supuesto, esto podría lograrse simplemente manteniendo una lista de aplicaciones instaladas. ¡Pero esto no debería ser necesario! Debería ser responsabilidad del PackageManager mantener la relación installBy(a, b). De hecho, según la API es:

public abstract String getInstallerPackageName (String packageName): recupera el nombre del paquete de la aplicación que instaló un paquete. Esto identifica de qué mercado proviene el paquete.

El enfoque actual

Instalar APK usando Intent

Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
startActivity(intent);

Desinstalar APK usando Intent:

Intent intent = new Intent(Intent.ACTION_DELETE, Uri.fromParts("package",
getPackageManager().getPackageArchiveInfo(apkUri.getPath(), 0).packageName,null));
startActivity(intent);

Obviamente, esta no es la forma en que, por ejemplo, Android Market instala/desinstala paquetes. Utilizan una versión más rica de PackageManager. Esto se puede ver descargando el código fuente de Android desde el repositorio Git de Android. A continuación se muestran los dos métodos ocultos que corresponden al enfoque Intent. Lamentablemente no están disponibles para desarrolladores externos. ¿Pero tal vez lo sean en el futuro?

El mejor enfoque

Instalación de APK usando PackageManager

/**
 * @hide
 * 
 * Install a package. Since this may take a little while, the result will
 * be posted back to the given observer.  An installation will fail if the calling context
 * lacks the {@link android.Manifest.permission#INSTALL_PACKAGES} permission, if the
 * package named in the package file's manifest is already installed, or if there's no space
 * available on the device.
 *
 * @param packageURI The location of the package file to install.  This can be a 'file:' or a
 * 'content:' URI.
 * @param observer An observer callback to get notified when the package installation is
 * complete. {@link IPackageInstallObserver#packageInstalled(String, int)} will be
 * called when that happens.  observer may be null to indicate that no callback is desired.
 * @param flags - possible values: {@link #INSTALL_FORWARD_LOCK},
 * {@link #INSTALL_REPLACE_EXISTING}, {@link #INSTALL_ALLOW_TEST}.
 * @param installerPackageName Optional package name of the application that is performing the
 * installation. This identifies which market the package came from.
 */
public abstract void installPackage(
        Uri packageURI, IPackageInstallObserver observer, int flags,
        String installerPackageName);

Desinstalar APK usando PackageManager

/**
 * Attempts to delete a package.  Since this may take a little while, the result will
 * be posted back to the given observer.  A deletion will fail if the calling context
 * lacks the {@link android.Manifest.permission#DELETE_PACKAGES} permission, if the
 * named package cannot be found, or if the named package is a "system package".
 * (TODO: include pointer to documentation on "system packages")
 *
 * @param packageName The name of the package to delete
 * @param observer An observer callback to get notified when the package deletion is
 * complete. {@link android.content.pm.IPackageDeleteObserver#packageDeleted(boolean)} will be
 * called when that happens.  observer may be null to indicate that no callback is desired.
 * @param flags - possible values: {@link #DONT_DELETE_DATA}
 *
 * @hide
 */
public abstract void deletePackage(
        String packageName, IPackageDeleteObserver observer, int flags);

Diferencias

  • Cuando se utilizan intents, el administrador de paquetes local no sabe de qué aplicación se originó la instalación. Específicamente, getInstallerPackageName(...) devuelve nulo.

  • El método oculto installPackage(...) toma el nombre del paquete de instalación como parámetro y probablemente sea capaz de establecer este valor.

Pregunta

¿Es posible especificar el nombre del instalador del paquete mediante intents? (¿Quizás el nombre del paquete de instalación pueda agregarse como un extra a la intención de instalación?)

Consejo: Si deseas descargar el código fuente de Android puedes seguir los pasos que se describen aquí: Descarga del árbol fuente. Para extraer los archivos *.java y colocarlos en carpetas de acuerdo con la jerarquía de paquetes, puede consultar este elegante script: Ver código fuente de Android en Eclipse .

Håvard Geithus avatar Jan 01 '70 08:01 Håvard Geithus
Aceptado

Android P+ requiere este permiso en AndroidManifest.xml

<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />

Entonces:

Intent intent = new Intent(Intent.ACTION_DELETE);
intent.setData(Uri.parse("package:com.example.mypackage"));
startActivity(intent);

para desinstalar. Parece más fácil...

JohnyTex avatar Feb 18 '2014 12:02 JohnyTex

Actualmente, esto no está disponible para aplicaciones de terceros. Tenga en cuenta que incluso usar la reflexión u otros trucos para acceder a installPackage() no ayudará, porque solo las aplicaciones del sistema pueden usarlo. (Esto se debe a que es el mecanismo de instalación de bajo nivel, después de que el usuario haya aprobado los permisos, por lo que no es seguro que las aplicaciones normales tengan acceso).

Además, los argumentos de la función installPackage() a menudo han cambiado entre las versiones de la plataforma, por lo que cualquier cosa que haga para intentar acceder a ella fallará en otras versiones de la plataforma.

EDITAR:

También vale la pena señalar que este paquete de instalación se agregó recientemente a la plataforma (¿2.2?) y originalmente no se usó para rastrear quién instaló la aplicación; la plataforma lo usa para determinar a quién iniciar cuando se informan errores. la aplicación, para implementar Android Feedback. (Esta fue también una de las veces que cambiaron los argumentos del método API). Durante al menos un largo tiempo después de su introducción, Market todavía no lo usó para rastrear las aplicaciones que había instalado (y es muy posible que todavía no lo use). ), sino que simplemente usó esto para configurar la aplicación de comentarios de Android (que estaba separada de Market) como el "propietario" para encargarse de los comentarios.

hackbod avatar Oct 09 '2011 18:10 hackbod

El nivel de API 14 introdujo dos nuevas acciones: ACTION_INSTALL_PACKAGE y ACTION_UNINSTALL_PACKAGE . Esas acciones le permiten pasar EXTRA_RETURN_RESULT valor booleano extra para obtener una notificación del resultado de la (des)instalación.

Código de ejemplo para invocar el cuadro de diálogo de desinstalación:

String app_pkg_name = "com.example.app";
int UNINSTALL_REQUEST_CODE = 1;

Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);  
intent.setData(Uri.parse("package:" + app_pkg_name));  
intent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
startActivityForResult(intent, UNINSTALL_REQUEST_CODE);

Y recibe la notificación en tu método Activity#onActivityResult :

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == UNINSTALL_REQUEST_CODE) {
        if (resultCode == RESULT_OK) {
            Log.d("TAG", "onActivityResult: user accepted the (un)install");
        } else if (resultCode == RESULT_CANCELED) {
            Log.d("TAG", "onActivityResult: user canceled the (un)install");
        } else if (resultCode == RESULT_FIRST_USER) {
            Log.d("TAG", "onActivityResult: failed to (un)install");
        }
    }
}
Pir Fahim Shah avatar Jul 15 '2014 11:07 Pir Fahim Shah