Установите приложения тихо, с предоставленным разрешением INSTALL_PACKAGES

Я пытаюсь тихо установить apk в систему. Мое приложение расположено в / system / app и успешно предоставлено разрешение «android.permission.INSTALL_PACKAGES»

Однако я не могу найти, как использовать это разрешение. Я попытался скопировать файлы в / data / app и не имел успеха. Также я попытался использовать этот код

Intent intent = new Intent(Intent.ACTION_VIEW); intent.setDataAndType( Uri.parse("file:///sdcard/app.apk"), "application/vnd.android.package-archive"); startActivity(intent); 

Но этот код открывает стандартный диалог установки. Как я могу без android.permission.INSTALL_PACKAGES установить приложение без root с предоставленным android.permission.INSTALL_PACKAGES ?

PS Я пишу приложение, которое установит много apks из папки в систему при первом запуске (замените Мастер установки). Мне нужно, чтобы упростить прошивку.

Если вы думаете, что я пишу вирус: все программы установлены в / data / app. Разрешение Install_packages может быть предоставлено только программам системного уровня, расположенным в / system / app или подписанным с системным ключом. Таким образом, вирус не может попасть туда.

Как сказано в http://www.mail-archive.com/[email protected]/msg06281.html приложениях МОЖЕТ быть установлена ​​без установки, если у них есть разрешение install_packages. Кроме того, вам не требуется разрешение Install_packages для установки пакетов не тихо. Плюс http://www.androidzoom.com/android_applications/tools/silent-installer_wgqi.html

Ваша первая ставка – посмотреть на собственный пакет PackageInstaller от Android. Я бы рекомендовал изменить это приложение так, как вам нравится, или просто извлечь требуемую функциональность.


