web-dev-qa-db-ja.com

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);

ただし、このコードは標準のインストールダイアログを開きます。 Android.permission.INSTALL_PACKAGESが付与されたルートなしでアプリをサイレントインストールするにはどうすればよいですか?

PS私は、最初の起動時にフォルダーからシステムに多くのapkをインストールするアプリを作成しています(セットアップウィザードを置き換えます)。ファームウェアを軽くするために必要です。

私がウイルスを書いていると思われる場合:すべてのプログラムは/ 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

82
POMATu

最初の賭けは、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がコアインストーラー関数であり、最後に行うことはPackageManagerinstallPackage関数の呼び出しです。

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); 

ただし、リフレクションを介してこのメ​​ソッドにアクセスできます。

PackageManagerinstallPackage関数の実装方法に興味がある場合は、 PackageManagerService をご覧ください。

概要

ContextgetPackageManager()を介してパッケージマネージャーオブジェクトを取得する必要があります。次に、リフレクションを介してinstallPackage関数を呼び出します。

59
inazaruk

ADBがアプリをインストールする方法を確認しました。
-APKを/ data/local/tmpにコピーします
-「Shell:pm install /data/local/tmp/app.apk」を実行します

私はこの動作を次のようにして再現しようとしました:(PCで、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ではなくpmによってエラーがスローされているようです。
SecurityExceptionはAppInstallにキャッチされず、アプリはクラッシュしないためです。

私はルート化されたデバイス(同じアプリとAppInstall)で同じことを試しましたが、それは魅力のように機能しました。
(通常は/ systemなどにはインストールされません)
AppInstallはルート権限さえも要求しませんでした。
しかし、それはシェルがそのデバイス上で#ではなく常に$であるためです。

ところで、/ systemにアプリをインストールするにはrootが必要ですか?
ルート化されていないデバイスでadbの再マウントを試みたところ、次の結果が得られました。
remount failed: Operation not permitted.
そのため、ルート化されていないデバイスで/ systemを試すことができませんでした。

結論:ルート化されたデバイスを使用する必要があります
お役に立てれば :)

12
FrankkieNL

私は最近、ユーザーの同意なしにインストールを実装しています-これは、環境を完全に制御できるAPIレベル21+のキオスクアプリケーションでした。

基本的な要件は

  • APIレベル21+
  • root accessシステム特権アプリとしてアップデーターをインストールします。

次のメソッドは、InputStreamからAPKを読み取り、インストールします。

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" />

この権限を取得するには、rootを必要とするシステムアプリケーションとしてAPKをインストールする必要があります(ただし、アップデータアプリケーションをインストールした後、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

何らかの理由で単純なデバッグバージョンでは機能しませんでしたが、priv-appのアプリケーションが選択されない場合、logcatは有用な情報を表示します何らかの理由。

9
Boris Treukhov

定義する必要があります

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

マニフェストで、システムパーティション(/ system/app)にいるか、製造元によって署名されたアプリケーションを持っているかどうかに関係なく、INSTALL_PACKAGES権限が与えられます。

私の提案は、リフレクションを介してinstallPackagesを呼び出し、パッケージをインストールして実際のメソッドを呼び出すメソッドでjarをエクスポートするために使用される1.5互換性レベルの小さなAndroidプロジェクトを作成することです。次に、プロジェクトにjarをインポートすることにより、パッケージをインストールする準備が整います。

8
iLRedEiMAtTi

ルート化された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();
        }
     }
}
5
Vladimir

誰もその時間に答えなかったので、私はこれを行う方法を知りませんでした、そして、私はこの許可に関する文書を見つけませんでした。だから私は自分の解決策を見つけました。それはあなたのものより悪いですが、これはとにかく解決策です。

/ data/appに777権限を設定するbusyboxをインストールしました(セキュリティについては気にしません)。次に、アプリから「busybox install」を実行しました。これは機能しますが、大きなセキュリティリークがあります。許可777を設定する場合、ルートは必要ありません。

4
POMATu

リフレクションによって非表示の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
Hermann Poilpre

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();
        }
     }
  }
3
ZeeShaN AbbAs

前提条件:

APKは、以前に正しく指摘されているようにシステムによって署名される必要があります。それを実現する1つの方法は、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でテストされ、正常に動作しました。

1
phoebus

サードパーティのアプリケーションは、Androidアプリを一時的にインストールできません。ただし、サードパーティのアプリケーションは、Android OSにアプリケーションのインストールを要求できます。

したがって、これを定義する必要があります。

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
user9370880

Pmインストールの前に、このLD_LIBRARY_PATH=/vendor/lib:/system/libを試してください。うまくいきます。

0
Ankita Gujar

Android 6以降でサイレントインストールを実行できます。 Boris Treukhovの回答で提供された関数を使用して、投稿内の他のすべてを無視します。ルートも必要ありません。

アプリをデバイス管理者としてインストールすると、バックグラウンドで更新プログラムのサイレントインストールを行う完全なキオスクモードを使用できます。

0
Raymie

PackageManager.installPackageメソッドを使用して、サイレントインストール用のテストアプリを作成しました。

リフレクションを通じてinstallPackageメソッドを取得し、srcフォルダーにAndroid.content.pm.IPackageInstallObserverインターフェースを作成しました(Android.content.pmパッケージに非表示になっているため)。

InstallPackageを実行すると、アプリにAndroid.permission.INSTALL_PACKAGESがないが、AndroidManifest.xmlで定義されているという文字列を示すSecurityExceptionが発生しました。

そのため、この方法を使用することはできません。

PS。 Android SDK 2.3および4.0でテストしました。たぶんそれは以前のバージョンで動作するでしょう。

すべての答えを確認しましたが、結論は、デバイスを機能させるには、まずデバイスへのルートアクセス権が必要であると思われます。

しかし、その後、これらの記事は非常に有用であることがわかりました。 「会社所有」のデバイスを作っているので。

ユーザーの操作なしでAndroidアプリをサイレントに更新する方法

Androidデバイスの所有者-最小限のアプリ

ここにグーグルの「管理デバイス」に関するドキュメントがあります

完全に管理されたデバイス

0
h--n