Встановлюйте програми безшумно, з наданим дозволом INSTALL_PACKAGES


86

Я намагаюся мовчки встановити 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);

Але цей код відкриває стандартне діалогове вікно встановлення. Як я можу встановити додаток мовчки без root без дозволу android.permission.INSTALL_PACKAGES?

PS Я пишу програму, яка встановить багато apk з папки в систему при першому запуску (замініть Майстер установки). Вона потрібна мені, щоб зробити прошивку легшою.

Якщо ви вважаєте, що я пишу вірус: усі програми встановлюються в / 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


9
@Fosco Припустимо, він робить такий пристрій, як виробник оригінального обладнання?
dascandy

41
@Fosco Хто піклується? Це слушне запитання, як описує дасканді. Як правило, непродуктивно, коли люди відповідають на подібні запитання "Не слід цього робити". Яке розуміння ми отримуємо завдяки такій формі відповіді?
ZachM

3
Якщо говорити про непродуктивність .. ви затрималися трохи більше ніж на 2 роки з цією вечіркою Заком, і питання виглядає набагато інакше, ніж спочатку.
Фоско

2
@Fosco LOL. Також я можу підтвердити, що це все ще працює в найновіших ОС Android.
POMATу

1
@LarsH Я щойно встановив додаток F-Droid: вони не встановлюють речі мовчки, вони просто підказують менеджеру пакетів Android.
Хоакін Ірчук

Відповіді:


60

Ваша перша ставка - вивчити рідний пакет 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();
    }
}

Тоді ви помітите, що фактичний інсталятор знаходиться в класі InstallAppProgress . Перевіривши цей клас, ви виявите, що initViewце основна функція інсталятора, і останнє, що він робить, це виклик функції PackageManager' installPackage:

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

Наступним кроком є ​​перевірка PackageManager , який є абстрактним класом. Ви знайдете installPackage(...)там функцію. Погана новина полягає в тому, що він позначений @hide. Це означає, що він не доступний безпосередньо (ви не зможете скомпілювати з викликом цього методу).

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

Але ви зможете отримати доступ до цих методів за допомогою роздумів.

Якщо ви зацікавлені в тому, як PackageManager«S installPackageреалізована функція, подивіться на PackageManagerService .

Резюме

Вам потрібно буде отримати об'єкт менеджера пакетів через Context's getPackageManager(). Тоді ви викличете installPackageфункцію за допомогою відображення.


2
Допоможіть! Результатом цієї техніки є: Викликано: java.lang.SecurityException: ні користувач 10101, ні поточний процес не мають android.permission.INSTALL_PACKAGES. Незважаючи на те, що мій додаток має такий дозвіл
gregm

13
Ваш додаток запитує цей дозвіл, але Android не надає цього дозволу стороннім програмам. Перегляньте LogCatжурнали, пов’язані з дозволами.
inazaruk

@inazaruk я читав, що програми, встановлені в / system / app, автоматично отримують дозвіл на використання install_pakages, але, як описано в цій публікації, це, здається, не працює. Ви можете дати мені кілька підказок?
Zorb

3
Як я можу викликати installPackage за допомогою відображення? насправді я не можу зробити Method method = aPackageManagerClass.getMethod ("installPackage", новий клас [] {Uri.class, IPackageInstallObserver.class, Integer.TYPE, String.class}); враховуючи, що також IPackageInstallObserver прихований, може хтось допомогти мені, розмістивши правильний код, щоб зробити тут роздуми.
Shilaghae

7
Усі посилання у відповіді мертві.
Mayur Gangurde

13

Я перевірив, як 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 на некоренованому пристрої і отримав:
remount failed: Operation not permitted.
Ось чому я не міг спробувати річ / system на некоренованому пристрої.

Висновок: вам слід використовувати корінний пристрій,
сподіваюся, це допоможе :)


Здається, я не можу змусити це спрацювати. Цікаво, що команда чудово працює в оболонці ADB, а потім, здається, не працює, коли я намагаюся запустити її програмно.
saulpower

Так, у мене така сама проблема - працює в оболонці, але не при виклику з мого додатка, навіть якщо я зателефоную через su)
velis

Чи існує інший спосіб роботи на пристроях Google, наприклад отримання дозволів супер адміністратора?
Рамеш Кумар,

