Установка приложений в автоматическом режиме с предоставленным разрешением на установку пакетов
Я пытаюсь молча установить apk в систему. Мое приложение находится в /system / app и успешно предоставлено разрешение "android.разрешение.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
?
PS I я пишу приложение, которое установит много apks из папки в систему при первом запуске (заменить Мастер Установки). Мне это нужно, чтобы сделать прошивку легче.
Если вы думаете, что я пишу вирус: все программы установлены в /data/app. Разрешение Install_packages может быть предоставлено только программам системного уровня, расположенным в /system / app или подписанным системным ключом. Так что вирус не может туда попасть.
Как сказал http://www.mail-archive.com/android-porting@googlegroups.com/msg06281.html приложения могут быть установлены без вывода сообщений, если у них есть разрешение install_packages. Кроме того, вам не нужно разрешение Install_packages для установки пакетов не молча. Плюс http://www.androidzoom.com/android_applications/tools/silent-installer_wgqi.html
14 ответов:
ваша первая ставка-заглянуть в родной Android PackageInstaller. Я бы рекомендовал изменить это приложение так, как вам нравится, или просто извлечь необходимую функциональность.
в частности, если вы посмотрите на 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(); } }
тогда вы заметите, что фактический установщик находится в InstallAppProgress класса. Осматривая этот класс, вы обнаружите, что
initView
is основная функция установщика, и последнее, что он делает, это вызовPackageManager
' sinstallPackage
функция:public void initView() { ... pm.installPackage(mPackageURI, observer, installFlags, installerPackageName); }
следующий шаг-проверить PackageManager, который является абстрактным классом. Вы найдете
Я проверил, как ADB устанавливает приложения.
- Он копирует APK в /data / local / tmp
- он работает ' shell: 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), и это сработало как шарм.
(Также обычно устанавливается, а не в /system или что-то еще)
AppInstall даже не спрашивал разрешения root.
Но это потому, что оболочка всегда#
вместо$
на этом устройстве.Кстати, вам нужен root для установки приложения в /system, правильно?
Я попробовал adb перемонтировать на некорневом устройстве и получил:remount failed: Operation not permitted.
Поэтому я не мог попробовать все системы на некорневых устройства.Вывод: вы должны использовать корневое устройство
Надеюсь, это поможет :)
вы должны определить
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
в манифесте, то если вы находитесь в системном разделе (/system / app) или у вас есть приложение, подписанное производителем, вы будете иметь разрешение INSTALL_PACKAGES.
мое предложение-создать небольшой android-проект с уровнем совместимости 1.5, используемый для вызова installPackages через отражение и экспорта jar с методами установки пакетов и вызова реальных методов. После этого, путем импортировать опарник внутри ваш проект вы будете готовы к установке пакетов.
недавно я реализовывал установку без согласия пользователя - это было приложение киоска для уровня API 21+, где у меня был полный контроль над средой.
основные требования
- уровень API 21+
- root access чтобы установить программу обновления в качестве системного привилегированного приложения.
следующий метод считывает и устанавливает 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
разрешение, или код выше не будет работать молча<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
чтобы получить это разрешение, вы должны установить APK как системное приложение, которое требует root (однако после установки приложения updater он, кажется, работает без корня)
для установки в качестве системного приложения я создал подписанный 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
почему-то не подобран.
я попробовал на укоренившихся 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, корень не требуется.
вы можете использовать скрытый 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 через отражение и сделал интерфейс android.content.pm.IPackageInstallObserver в моей папке src (потому что он скрыт в android.content.pm пакет).
когда я запускаю installPackage, я получил SecurityException с указанием строки, что мое приложение не имеет android.разрешение.INSTALL_PACKAGES, но он определен в AndroidManifest.XML.
Так что, я думаю, это не возможно использовать этот метод.
PS. Я тестировал на Android SDK 2.3 и 4.0. Возможно, он будет работать с более ранними версиями.
стороннее приложение не может установить приложение Android скользко. Однако стороннее приложение может попросить ОС Android Установить приложение.
поэтому вы должны определить этот:
Intent intent = new Intent(Intent.ACTION_VIEW); intent.setDataAndType(Uri.parse("file:///sdcard/app.apk", "application/vnd.android.package-archive"); startActivity(intent);
Вы можете также попробовать установить его в качестве системного приложения, чтобы разрешить и игнорировать это определение. (Требуется Root)
вы можете запустить следующую команду на вашем стороннем приложении, чтобы установить приложение на корневом устройстве.
код это:
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 / bash
f=/home/cox/myapp.apk #or 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, на терминале оболочки.