instal / hapus instalan APK secara terprogram (PackageManager vs Intents)

141

Aplikasi saya menginstal aplikasi lain, dan perlu melacak aplikasi apa yang telah diinstal. Tentu saja, ini dapat dicapai dengan hanya menyimpan daftar aplikasi yang diinstal. Tapi ini seharusnya tidak perlu! Seharusnya menjadi tanggung jawab PackageManager untuk mempertahankan hubungan yang diinstal oleh (a, b). Bahkan, menurut API itu adalah:

public abstrak String getInstallerPackageName (String packageName) - Ambil nama paket aplikasi yang menginstal paket. Ini mengidentifikasi dari pasar mana paket itu berasal.

Pendekatan saat ini

Instal APK menggunakan Intent

Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
startActivity(intent);

Copot pemasangan APK menggunakan Intent:

Intent intent = new Intent(Intent.ACTION_DELETE, Uri.fromParts("package",
getPackageManager().getPackageArchiveInfo(apkUri.getPath(), 0).packageName,null));
startActivity(intent);

Ini jelas bukan cara misal Android Market menginstal / mencopot paket. Mereka menggunakan versi PackageManager yang lebih kaya. Ini dapat dilihat dengan mengunduh kode sumber Android dari repositori Android Git. Di bawah ini adalah dua metode tersembunyi yang sesuai dengan pendekatan Intent. Sayangnya mereka tidak tersedia untuk pengembang eksternal. Tapi mungkin mereka akan ada di masa depan?

Pendekatan yang lebih baik

Menginstal APK menggunakan PackageManager

/**
 * @hide
 * 
 * Install a package. Since this may take a little while, the result will
 * be posted back to the given observer.  An installation will fail if the calling context
 * lacks the {@link android.Manifest.permission#INSTALL_PACKAGES} permission, if the
 * package named in the package file's manifest is already installed, or if there's no space
 * available on the device.
 *
 * @param packageURI The location of the package file to install.  This can be a 'file:' or a
 * 'content:' URI.
 * @param observer An observer callback to get notified when the package installation is
 * complete. {@link IPackageInstallObserver#packageInstalled(String, int)} will be
 * called when that happens.  observer may be null to indicate that no callback is desired.
 * @param flags - possible values: {@link #INSTALL_FORWARD_LOCK},
 * {@link #INSTALL_REPLACE_EXISTING}, {@link #INSTALL_ALLOW_TEST}.
 * @param installerPackageName Optional package name of the application that is performing the
 * installation. This identifies which market the package came from.
 */
public abstract void installPackage(
        Uri packageURI, IPackageInstallObserver observer, int flags,
        String installerPackageName);

Menghapus instalasi APK menggunakan PackageManager

/**
 * Attempts to delete a package.  Since this may take a little while, the result will
 * be posted back to the given observer.  A deletion will fail if the calling context
 * lacks the {@link android.Manifest.permission#DELETE_PACKAGES} permission, if the
 * named package cannot be found, or if the named package is a "system package".
 * (TODO: include pointer to documentation on "system packages")
 *
 * @param packageName The name of the package to delete
 * @param observer An observer callback to get notified when the package deletion is
 * complete. {@link android.content.pm.IPackageDeleteObserver#packageDeleted(boolean)} will be
 * called when that happens.  observer may be null to indicate that no callback is desired.
 * @param flags - possible values: {@link #DONT_DELETE_DATA}
 *
 * @hide
 */
public abstract void deletePackage(
        String packageName, IPackageDeleteObserver observer, int flags);

Perbedaan

  • Saat menggunakan maksud, manajer paket lokal tidak mengetahui dari aplikasi mana asalnya. Secara khusus, getInstallerPackageName (...) mengembalikan nol.

  • Metode tersembunyi installPackage (...) menggunakan nama paket installer sebagai parameter, dan kemungkinan besar mampu mengatur nilai ini.

Pertanyaan

Apakah mungkin untuk menentukan nama pemasang paket menggunakan maksud? (Mungkin nama paket penginstal dapat ditambahkan sebagai tambahan untuk maksud instalasi?)

Tip: Jika Anda ingin mengunduh kode sumber Android, Anda dapat mengikuti langkah-langkah yang dijelaskan di sini: Mengunduh Source Tree. Untuk mengekstrak file * .java dan meletakkannya di folder sesuai dengan hierarki paket, Anda dapat memeriksa skrip yang rapi ini: Lihat Kode Sumber Android di Eclipse .

