Установить/Unistall из командной оболочки на Android

Я хочу реализовать в Android безмолвный файл installer-from-apk файл и unistaller-package. Тема в основном обсуждалась в SO и в других местах, но я не могу применить по какой-то причине, что мне не хватает. Масштабы, очевидно, трудно достичь, поскольку в случае успеха это будет серьезное нарушение безопасности в Android. НО, мне нужно реализовать его для специального проекта, а не для потребительского рынка. Существует два подхода:

  • для создания пользовательского ПЗУ из исходного кода (например, AOSP или Cyanogen mod), путем настройки инсталлятора PackageManager (на самом деле просто для удаления диалоговых окон принятия пользователем).
  • сделать это программно, создав процесс как суперпользователя и выполнив "adb shell pm install". Я ранее устанавливал 'su' в /system/xbin, и я тестировал во время выполнения RootTools.rootIsAvailable().

В первом случае я выкопал исходный код Froyo, но попал в тупик с помощью метода @hide. Для второго я сначала попробовал команды из терминала

adb shell pm install /mnt/sdcard/HelloAndroid.apk

и

adb shell pm uninstall com.example.helloandroid

Оба работают нормально. Затем я использовал следующий код: разработка тестировалась на внедренном эмуляторе (2.2 - Froyo):

@Override
    public void onClick(View v) {
        switch (v.getId())
           {
              case R.id.btnInstall:
                  try {  
                      install = Runtime.getRuntime().exec("su\n");   
                      DataOutputStream os = new DataOutputStream(install.getOutputStream());
                      os.writeBytes("pm install /mnt/sdcard/HelloAndroid.apk\n"); 
                      os.writeBytes("exit\n"); 
                      os.flush();
                      install.waitFor();

                              if (install.exitValue() == 0) {  
                                  Toast.makeText(MainActivity.this, "Success!", Toast.LENGTH_LONG).show();
                              }  
                              else {  
                                  Toast.makeText(MainActivity.this, "Failure. Exit code: "+String.valueOf(install.exitValue()), Toast.LENGTH_LONG).show();
                              }
                  }
                  catch (InterruptedException e) {  
                      logError(e);
                  }
                  catch (IOException e) {  
                  logError(e);
                  } 
                  break;

              case R.id.btnUninstall:
                  try {
                      install = Runtime.getRuntime().exec("su\n"); 
                      install=Runtime.getRuntime().exec("pm uninstall "+txtPackageName.getText().toString()+"\n");

                } catch (Exception e) {
                    logError(e);
                }
                  break;
           }

    }

Чтобы избежать опечаток и других обрезков, я жестко запрограммировал параметр файла apk команды для установки; on 'case R.id.btnInstall' команда не выполняется, а выход включен в "Failure" с значением выхода 1, что означает, что "класс не может быть найден"; не знаю, что это значит... Я ценю вашу помощь!

EDITED: у меня есть чистое решение, я отправлю ответ от A-Z, как только у меня появится время и код в правильной форме!

Ответ 1

Как я и обещал здесь, это решение этой проблемы, без какого-либо принуждения к системе, кроме необходимости устанавливать все приложение в каталог /system/app. Я последовал за этим, затем сделал некоторые исправления для отличной статьи здесь: http://paulononaka.wordpress.com/2011/07/02/how-to-install-a-application-in-background-on-android/. Я загрузил zip файл, указанный в статье, затем (я попытался сохранить те же имена классов, где это возможно):

  • создал новый проект и основное действие в качестве точки входа

package com.example.silentinstuninst;

import java.io.File;
import java.lang.reflect.InvocationTargetException;

import com.example.instuninsthelper.ApplicationManager;
import com.example.instuninsthelper.OnDeletedPackage;
import com.example.instuninsthelper.OnInstalledPackage;

import android.os.Bundle;
import android.os.Environment;
import android.app.Activity;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends Activity implements OnClickListener {

    Process install;
    Button btnInstall, btnUninstall;
    EditText txtApkFileName, txtPackageName; 

    public static final String TAG = "SilentInstall/Uninstall";

    private static ApplicationManager am;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initializeValues();

    }

    private void initializeValues() {

        btnInstall = (Button) findViewById(R.id.btnInstall);
        btnUninstall = (Button) findViewById(R.id.btnUninstall);
        txtApkFileName = (EditText) findViewById(R.id.txtApkFilePath);
        txtPackageName = (EditText) findViewById(R.id.txtPackageName);

        btnInstall.setOnClickListener(this);
        btnUninstall.setOnClickListener(this);

        try {
            am = new ApplicationManager(this);
            am.setOnInstalledPackage(new OnInstalledPackage() {

                public void packageInstalled(String packageName, int returnCode) {
                    if (returnCode == ApplicationManager.INSTALL_SUCCEEDED) {
                        Log.d(TAG, "Install succeeded");
                    } else {
                        Log.d(TAG, "Install failed: " + returnCode);
                    }
                }
            });

            am.setOnDeletedPackage(new OnDeletedPackage() {
                public void packageDeleted(boolean succeeded) {
                    Log.d(TAG, "Uninstall succeeded");  
                }
            });

        } catch (Exception e) {
            logError(e);
        }
    }

    private void logError(Exception e) {
        e.printStackTrace();
        Toast.makeText(this, R.string.error+e.getMessage(), Toast.LENGTH_LONG).show();
    }

    @Override
    public void onClick(View v) {
        switch (v.getId())
           {
              case R.id.btnInstall:
                  // InstallUninstall.Install(txtApkFileName.getText().toString());
            try {
                am.installPackage(Environment.getExternalStorageDirectory() +
                        File.separator + txtApkFileName.getText().toString());
            } catch (IllegalArgumentException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            } catch (IllegalAccessException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            } catch (InvocationTargetException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            } // install package
                  break;

              case R.id.btnUninstall:
                  // InstallUninstall.Uninstall(txtPackageName.getText().toString());
            try {
                am.uninstallPackage(txtPackageName.getText().toString());
            } catch (IllegalArgumentException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
                logError(e);
            } catch (IllegalAccessException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
                logError(e);
            } catch (InvocationTargetException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
                logError(e);
            }
                  break;
           }    
    }
}
  1. создайте в /src пакет com.example.instuninsthelper. Я добавил туда файлы ApplicationManager.java и OnInstalledPackage.java.
  2. вставил следующий код внутри класса ApplicationManager:

