Мое приложение устанавливает другие приложения, и ему необходимо отслеживать, какие приложения оно установило. Конечно, этого можно добиться, просто ведя список установленных приложений. Но в этом не должно быть необходимости! PackageManager должен нести ответственность за поддержание отношения installedBy (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 = 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. (Это также был один из случаев, когда менялись аргументы метода API.) По крайней мере, долгое время после того, как он был представлен, Market все еще не использовал его для отслеживания установленных приложений (и вполне может, что он все еще не использует его. ), но вместо этого просто использовал это, чтобы установить приложение Android Feedback (которое было отдельным от Market) в качестве «владельца», чтобы заботиться об обратной связи.
источник
Android P + требует это разрешение в AndroidManifest.xml
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
Затем:
Intent intent = new Intent(Intent.ACTION_DELETE); intent.setData(Uri.parse("package:com.example.mypackage")); startActivity(intent);
удалить. Вроде проще ...
источник
onDestroy()
методе?Уровень API 14 представил два новых действия: ACTION_INSTALL_PACKAGE и ACTION_UNINSTALL_PACKAGE . Эти действия позволяют передать дополнительное логическое значение EXTRA_RETURN_RESULT для получения уведомления о результате (не) установки.
Пример кода для вызова диалогового окна удаления:
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"); } } }
источник
ACTION_INSTALL_PACKAGE
потребуетсяREQUEST_INSTALL_PACKAGES
разрешение уровня подписи . Точно так же, начиная с API 28 (Android P), для вызоваACTION_UNINSTALL_PACKAGE
потребуется неопасноеREQUEST_DELETE_PACKAGES
разрешение. По крайней мере, согласно документам.Если у вас есть разрешение владельца устройства (или владельца профиля, я не пробовал), вы можете автоматически устанавливать / удалять пакеты с помощью 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; }
источник
Единственный способ получить доступ к этим методам - это отражение. Вы можете получить дескриптор
PackageManager
объекта, вызвавgetApplicationContext().getPackageManager()
и используя эти методы с помощью отражения. Ознакомьтесь с этим руководством.источник
Согласно исходному коду 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()
определяется здесь.источник
Если вы передаете имя пакета в качестве параметра любой из ваших пользовательских функций, используйте следующий код:
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
имя пакета на любое другое, если хотите предложить пользователю удалить другое приложение на устройстве.источник
Предпосылка:
Ваш 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); }
источник