Håvard Geithus
sumber
Beberapa URI tidak ada dalam teks. Saya akan menambahkannya segera setelah saya diizinkan (pengguna baru memiliki beberapa batasan untuk mencegah spam).
Håvard Geithus
1
bagaimana menonaktifkan fungsionalitas penghapusan?
2
@ user938893: "bagaimana cara menonaktifkan fungsionalitas penghapusan?" - Mengatasi beberapa malware yang sulit dihapus instalasinya, bukan?
Daniel

Jawaban:

66

Saat ini tidak tersedia untuk aplikasi pihak ketiga. Perhatikan bahwa menggunakan refleksi atau trik lain untuk mengakses installPackage () tidak akan membantu, karena hanya aplikasi sistem yang dapat menggunakannya. (Ini karena ini adalah mekanisme pemasangan tingkat rendah, setelah izin disetujui oleh pengguna, sehingga tidak aman bagi aplikasi reguler untuk memiliki akses.)

Juga argumen fungsi installPackage () sering berubah di antara rilis platform, sehingga apa pun yang Anda coba akses akan gagal pada berbagai versi platform lainnya.

EDIT:

Juga patut untuk menunjukkan bahwa installerPackage ini hanya ditambahkan cukup baru ke platform (2.2?) Dan pada awalnya tidak benar-benar digunakan untuk melacak siapa yang menginstal aplikasi - ini digunakan oleh platform untuk menentukan siapa yang akan diluncurkan ketika melaporkan bug dengan aplikasi, untuk mengimplementasikan Umpan Balik Android. (Ini juga merupakan salah satu kali argumen metode API berubah.) Untuk setidaknya lama setelah diperkenalkan, Market masih tidak menggunakannya untuk melacak aplikasi yang telah diinstal (dan mungkin masih tidak menggunakannya) ), tetapi alih-alih hanya menggunakan ini untuk mengatur aplikasi Android Umpan Balik (yang terpisah dari Market) sebagai "pemilik" untuk menangani umpan balik.

hackbod
sumber
"Perhatikan bahwa walaupun menggunakan refleksi atau trik lain untuk mengakses installPackage () tidak akan membantu, karena hanya aplikasi sistem yang dapat menggunakannya." Misalkan saya membuat paket instal / hapus / kelola aplikasi untuk platform yang diberikan, selain Android asli itu sendiri. Bagaimana cara saya mengakses instal / hapus?
dascandy
startActivity () dengan Intent yang terbentuk dengan tepat. (Saya yakin ini telah dijawab di tempat lain di StackOverflow, jadi saya tidak akan mencoba memberikan jawaban yang tepat di sini dengan risiko mendapatkan sesuatu yang salah.)
hackbod
mmmkay, yang memunculkan dialog instal / hapus Android standar. Detail-detail itu sudah ditangani - Saya mencari fungsi "just **** instal paket ini" dan "just **** hapus paket ini", secara harfiah tidak ada pertanyaan yang diajukan.
dascandy
2
Seperti yang saya katakan, ini tidak tersedia untuk aplikasi pihak ketiga. Jika Anda membuat gambar sistem Anda sendiri, Anda memiliki implementasi platform, dan Anda dapat menemukan fungsinya di sana, tetapi mereka bukan bagian dari API yang tersedia untuk aplikasi pihak ketiga yang normal.
hackbod
saya membuat file apk explorer dengan menginstal, menghapus, dan mencadangkan fungsi, jadi apakah Google mengizinkan saya untuk tetap menerbitkan aplikasi saya di google play? dan kebijakan apa yang akan kita langgar?
Rahul Mandaliya
85

Android P + memerlukan izin ini di AndroidManifest.xml

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

Kemudian:

Intent intent = new Intent(Intent.ACTION_DELETE);
intent.setData(Uri.parse("package:com.example.mypackage"));
startActivity(intent);

untuk menghapus. Tampak lebih mudah ...

JohnyTex
sumber
Apakah ini aplikasi yang menjalankan kode? seperti dalam onDestroy()metode?
Mahdi-Malv
bagaimana dengan ACTION_INSTALL_PACKAGE? dapatkah kita mengunduh dan menginstal versi terbaru aplikasi kita dari play store?
MAS. John
3
Karena Android P menghapus aplikasi memerlukan izin nyata "android.permission.REQUEST_DELETE_PACKAGES" tidak masalah jika Anda menggunakan "ACTION_DELETE" atau "ACTION_UNINSTALL_PACKAGE" developer.android.com/reference/android/content/…
Darklord5
Terima kasih telah menyebutkan izin Android P, saya macet dan tidak yakin apa yang terjadi sebelumnya.
Avi Parshan
43