Якщо вам потрібні дозволи супер адміністратора, вам слід: 1) укорінити пристрій, 2) встановити програму на системному розділі, 3) або програму слід підписати тим же ключем, що і сама ОС.
FrankkieNL

1
Це відбувається тому, що користувач ADB та користувач вашої програми мають різні дозволи
PHPoenX,

11

Нещодавно я впроваджував установку без згоди користувача - це була програма-кіоск для 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дозвіл, інакше наведений вище код не вдасться мовчки

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

щоб отримати цей дозвіл, ви повинні встановити ваш файл .apk як системну програму, яка ПОТРІБНА корінь (однак ПІСЛЯ встановлення програми оновлення вона, здається, працює БЕЗ кореня)

Щоб встановити як системну програму, я створив підписаний файл .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з якихось причин не забрали.


"з якихось причин це не спрацювало з простою версією налагодження" Ви маєте на увазі, що вона працювала у випуску, або якоюсь іншою менш "простою" версією налагодження?
JM Lord

1
@JMLord Це працювало з підписаною версією випуску, і не працювало як із підписаною, так і без підписаної версії налагодження - мені здається дивним, що вона не працювала з підписаною налагоджувальною версією I, але у мене не було часу на дослідження. Тим не менше, журнал системного журналу дуже важливий, щоб з'ясувати, встановлений APK чи ні.
Борис Треухов

Справді, з якихось причин встановлення як випуск було ключем до отримання дозволу INSTALL_PACKAGES. Додаток може працювати в підписаній налагодженні з priv-app, але запит на дозвіл відхилено. Дуже дякую!
JM Lord

@BorisTreukhov Гей, я можу з вами перевірити, для чого призначений параметр packageName? Я змінив InputStream на FileInputStream, але я отримую таке повідомлення про помилку: Не вдалося проаналізувати /data/app/vmdl676191029.tmp/COSU

як встановити системну програму priv після цих команд?
user2323471

8

Ви повинні визначити

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

у вашому маніфесті, тоді, якщо ви перебуваєте в системному розділі (/ system / app) або якщо ваша програма підписана виробником, ви матимете дозвіл INSTALL_PACKAGES.

Моя порада - створити маленький андроїд-проект із рівнем сумісності 1,5, який використовується для виклику installPackages через відображення та експортувати банку з методами для встановлення пакунків та викликати реальні методи. Тоді, імпортуючи jar у свій проект, ви будете готові до встановлення пакунків.


5

Я спробував укорінений 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();
        }
     }
}

2
Питання було про встановлення програм без root, але з системним дозволом "INSTALL_PACKAGES". Я бачу "su" у вашій відповіді, тому для цього потрібен root.
POMATу

Щиро дякую за цю відповідь! У мене був той самий код, але я використовував "adb install -r" замість "pm install -r", і він НЕ буде працювати для мене. Ви заощадили мені тут багато часу!
Джефф. H

@Vladmir мені потрібна твоя допомога. Можеш дати мені трохи часу. Буду дуже вдячний u
twana eng

4

Я не уявляв, як це зробити, тому що тоді ніхто не відповів, і я не знайшов жодної документації щодо цього дозволу. Тож я знайшов своє рішення. Це гірше, ніж ваше, але це рішення в будь-якому випадку.

Я встановив busybox, який встановив дозвіл 777 на / data / app (я не дбаю про безпеку). Потім просто виконується "busybox install" з програми. Це працює, але має значний витік безпеки. Якщо ви встановите дозволи 777, корінь не потрібен.


3
ти використовував такий код? Process install; install = Runtime.getRuntime().exec("/system/bin/busybox install " + myAPK.getAbsolutePath()); int iSuccess = install.waitFor();
Someone Somewhere

4

Ви можете використовувати прихований 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у своєму файлі маніфесту.


Ви тестували цей код на льодяниках і зефірах? Мені здається чудовим підходом, і я хочу ним скористатися
Фархад Фагіхі 02.03.16

Я тестував від ICS до льодяника
Герман Пойльпре

Рядок, який потрібно шукати, - "ваш додаток повинен бути системним". Я майже впевнений, що це може працювати на льодянику чи будь-якому іншому - за умови, що ваш додаток є "системним", тобто ваш додаток має системні дозволи (в основному root). У цьому випадку ви також можете скористатися програмою встановлення командного рядка.
Lassi Kinnunen

