Установка приложений в автоматическом режиме с предоставленным разрешением на установку пакетов


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

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 ' s installPackage функция:

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

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

стороннее приложение не может установить приложение 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, на терминале оболочки.