В частности, если вы посмотрите на PackageInstallerActivity и его метод onClickListener :

  public void onClick(View v) { if(v == mOk) { // Start subactivity to actually install the application Intent newIntent = new Intent(); ... newIntent.setClass(this, InstallAppProgress.class); ... startActivity(newIntent); finish(); } else if(v == mCancel) { // Cancel and finish finish(); } } 

Затем вы заметите, что фактический установщик находится в classе InstallAppProgress . initView этот class, вы обнаружите, что initView – это основная функция установщика, и последнее, что он делает, это вызвать функцию installPackage :

 public void initView() { ... pm.installPackage(mPackageURI, observer, installFlags, installerPackageName); } 

Следующим шагом является проверка PackageManager , который является абстрактным classом. Здесь вы найдете installPackage(...) . Плохая новость заключается в том, что она отмечена знаком @hide. Это означает, что он не доступен напрямую (вы не сможете скомпилировать его с вызовом этого метода).

  /** * @hide * .... */ public abstract void installPackage(Uri packageURI, IPackageInstallObserver observer, int flags,String installerPackageName); 

Но вы сможете получить доступ к этим методам посредством отражения.

Если вас интересует, как installPackage функция installPackage , взгляните на PackageManagerService .

Резюме

Вам нужно будет получить объект менеджера пакетов через getPackageManager() Context . Затем вы вызываете функцию installPackage через reflection.

Я проверил, как ADB устанавливает приложения.
– Он копирует APK в / data / local / tmp
– он запускает оболочку: pm install /data/local/tmp/app.apk ‘

Я попытался воспроизвести это поведение, выполнив: (на ПК, используя usb-кабель)
adb push app.apk /sdcard/app.apk
adb shell
$ pm install /sdcard/app.apk
Это работает. Приложение установлено.

Я сделал приложение (с именем AppInstall), которое должно установить другое приложение.
(установленное нормально, не закрепленное устройство)
Оно делает:
Runtime.getRuntime().exec("pm install /sdcard/app.apk").waitFor();
Но это дает ошибку:
java.lang.SecurityException: Neither user 10019 nor current process has android.permission.INSTALL_PACKAGES.
Похоже, что ошибка вызывается pm, а не AppInstall.
Поскольку SecurityException не улавливается AppInstall, и приложение не сбой.

Я пробовал то же самое на корневом устройстве (такое же приложение и AppInstall), и он работал как шарм.
(Также нормально установлен, не в / системе или что-то еще)
AppInstall даже не просил разрешения root.
Но это потому, что shell всегда # вместо $ на этом устройстве.

Кстати, вам нужно root для установки приложения в / system, правильно?
Я попробовал adb remount на неуправляемом устройстве и получил:
remount failed: Operation not permitted.
Вот почему я не мог попробовать / system на ненарушенном устройстве.

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

Вы должны определить

  

в вашем манифесте, то, если вы находитесь в системном разделе (/ system / app) или у вас есть приложение, подписанное производителем, у вас будет разрешение INSTALL_PACKAGES.

Мое предложение состоит в том, чтобы создать небольшой проект андроида с 1,5 уровня совместимости, используемый для вызова installPackages посредством отражения и для экспорта флага с помощью методов для установки пакетов и вызова реальных методов. Затем, импортируя банку в свой проект, вы будете готовы к установке пакетов.

Я недавно внедрял установку без согласия пользователя – это приложение для киоска для уровня API 21+, где я имел полный контроль над средой.

Основные требования:

  • Уровень API 21+
  • root для установки обновления в качестве привилегированного приложения с системой.

Следующий метод считывает и устанавливает APK из InputStream:

 public static boolean installPackage(Context context, InputStream in, String packageName) throws IOException { PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller(); PackageInstaller.SessionParams params = new PackageInstaller.SessionParams( PackageInstaller.SessionParams.MODE_FULL_INSTALL); params.setAppPackageName(packageName); // set params int sessionId = packageInstaller.createSession(params); PackageInstaller.Session session = packageInstaller.openSession(sessionId); OutputStream out = session.openWrite("COSU", 0, -1); byte[] buffer = new byte[65536]; int c; while ((c = in.read(buffer)) != -1) { out.write(buffer, 0, c); } session.fsync(out); in.close(); out.close(); Intent intent = new Intent(context, MainActivity.class); intent.putExtra("info", "somedata"); // for extra data if needed.. Random generator = new Random(); PendingIntent i = PendingIntent.getActivity(context, generator.nextInt(), intent,PendingIntent.FLAG_UPDATE_CURRENT); session.commit(i.getIntentSender()); return true; } 

Следующий код вызывает установку

  try { InputStream is = getResources().openRawResource(R.raw.someapk_source); installPackage(MainActivity.this, is, "com.example.apk"); } catch (IOException e) { Toast.makeText(MainActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show(); } 

для всего, что нужно работать, вам отчаянно требуется разрешение INSTALL_PACKAGES , или код выше не будет работать молча

  

чтобы получить это разрешение, вы должны установить APK как системное приложение, которое НЕОБХОДИМО использовать root (однако ПОСЛЕ того, как вы установили приложение для обновления, оно, похоже, работает WITHOUT root)

Для установки в качестве системного приложения я создал подписанный APK и нажал его

 adb push updater.apk /sdcard/updater.apk 

а затем переместил его в system/priv-app – для чего требуется перезагрузка FS (поэтому необходим корень)

 adb shell su mount -o rw,remount /system mv /sdcard/updater.apk /system/priv-app chmod 644 /system/priv-app/updater.apk 

по какой-то причине он не работал с простой отладочной версией, но logcat показывает полезную информацию, если по priv-app либо причине ваше приложение в priv-app не подхвачено.

Я пробовал на корневом Android 4.2.2, и этот метод работает для меня:

 private void installApk(String filename) { File file = new File(filename); if(file.exists()){ try { final String command = "pm install -r " + file.getAbsolutePath(); Process proc = Runtime.getRuntime().exec(new String[] { "su", "-c", command }); proc.waitFor(); } catch (Exception e) { e.printStackTrace(); } } } 

Я понятия не имел, как это сделать, потому что никто не отвечал в то время, и я не нашел документацию об этом разрешении. Поэтому я нашел свое решение. Это хуже, чем ваше, но это решение в любом случае.

Я установил busybox, который установил 777-разрешение / data / app (я не забочусь о безопасности). Затем просто выполнил «busybox install» из приложения. Это работает, но имеет большую утечку безопасности. Если вы устанавливаете разрешения 777, root не требуется.

Вы можете использовать скрытый API android.content.pm.IPackageInstallObserver путем отражения:

 public class PackageManagement { public static final int INSTALL_REPLACE_EXISTING = 0x00000002; public static final int INSTALL_SUCCEEDED = 1; private static Method installPackageMethod; private static Method deletePackageMethod; static { try { installPackageMethod = PackageManager.class.getMethod("installPackage", Uri.class, IPackageInstallObserver.class, Integer.TYPE, String.class); } catch (NoSuchMethodException e) { e.printStackTrace(); } } public static void installPackage(PackageManager pm, Uri mPackageUri, IPackageInstallObserver observer, int installFlags, String installerPackageName) { try { installPackageMethod.invoke(pm, mPackageUri, observer, installFlags, installerPackageName); } catch (Exception e) { e.printStackTrace(); } } } 

Импортируйте файл android.content.pm.IPackageInstallObserver в свой проект. Ваше приложение должно быть системным. Вы должны активировать разрешение android.permission.INSTALL_PACKAGES в вашем файле манифеста.

Вот очень хороший пост на эту тему. Это мне очень помогло.

Вы можете просто использовать команду adb install для автоматической установки / обновления APK. Пример кода ниже

 public static void InstallAPK(String filename){ File file = new File(filename); if(file.exists()){ try { String command; filename = StringUtil.insertEscape(filename); command = "adb install -r " + filename; Process proc = Runtime.getRuntime().exec(new String[] { "su", "-c", command }); proc.waitFor(); } catch (Exception e) { e.printStackTrace(); } } } 

Я сделал тестовое приложение для бесшумной установки, используя метод PackageManager.installPackage.

Я получаю метод installPackage через reflection, и сделал интерфейс android.content.pm.IPackageInstallObserver в моей папке src (потому что он скрыт в пакете android.content.pm).

Когда я запускаю installPackage, я получил SecurityException со строкой, что у моего приложения нет android.permission.INSTALL_PACKAGES, но он определен в AndroidManifest.xml.

Поэтому, я думаю, использовать этот метод невозможно.

PS. Я тестировал на Android SDK 2.3 и 4.0. Возможно, он будет работать с более ранними версиями.

Попробуйте это LD_LIBRARY_PATH=/vendor/lib:/system/lib до pm install. Это работает хорошо.

Стороннее приложение не может установить приложение для Android. Однако стороннее приложение может попросить ОС Android установить приложение.

Поэтому вы должны определить это:

 Intent intent = new Intent(Intent.ACTION_VIEW); intent.setDataAndType(Uri.parse("file:///sdcard/app.apk", "application/vnd.android.package-archive"); startActivity(intent); 

Вы также можете попытаться установить его как системное приложение для предоставления разрешения и игнорировать это определение. (Требуется корень)

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

Код:

 private void installApk(String filename) { File file = new File(filename); if(file.exists()){ try { final String command = "pm install -r " + file.getAbsolutePath(); Process proc = Runtime.getRuntime().exec(new String[] { "su", "-c", command }); proc.waitFor(); } catch (Exception e) { e.printStackTrace(); } } } 

Я надеюсь, что этот ответ будет полезен для вас.

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

Ваш 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); } 

===> Протестировано на Android 8.1 и работает хорошо.

! / Bin / Баш

 f=/home/cox/myapp.apk #or $1 if input from terminal. #backup env var backup=$LD_LIBRARY_PATH LD_LIBRARY_PATH=/vendor/lib:/system/lib myTemp=/sdcard/temp.apk adb push $f $myTemp adb shell pm install -r $myTemp #restore env var LD_LIBRARY_PATH=$backup 

Это работает для меня. Я запускаю это на ubuntu 12.04, на терминале оболочки.

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