1
як я викликаю метод installPackage? Значить, як мені отримати IPackageInstallObserver, наприклад?
фоб

Що таке значення installFlags?
Шивам Кумар,

3

Ви можете просто використовувати команду 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();
        }
     }
  }

Тут що мається на увазі під StringUtils? Чи виникає помилка, оскільки її неможливо вирішити?
AndroidOptimist 03.03.14

вибачте, я повинен був пояснити. це мій спеціальний клас для рядків, і цей метод робить sures правильним шляхом. ви можете це проігнорувати
ZeeShaN AbbAs

3
що таке клас StringUtil. Ви можете опублікувати метод StringUtil.insertEscape () ?. тому що, якщо я коментую цей рядок, я отримую виняток Помилка запуску exec (). Команда: [su, -c, adb install -r /storage/sdcard0/Android/data/com.install.file Заздалегідь
дякую

java.io.IOException: Помилка запуску exec (). Команда: [su, -c, adb install -r /storage/emulated/0/download/SwipeAnimation.apk] Робочий каталог: null Середовище: null - чи можете ви пояснити помилку ..? а якщо є якісь дозволи в маніфесті
Sandeep P

1
@ZeeShaNAbbAs після вирішення проблеми вкорінення .. спасибі
Sandeep P

1

Обов’язкова умова:

Ваш файл .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 і працювало добре.


мені потрібна твоя допомога. Можеш дати мені трохи часу. Буду дуже вдячний u
twana eng

Я хочу вашої допомоги. Мені потрібно знати, як ми можемо створити наш власний
AOSP

0

Я зробив тестовий додаток для безшумних інсталяцій, використовуючи метод PackageManager.installPackage.

Я отримую метод installPackage за допомогою відображення і створюю інтерфейс android.content.pm.IPackageInstallObserver у моїй папці src (оскільки він прихований у пакеті android.content.pm).

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

Отже, я думаю, використовувати цей метод неможливо.

PS. Я тестував на Android SDK 2.3 та 4.0. Можливо, це буде працювати з попередніми версіями.


Він працює в версіях 4.1 та 4.0, а також у будь-якій версії Android, що перевищує 2.1 (включаючи 2.1). Зараз я використовую 4.0 SDK. Вам потрібно додати його в папку / system / app, інакше ваша програма не матиме необхідних дозволів на системному рівні (не root). Також, оскільки я можу безшумно видаляти програми цим методом, перевірте моє інше питання, пов’язане з видаленням.
POMATу

Так, це працює, але лише з дозволами root. І неможливо встановити програму в систему / програму на некоренованому пристрої.
Андрей Москвичёв

Не з root, а з дозволами на рівні системи. Ви можете додати його в / system / app, якщо ви виробник мікропрограми. У моєму випадку - я (прошу виробників пристроїв додати мої apk до прошивки). Ви не зможете іншим способом встановлювати програми безшумно - інакше це було б великою дірою в безпеці.
POMATу

Щодо діри в безпеці - я абсолютно з вами згоден. І це буде для мене несподіванкою, якщо цей метод працює з будь-яким додатком. Як я можу встановити програму в / system / app без вкорінення? Я не виробник.
Андрей Москвичёв

1
Без кореня або перепрошивки ви не можете.
POMATу


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

Ви також можете спробувати встановити його як системну програму, щоб надати дозвіл і проігнорувати це визначення. (Потрібен корінь)

Ви можете запустити таку команду у своїй сторонній програмі, щоб встановити програму на корінному пристрої.

Код:

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

Я сподіваюся, що ця відповідь буде для вас корисною.


0

Це можна зробити безшумною установкою на Android 6 і вище. Використовуючи функцію, надану у відповіді Борисом Треуховим, ігноруйте все інше у дописі, також не потрібно root.

Встановіть свою програму як адміністратор пристрою, ви можете мати повний режим кіоску з тихою установкою оновлень у фоновому режимі.


0

Я перевірив усі відповіді, висновок здається такий: спочатку потрібно мати рут-доступ до пристрою, щоб він працював.

Але тоді я знайшов ці статті дуже корисними. Оскільки я роблю пристрої, що належать компанії.

Як мовчки оновити додаток Android без взаємодії з користувачем

Власник пристрою Android - мінімальний додаток

Ось документація Google про "керований пристрій"

Повністю керований пристрій



-6

! / bin / bash

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, на терміналі оболонки.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.