установить / удалить APK программно (PackageManager vs Intents)

Мое приложение устанавливает другие приложения, и ему необходимо отслеживать, какие приложения он установил. Конечно, этого можно добиться, просто сохранив список установленных приложений. Но это не обязательно! Администратор PackageManager должен поддерживать поддерживаемые отношения b (a, b). Фактически, согласно API, это:

public abstract String getInstallerPackageName (String packageName) – Получить имя пакета приложения, которое установило пакет. Это определяет, на каком рынке появился пакет.

Текущий подход

Установка APK с использованием Intent

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

Удалите APK с помощью Intent:

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

Это, конечно, не так, например, Android Market устанавливает / удаляет пакеты. Они используют более богатую версию PackageManager. Это можно увидеть, загрузив исходный код Android из репозитория Android Git. Ниже приведены два скрытых метода, которые соответствуют подходу Intent. К сожалению, они недоступны для внешних разработчиков. Но, возможно, они будут в будущем?

Лучший подход

Установка APK с помощью 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); 

Удаление APK с помощью 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); 

Различия

  • При использовании намерений локальный менеджер пакетов не получает информацию о том, из какого приложения возникла установка. В частности, getInstallerPackageName (…) возвращает null.

  • Скрытый метод installPackage (…) принимает имя пакета установщика в качестве параметра и, скорее всего, способен установить это значение.

Вопрос

Можно ли указать имя установщика пакетов с использованием намерений? (Может быть, имя пакета установки может быть добавлено в дополнение к намерению установки?)

Совет. Если вы хотите загрузить исходный код Android, вы можете выполнить следующие действия: Загрузка исходного дерева. Чтобы извлечь файлы * .java и поместить их в папки в соответствии с иерархией пакетов, вы можете проверить этот чистый сценарий: Открыть исходный код Android в Eclipse .

В настоящее время это недоступно для сторонних приложений. Обратите внимание, что даже использование отражений или других трюков для доступа к installPackage () не поможет, потому что только системные приложения могут его использовать. (Это связано с тем, что это механизм установки на низком уровне, после того, как разрешения были одобрены пользователем, поэтому для обычных приложений это небезопасно.)

Также аргументы функции installPackage () часто меняются между версиями платформ, поэтому все, что вы пытаетесь получить доступ к нему, не будет работать на других версиях платформы.

РЕДАКТИРОВАТЬ:

Также стоит отметить, что этот файл installerPackage был добавлен сравнительно недавно на платформу (2.2?) И изначально не использовался для отслеживания того, кто установил приложение – он используется платформой для определения того, кто запускается при создании отчетов об ошибках с помощью приложение для реализации Android Feedback. (Это также было время, когда изменились аргументы метода API.) По крайней мере, после того, как он был введен, Market по-прежнему не использовал его для отслеживания установленных приложений (и он может очень сильно не использовать его ), но вместо этого просто использовал это, чтобы установить приложение Android Feedback (которое было отделено от Market) как «владелец», чтобы позаботиться о обратной связи.

[Удаление]

Как насчет:

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

для удаления. Кажется, проще …

API уровня 14 представил два новых действия: ACTION_INSTALL_PACKAGE и ACTION_UNINSTALL_PACKAGE . Эти действия позволяют вам передать EXTRA_RETURN_RESULT boolean extra, чтобы получить уведомление об окончании установки (un).

Пример кода для вызова диалогового windows удаления:

 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); 

И получите уведомление в методе 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"); } } } 

Если у вас есть владелец устройства (или владелец профиля, я не пробовал), вы можете без проблем устанавливать / удалять пакеты с помощью API-интерфейсов владельцев устройств.

для удаления:

 public boolean uninstallPackage(Context context, String packageName) { ComponentName name = new ComponentName(MyAppName, MyDeviceAdminReceiver.class.getCanonicalName()); PackageManager packageManger = context.getPackageManager(); if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { PackageInstaller packageInstaller = packageManger.getPackageInstaller(); PackageInstaller.SessionParams params = new PackageInstaller.SessionParams( PackageInstaller.SessionParams.MODE_FULL_INSTALL); params.setAppPackageName(packageName); int sessionId = 0; try { sessionId = packageInstaller.createSession(params); } catch (IOException e) { e.printStackTrace(); return false; } packageInstaller.uninstall(packageName, PendingIntent.getBroadcast(context, sessionId, new Intent("android.intent.action.MAIN"), 0).getIntentSender()); return true; } System.err.println("old sdk"); return false; } 

