Устанавливать приложения в автоматическом режиме с предоставленным разрешением 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);

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

PS Я пишу приложение, которое при первом запуске установит множество 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

POMATu
источник
9
@Fosco Предположим, он делает такое устройство как производитель оригинального оборудования?
dascandy 06
41
@Fosco Какая разница? Это правильный вопрос, как описывает dascandy. Как правило, непродуктивно, когда люди отвечают на подобные вопросы словами: «Вы не должны этого делать». Какое понимание мы получаем с таким ответом?
ZachM 07
3
Кстати о непродуктивном ... ты опоздал на вечеринку, Зак, чуть более чем на 2 года, и вопрос выглядит совсем иначе, чем был изначально.
Fosco
2
@Fosco LOL. Также я могу подтвердить, что это все еще работает в новейшей ОС Android.
POMATu
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«s 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файла 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", new Class [] {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.
Поскольку AppInstall не перехватывает SecurityException, и приложение не аварийно завершает работу.

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

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

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

FrankkieNL
источник
Кажется, я не могу заставить это работать. Интересно, что команда отлично работает в оболочке ADB, а затем, похоже, не работает, когда я пытаюсь запустить ее программно.
saulpower 01
Да, у меня та же проблема - работает в оболочке, но не при вызове из моего приложения, даже если я вызываю его через su)
velis
Есть ли другой способ работать на устройствах Google, например, получить права суперадминистратора?
Рамеш Кумар,
Если вам нужны права суперадминистратора, вы должны: 1) получить root-права на своем устройстве, 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 как системное приложение, которое ТРЕБУЕТ root (однако ПОСЛЕ того, как вы установили приложение для обновления, оно, похоже, работает БЕЗ root)

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

adb push updater.apk /sdcard/updater.apk

а затем переместил его в system/priv-app- что требует перемонтирования ФС (вот почему требуется рут)

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 Он работал с подписанной версией выпуска и не работал как с подписанной, так и с неподписанной отладочной версией - мне кажется странным, что он не работал с подписанной версией отладки, но у меня не было времени исследовать. Тем не менее, системный журнал logcat очень важен, чтобы узнать, установлен APK или нет.
Борис Треухов
Действительно, по какой-то причине установка в качестве выпуска была ключом к получению разрешения INSTALL_PACKAGES. Приложение могло работать в подписанной отладке из Priv-app, но запрос на разрешение был отклонен. Большое спасибо!
JM Lord
@BorisTreukhov. Я могу уточнить у вас, для чего нужен параметр packageName? Я изменил InputStream на FileInputStream, но получаю это сообщение об ошибке: Failed to parse /data/app/vmdl676191029.tmp/COSU
как установить приложение системы Priv после этих команд?
user2323471
8

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

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

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

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

iLREDEIMATTI
источник
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
Речь шла об установке приложений без рута, но с системным разрешением INSTALL_PACKAGES. Я вижу "su" в вашем ответе, значит для этого требуется root.
POMATu
Большое спасибо за этот ответ! У меня был тот же код, но я использовал «adb install -r» вместо «pm install -r», и он НЕ работал у меня. Вы сэкономили мне здесь много времени!
Jeff.H
@Vladmir, мне нужна твоя помощь. Можешь дать мне немного времени? Буду вам очень признателен
twana eng
4

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

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

POMATu
источник
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в своем файле манифеста.

Герман Пуильпре
источник
Вы тестировали этот код на Lollipop и Marshamallow? Мне кажется, это отличный подход, и я хочу его использовать
Фархад Фагихи
Я тестировал от ICS до Lollipop
Hermann Poilpre
Строка, на которую следует обратить внимание: «ваше приложение должно быть системным». Я почти уверен, что это сработает с леденцом или чем-то еще - при условии, что ваше приложение является «системным», то есть ваше приложение имеет системные разрешения (в основном root). В этом случае вы также можете использовать установщик из командной строки.
Lassi Kinnunen
1
как вызвать метод installPackage? То есть, например, как мне получить IPackageInstallObserver?
phoebus
Какое значение имеет 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();
        }
     }
  }
ZeeShaN AbbAs
источник
Вот что подразумевается под StringUtils? Столкнулся с ошибкой, поскольку не может быть решен?
AndroidOptimist 03
извините, я должен был объяснить. это мой собственный класс для строк, и этот метод проверяет правильность пути. Вы можете игнорировать это
ZeeShaN AbbAs 06
3
что такое класс StringUtil. Вы можете опубликовать метод StringUtil.insertEscape () ?. Потому что, если я прокомментирую эту строку, я получаю исключение Ошибка при запуске exec (). Команда: [su, -c, adb install -r /storage/sdcard0/Android/data/com.install.file Заранее спасибо
sandeepmaaram
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, работает хорошо.

Феб
источник
мне нужна ваща помощь. Можешь дать мне немного времени? Буду вам очень признателен
twana eng
Мне нужна твоя помощь. Мне нужно знать, как мы можем создать свой собственный AOSP
twana rus
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). Также я могу удалять приложения без уведомления с помощью этого метода, проверьте другой мой вопрос, связанный с удалением.
POMATu
Да, работает, но только с правами root. И невозможно установить приложение в систему / приложение на устройстве без рутирования.
Андрей Москвичёв
Не с root, а с разрешениями на уровне системы. Вы можете добавить его в / system / app, если вы производитель прошивки. В моем случае - я (прошу производителей устройств добавлять мои апки в прошивки). Вы не сможете устанавливать приложения в автоматическом режиме каким-либо другим способом - в противном случае это была бы большая дыра в безопасности.
POMATu
Насчет дыры в безопасности - абсолютно согласен с вами. И для меня будет сюрпризом, если этот метод работает с любым приложением. Как я могу установить приложение в / system / app без рутирования? Я не производитель.
Андрей Москвичёв
1
Без рута и перепрошивки нельзя.
POMATu
0

Попробуйте это LD_LIBRARY_PATH=/vendor/lib:/system/libперед установкой вечера. Это работает хорошо.

Анкита Гуджар
источник
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 и выше. Используя функцию, предоставленную в ответе Бориса Треухова, игнорируйте все остальное в сообщении, рут также не требуется.

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

Raymie
источник
0

Я проверил все ответы и пришел к выводу, что сначала у вас должен быть root-доступ к устройству, чтобы оно работало.

Но потом я нашел эти статьи очень полезными. С тех пор, как делаю "фирменные" устройства.

Как обновить приложение для Android в автоматическом режиме без взаимодействия с пользователем

Владелец устройства Android - минимальное приложение

Вот документация Google об "управляемом устройстве"

Полностью управляемое устройство

ч - п
источник
0

вы можете использовать это в терминале или оболочке

adb shell install -g MyApp.apk

увидеть больше в разработке 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 на терминале оболочки.

рулевой
источник