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

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

Ответ 1

Ваша первая ставка - посмотреть на Android native 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 является основной установкой, и последнее, что она делает, это вызов функции 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 installPackage, посмотрите PackageManagerService.

Резюме

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

Ответ 2

Я проверил, как 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.
Но это потому, что оболочка всегда # вместо $ на этом устройстве.

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

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

Ответ 3

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

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

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

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

Ответ 4

Недавно я реализовал установку без согласия пользователя - это приложение для киоска для уровня 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 (однако ПОСЛЕ того, как вы установили приложение для обновления, оно работает 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 по какой-то причине не подхвачено.

Ответ 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();
        }
     }
}

Ответ 6

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

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

Ответ 7

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

Ответ 8

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

Ответ 9

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

Ответ 10

Я сделал тестовое приложение для бесшумной установки, используя метод 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. Возможно, он будет работать с более ранними версиями.

Ответ 11

Попробуйте выполнить LD_LIBRARY_PATH=/vendor/lib:/system/lib до pm install. Он работает хорошо.

Ответ 12

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

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

Ответ 13

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

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

Ответ 14

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

Ваш 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 и работало хорошо.

Ответ 15

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