и установить пакет:

 public boolean installPackage(Context context, String packageName, String packagePath) { ComponentName name = new ComponentName(MyAppName, MyDeviceAdminReceiver.class.getCanonicalName()); PackageManager packageManger = context.getPackageManager(); if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { PackageInstaller packageInstaller = packageManger.getPackageInstaller(); PackageInstaller.SessionParams params = new PackageInstaller.SessionParams( PackageInstaller.SessionParams.MODE_FULL_INSTALL); params.setAppPackageName(packageName); try { int sessionId = packageInstaller.createSession(params); PackageInstaller.Session session = packageInstaller.openSession(sessionId); OutputStream out = session.openWrite(packageName + ".apk", 0, -1); readTo(packagePath, out); //read the apk content and write it to out session.fsync(out); out.close(); System.out.println("installing..."); session.commit(PendingIntent.getBroadcast(context, sessionId, new Intent("android.intent.action.MAIN"), 0).getIntentSender()); System.out.println("install request sent"); return true; } catch (IOException e) { e.printStackTrace(); return false; } } System.err.println("old sdk"); return false; } 

Единственный способ получить доступ к этим методам – ​​это reflection. Вы можете получить дескриптор объекта getApplicationContext().getPackageManager() вызвав getApplicationContext().getPackageManager() и используя reflection доступа к этим методам. Оформить этот урок.

Согласно исходному коду Froyo, дополнительный ключ Intent.EXTRA_INSTALLER_PACKAGE_NAME запрашивается для имени пакета установщика в PackageInstallerActivity.

На корневом устройстве вы можете использовать:

 String pkg = context.getPackageName(); String shellCmd = "rm -r /data/app/" + pkg + "*.apk\n" + "rm -r /data/data/" + pkg + "\n" // TODO remove data on the sd card + "sync\n" + "reboot\n"; Util.sudo(shellCmd); 

Util.sudo() определяется Util.sudo() .

Если вы передаете имя пакета в качестве параметра в любую вашу пользовательскую функцию, используйте следующий код:

  Intent intent=new Intent(Intent.ACTION_DELETE); intent.setData(Uri.parse("package:"+packageName)); startActivity(intent); 

Если вы используете Kotlin, API 14+ и просто хотите показать диалог удаления для вашего приложения:

 startActivity(Intent(Intent.ACTION_UNINSTALL_PACKAGE).apply { data = Uri.parse("package:$packageName") }) 

Вы можете изменить имя packageName на любое другое имя пакета, если хотите, чтобы пользователь packageName удалить другое приложение на устройстве

Необходимое условие:

Ваш APK должен быть подписан системой, как указано ранее. Один из способов добиться этого – создать изображение AOSP самостоятельно и добавить исходный код в сборку.

Код:

После установки в качестве системного приложения вы можете использовать методы диспетчера пакетов для установки и удаления APK следующим образом:

Установка:

 public boolean install(final String apkPath, final Context context) { Log.d(TAG, "Installing apk at " + apkPath); try { final Uri apkUri = Uri.fromFile(new File(apkPath)); final String installerPackageName = "MyInstaller"; context.getPackageManager().installPackage(apkUri, installObserver, PackageManager.INSTALL_REPLACE_EXISTING, installerPackageName); return true; } catch (Exception e) { e.printStackTrace(); return false; } } 

Удаление:

 public boolean uninstall(final String packageName, final Context context) { Log.d(TAG, "Uninstalling package " + packageName); try { context.getPackageManager().deletePackage(packageName, deleteObserver, PackageManager.DELETE_ALL_USERS); return true; } catch (Exception e) { e.printStackTrace(); return false; } } 

Чтобы иметь обратный вызов после установки / удаления APK, вы можете использовать это:

 /** * Callback after a package was installed be it success or failure. */ private class InstallObserver implements IPackageInstallObserver { @Override public void packageInstalled(String packageName, int returnCode) throws RemoteException { if (packageName != null) { Log.d(TAG, "Successfully installed package " + packageName); callback.onAppInstalled(true, packageName); } else { Log.e(TAG, "Failed to install package."); callback.onAppInstalled(false, null); } } @Override public IBinder asBinder() { return null; } } /** * Callback after a package was deleted be it success or failure. */ private class DeleteObserver implements IPackageDeleteObserver { @Override public void packageDeleted(String packageName, int returnCode) throws RemoteException { if (packageName != null) { Log.d(TAG, "Successfully uninstalled package " + packageName); callback.onAppUninstalled(true, packageName); } else { Log.e(TAG, "Failed to uninstall package."); callback.onAppUninstalled(false, null); } } @Override public IBinder asBinder() { return null; } } /** * Callback to give the flow back to the calling class. */ public interface InstallerCallback { void onAppInstalled(final boolean success, final String packageName); void onAppUninstalled(final boolean success, final String packageName); } 
Interesting Posts

Пользовательский десериализатор JSON с использованием Gson

Каковы наилучшие методы структурирования большого приложения Meteor со многими файлами шаблонов HTML?

Чтение целого числа из пользовательского ввода

Добавление локального принтера через создание стандартного порта TCP / IP и добавление сетевого принтера

Критерии для гибернации для дат

Отключить события сенсорного экрана WebView в Android

Как его срок службы возвращаемого значения распространяется на область действия вызывающей функции, когда она привязана к константной ссылке в вызывающей функции?

Войдите в gmail, используя Selenium в Python

Просмотр заголовков HTTP в Google Chrome?

Потребляйте нажатие клавиши в autohotkey (не позволяйте ей распространяться в других приложениях)

Функциональное программирование, карта Scala и свернуть влево

Bluetooth Speaker no bass on windows 10 / не отображается в устройствах и принтерах

Использование странных меток для условия IF в цикле DO

Как отключить горячую клавишу Windows + K в Windows 8?

Команда оболочки для суммирования целых чисел, по одной в строке?

Давайте будем гением компьютера.