Установка приложений в автоматическом режиме с предоставленным разрешением INSTALL_PACKAGES - PullRequest
82 голосов
/ 27 апреля 2011

Я пытаюсь тихо установить 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

Ответы [ 15 ]

59 голосов
/ 27 апреля 2011

Ваша первая ставка - заглянуть в собственный 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 является основной установочной функцией, и последнее, что он делает, это вызывает installPackage функцию PackageManager:

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 installPackage, взгляните на PackageManagerService .

Сводка

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

12 голосов
/ 20 мая 2012

Я проверил, как АБР устанавливает приложения.
- Он копирует 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.
Кажется, что ошибка выдается вечера, а не AppInstall.
Потому что SecurityException не перехватывается AppInstall, и приложение не падает.

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

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

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

9 голосов
/ 05 ноября 2016

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

8 голосов
/ 13 февраля 2012

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

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

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

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

5 голосов
/ 20 февраля 2014

Я пробовал на рутированном 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();
        }
     }
}
4 голосов
/ 27 ноября 2014

Вы можете использовать скрытый 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 в файле манифеста.

4 голосов
/ 19 июля 2011

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

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

3 голосов
/ 29 августа 2013

Вы можете просто использовать команду 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();
        }
     }
  }
0 голосов
/ 15 марта 2019

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

Но потом я нашел эти статьи очень полезными. Так как я делаю "принадлежащие компании" устройства.

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

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

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

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

0 голосов
/ 27 января 2019

Возможна тихая установка на Android 6 и выше. Используя функцию, предоставленную в ответе Бориса Треухова, игнорируйте все остальное в посте, root тоже не требуется.

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

...