API level 14 memperkenalkan dua tindakan baru: ACTION_INSTALL_PACKAGE dan ACTION_UNINSTALL_PACKAGE . Tindakan tersebut memungkinkan Anda untuk melewati EXTRA_RETURN_RESULT ekstra boolean untuk mendapatkan notifikasi hasil instalasi (tidak).

Kode contoh untuk menjalankan dialog penghapusan instalasi:

String app_pkg_name = "com.example.app";
int UNINSTALL_REQUEST_CODE = 1;

Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);  
intent.setData(Uri.parse("package:" + app_pkg_name));  
intent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
startActivityForResult(intent, UNINSTALL_REQUEST_CODE);

Dan terima pemberitahuan dalam metode Activity # onActivityResult Anda :

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == UNINSTALL_REQUEST_CODE) {
        if (resultCode == RESULT_OK) {
            Log.d("TAG", "onActivityResult: user accepted the (un)install");
        } else if (resultCode == RESULT_CANCELED) {
            Log.d("TAG", "onActivityResult: user canceled the (un)install");
        } else if (resultCode == RESULT_FIRST_USER) {
            Log.d("TAG", "onActivityResult: failed to (un)install");
        }
    }
}
Pir Fahim Shah
sumber
bagaimana saya bisa mengkonfirmasi dari kotak dialog tindakan ini bahwa salah satu pengguna telah menekan ok atau membatalkan sehingga saya dapat mengambil keputusan berdasarkan hal ini
Erum
2
@Erum Saya telah menambahkan contoh untuk apa yang Anda minta
Alex Lipov
Pada instalasi, tombol batal tidak mendapatkan hasil kembali ke metode
onActivityResult
2
Mulai dengan API 25, panggilan ACTION_INSTALL_PACKAGEakan memerlukan REQUEST_INSTALL_PACKAGESizin tingkat tanda tangan . Demikian juga, dimulai dengan API 28 (Android P), panggilan ACTION_UNINSTALL_PACKAGEakan memerlukan REQUEST_DELETE_PACKAGESizin yang tidak berbahaya . Setidaknya menurut dokumen.
Steve Blackwell
22

Jika Anda memiliki izin Pemilik Perangkat (atau pemilik profil, saya belum mencoba), Anda dapat menginstal / menghapus paket secara diam-diam menggunakan API pemilik perangkat.

untuk menghapus instalasi:

public boolean uninstallPackage(Context context, String packageName) {
    ComponentName name = new ComponentName(MyAppName, MyDeviceAdminReceiver.class.getCanonicalName());
    PackageManager packageManger = context.getPackageManager();
    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
        PackageInstaller packageInstaller = packageManger.getPackageInstaller();
        PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
                PackageInstaller.SessionParams.MODE_FULL_INSTALL);
        params.setAppPackageName(packageName);
        int sessionId = 0;
        try {
            sessionId = packageInstaller.createSession(params);
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
        packageInstaller.uninstall(packageName, PendingIntent.getBroadcast(context, sessionId,
                new Intent("android.intent.action.MAIN"), 0).getIntentSender());
        return true;
    }
    System.err.println("old sdk");
    return false;
}

dan untuk menginstal paket:

public boolean installPackage(Context context,
                                     String packageName, String packagePath) {
    ComponentName name = new ComponentName(MyAppName, MyDeviceAdminReceiver.class.getCanonicalName());
    PackageManager packageManger = context.getPackageManager();
    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
        PackageInstaller packageInstaller = packageManger.getPackageInstaller();
        PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
                PackageInstaller.SessionParams.MODE_FULL_INSTALL);
        params.setAppPackageName(packageName);
        try {
            int sessionId = packageInstaller.createSession(params);
            PackageInstaller.Session session = packageInstaller.openSession(sessionId);
            OutputStream out = session.openWrite(packageName + ".apk", 0, -1);
            readTo(packagePath, out); //read the apk content and write it to out
            session.fsync(out);
            out.close();
            System.out.println("installing...");
            session.commit(PendingIntent.getBroadcast(context, sessionId,
                    new Intent("android.intent.action.MAIN"), 0).getIntentSender());
            System.out.println("install request sent");
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }
    System.err.println("old sdk");
    return false;
}
Ohad Cohen
sumber
Saya tahu itu harus dimungkinkan untuk melakukan ini menjadi perangkat pemilik. Terima kasih atas jawabannya!
Luke Cauthen
@sandeep itu hanya membaca konten APK ke dalam arus keluaran
Ohad Cohen
@LukeCauthen, apakah Anda sudah mencoba menjadi pemilik perangkat? apakah itu berhasil?
NetStarter
@ NetStarter Ya saya punya. Itu hanya rasa sakit di pantat mendapatkan aplikasi untuk menjadi pemilik perangkat. Setelah Anda melakukannya, Anda mendapatkan banyak daya yang biasanya membutuhkan root.
Luke Cauthen
1
Harap dicatat bahwa Anda harus menambahkan android.permission.DELETE_PACKAGES ke manifes Anda agar pencopotan berfungsi (diuji pada Api level 22 atau di bawah)
benchuk
4