private OnDeletedPackage onDeletedPackage;
class PackageDeleteObserver extends IPackageDeleteObserver.Stub { 

        public void packageDeleted(boolean succeeded) throws RemoteException {
            if (onDeletedPackage != null) {
                onDeletedPackage.packageDeleted(succeeded);
            }
        }

    }
  1. создается под тем же пакетом com.example.instuninsthelper файл OnDeletedPackage.java со следующим кодом:

package com.example.instuninsthelper;
public interface OnDeletedPackage {
    public void packageDeleted(boolean succeeded);
}
  1. в пакете android.content.pm(пространство имен НЕ ДОЛЖНО меняться). Я изменил IPackageDeleteObserver.java, с этим результатом:

package android.content.pm;

public interface IPackageDeleteObserver extends android.os.IInterface {

    public abstract static class Stub extends android.os.Binder implements android.content.pm.IPackageDeleteObserver {
        public Stub() {
            throw new RuntimeException("Stub!");
        }

        public static android.content.pm.IPackageDeleteObserver asInterface(android.os.IBinder obj) {
            throw new RuntimeException("Stub!");
        }

        public android.os.IBinder asBinder() {
            throw new RuntimeException("Stub!");
        }

        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
                throws android.os.RemoteException {
            throw new RuntimeException("Stub!");
        }
    }

    public abstract void packageDeleted(boolean succeeded)
            throws android.os.RemoteException;
}
  1. создать приложение в Eclipse и развернуть его в эмуляторе
  2. в эмуляторе: главная кнопкa > Настройки > приложения > ... удалить приложение (потому что оно не установлено в /system/app, и нам просто нужно генерировать файл apk)
  3. выполните следующие действия, чтобы запустить эмулятор (чтобы мы могли писать в /system/app; другое решение, которое я использовал, - это создание пользовательского ПЗУ с этим приложением, включенным в /system/app ):
  4. с консоли, перейдите в каталог /bin проекта, затем введите:       * adb push.apk/system/app
  5. наконец, всегда с консоли, введите:       * adb shell am start -n com.example.silentinstuninst/com.example.silentinstuninst.MainActivity
  6. наслаждайтесь!

Ответ 2

Не знаю, но просто идея:

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

Runtime.getRuntime().exec("pm install /mnt/sdcard/HelloAndroid.apk\n"); 

Надеюсь, что это поможет.

Ответ 3

Установка в каталог /system/app по существу такая же, как и для root.

Предполагая, что у вас есть root, проверьте RootTools. Затем вы можете сделать:

if (RootTools.isAccessGiven()) {
    CommandCapture command = new CommandCapture(0, "pm install " + PATH_TO_APK);
    RootTools.getShell(true).add(command).waitForFinish();
}

Обратите внимание, что waitForFinish() является блокирующим вызовом!

Ответ 4

Ну, вы можете сделать это также непосредственно с PackageManager (требуется root-доступ):

  • Создайте приложение с платформой-sdk, которая имеет интерфейсы публично (создайте или загрузите его и настройте eclipse)
  • В приложении прямо вызовите скрытые функции API, которые позволяют без проблем устанавливать/удалять.
  • Установите APK на устройство как системное приложение, скопировав его в /system/app (требуется корень)

Смотрите это: http://forum.xda-developers.com/showthread.php?t=1711653

Ответ 5

Runtime.getRuntime(). exec ( "pm install/mnt/sdcard/HelloAndroid.apk\n" );

Это работает для меня, хотя еще нужно сделать две дополнительные детали:

  • Добавьте android: sharedUserId = "android.uid.system" в AndroidManifest.xml.

  • Подписал apk с системным ключом.

    Но, таким образом, кажется, что нет способа узнать, выполнена ли установка, поэтому я попробую позже использовать метод @Ginger.

Ответ 6

Для всех, у кого проблема: вам потребуется корневое устройство и используйте

Process result = Runtime.getRuntime().exec("pm install -r -d MyApp.apk /system/app")

Если вы получаете код результата 9 (код ошибки 9), вам нужно удалить свой apk с устройства и нажать его (PUSH not INSTAL!).

Перейдите в оболочку устройства и нажмите apk

launcher=MyApp.apk
$adb shell su -c "mount -o remount,rw -t rfs /dev/stl5 /system"
$adb push $launcher /sdcard/$launcher
$adb shell su -c "chmod 644 /system/app/$launcher"

Теперь вы можете использовать pm install без получения ошибки. Надеюсь, это поможет кому-то.