Satu-satunya cara untuk mengakses metode-metode itu adalah melalui refleksi. Anda bisa mendapatkan pegangan pada suatu PackageManagerobjek dengan memanggil getApplicationContext().getPackageManager()dan menggunakan refleksi mengakses metode ini. Lihat tutorial ini .

HandlerExploit
sumber
Ini bekerja sangat baik dengan 2.2, tapi saya tidak beruntung menggunakannya dengan 2.3
Seseorang di suatu tempat
3
Refleksi tidak stabil di semua versi api
HandlerExploit
3

Menurut kode sumber Froyo, kunci tambahan Intent.EXTRA_INSTALLER_PACKAGE_NAME diminta untuk nama paket penginstal di PackageInstallerActivity.

njzk2
sumber
1
Dengan melihat komit ini, saya pikir ini akan berhasil
sergio91pt
2

Pada perangkat yang di-rooting, Anda dapat menggunakan:

String pkg = context.getPackageName();
String shellCmd = "rm -r /data/app/" + pkg + "*.apk\n"
                + "rm -r /data/data/" + pkg + "\n"
                // TODO remove data on the sd card
                + "sync\n"
                + "reboot\n";
Util.sudo(shellCmd);

Util.sudo() didefinisikan di sini.

18446744073709551615
sumber
Apakah ada cara untuk menginstal aplikasi yang sudah diunduh di sdcard juga? Atau bisakah Anda menyarankan saya ke beberapa halaman untuk memeriksa perintah mana yang bisa kita gunakan pada shell di Platform Android?
yahya
1
@yahya developer.android.com/tools/help/shell.html ditemukan dengan frasa "pm android", pm = manajer paket
18446744073709551615
1
@yahya cheatography.com/citguy/cheat-sheets/android-package-manager-pm set-install-location
18446744073709551615
Terima kasih banyak! Tautan ini adalah panduan yang sangat keren untuk memulai dengan :)
yahya
@ V. Kalyuzhnyu Dulu bekerja kembali pada tahun 2015. IIRC itu adalah Samsung Galaxy, mungkin S5.
18446744073709551615
2

Jika Anda meneruskan nama paket sebagai parameter ke salah satu fungsi yang ditentukan pengguna Anda, gunakan kode di bawah ini:

    Intent intent=new Intent(Intent.ACTION_DELETE);
    intent.setData(Uri.parse("package:"+packageName));
    startActivity(intent);
Rashwin SM
sumber
0

Jika Anda menggunakan Kotlin, API 14+, dan hanya ingin menampilkan dialog uninstall untuk aplikasi Anda:

startActivity(Intent(Intent.ACTION_UNINSTALL_PACKAGE).apply {
    data = Uri.parse("package:$packageName")
})

Anda dapat mengubah packageNameke nama paket lain jika Anda ingin meminta pengguna untuk menghapus aplikasi lain di perangkat

Louis CAD
sumber
0

Prasyarat:

APK Anda harus ditandatangani oleh sistem seperti yang ditunjukkan sebelumnya dengan benar. Salah satu cara untuk mencapai itu adalah membangun gambar AOSP sendiri dan menambahkan kode sumber ke dalam build.

Kode:

Setelah diinstal sebagai aplikasi sistem, Anda dapat menggunakan metode manajer paket untuk menginstal dan menghapus instalan APK sebagai berikut:

Install:

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

Copot pemasangan:

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

Untuk memiliki panggilan balik setelah APK diinstal / dihapus, Anda dapat menggunakan ini:

/**
 * 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);
}
phoebus
sumber