155

My application installs other applications, and it needs to keep track of what applications it has installed. Of course, this could be achieved by simply keeping a list of installed applications. But this should not be necessary! It should be the responsibility of the PackageManager to maintain the installedBy(a, b) relationship. In fact, according to the API it is:

public abstract String getInstallerPackageName(String packageName) - Retrieve the package name of the application that installed a package. This identifies which market the package came from.

The current approach

Install APK using Intent

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

Uninstall APK using Intent:

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

This is obviously not the way e.g. Android Market installs / uninstalls packages. They use a richer version of the PackageManager. This can bee seen by downloading the Android source code from the Android Git repository. Below are the two hidden methods that corresponds to the Intent approach. Unfortunately they are not available to external developers. But perhaps they will be in the future?

The better approach

Installing APK using the 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);

Uninstalling APK using the 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);

Differences

  • When using intents the local package manager is not made aware of which application the installation originated from. Specifically, getInstallerPackageName(...) returns null.

  • The hidden method installPackage(...) takes the installer package name as a parameter, and is most likely capable of setting this value.

Question

Is it possible to specify package installer name using intents? (Maybe the name of the installer package can be added as an extra to the installation intent?)

Tip: If you want to download the Android source code you can follow the steps described here: Downloading the Source Tree. To extract the *.java files and put them in folders according to the package hierarchy you can check out this neat script: View Android Source Code in Eclipse.

Håvard Geithus
  • 5,544
  • 7
  • 36
  • 51

10 Answers10

111

Android P+ requires this permission in AndroidManifest.xml

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

Then:

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

to uninstall. Seems easier...

CamHart
  • 3,825
  • 7
  • 33
  • 69
JohnyTex
  • 3,323
  • 5
  • 29
  • 52
  • Can this be the app running the code? like in `onDestroy()` method? – Mahdi-Malv Jun 09 '17 at 21:05
  • how about ACTION_INSTALL_PACKAGE ? can we download and install latest version our app from play store ? – MAS. John Mar 03 '18 at 01:44
  • 3
    Since Android P deleting apps requires manifest permission "android.permission.REQUEST_DELETE_PACKAGES" no matter if you use "ACTION_DELETE" or "ACTION_UNINSTALL_PACKAGE" https://developer.android.com/reference/android/content/Intent.html#ACTION_UNINSTALL_PACKAGE – Darklord5 Mar 06 '19 at 16:17
  • 1
    Thank you for mentioning the Android P permission, I was stuck and wasn't sure what was going on prior. – A P Aug 07 '19 at 18:11
68

This is not currently available to third party applications. Note that even using reflection or other tricks to access installPackage() will not help, because only system applications can use it. (This is because it is the low-level install mechanism, after the permissions have been approved by the user, so it is not safe for regular applications to have access to.)

Also the installPackage() function arguments have often changed between platform releases, so anything you do trying access it will fail on various other versions of the platform.

EDIT:

Also it is worth pointing out that this installerPackage was only added fairly recently to the platform (2.2?) and was originally not actually used for tracking who installed the app -- it is used by the platform to determine who to launch when reporting bugs with the app, for implementing Android Feedback. (This was also one of the times the API method arguments changed.) For at least a long while after it was introduced, Market still didn't use it to track the apps it has installed (and it may very well still not use it), but instead just used this to set the Android Feedback app (which was separate from Market) as the "owner" to take care of feedback.

hackbod
  • 90,665
  • 16
  • 140
  • 154
  • "Note that even using reflection or other tricks to access installPackage() will not help, because only system applications can use it." Suppose I'm making a package install/remove/manage app for a given platform, other than native Android itself. How should I access install/remove? – dascandy Oct 10 '11 at 10:04
  • startActivity() with an appropriately formed Intent. (I am sure this has been answered elsewhere on StackOverflow, so I won't try to give the exact answer here at risk of getting something wrong.) – hackbod Oct 11 '11 at 00:52
  • mmmkay, that brings up the standard Android install/remove dialogs. Those details have already been handled - I'm looking for the "just **** install this package" and "just **** remove this package" functions, literally no questions asked. – dascandy Oct 11 '11 at 07:37
  • 3
    As I said, these are not available to third party applications. If you are making your own system image, you have the platform implementation, and you can find the functions there, but they are not part of the APIs available to normal third party apps. – hackbod Oct 13 '11 at 03:17
  • i am making apk file explorer with install, remove and backup functionality, so is google allow me to stay publish my application on google play? and which policy we are going to break? – Rahul Mandaliya Nov 18 '14 at 13:51
  • Is it possible to use these methods if the device is rooted? or even then? – yahya Oct 14 '15 at 07:41
  • This is an outdated answer. I would consider updating it to reflect the new Android for Work APIs. – Luke Cauthen Jul 29 '16 at 16:22
47

API level 14 introduced two new actions: ACTION_INSTALL_PACKAGE and ACTION_UNINSTALL_PACKAGE. Those actions allow you to pass EXTRA_RETURN_RESULT boolean extra to get an (un)installation result notification.

Example code for invoking the uninstall dialog:

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

And receive the notification in your Activity#onActivityResult method:

@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");
        }
    }
}
Alex Lipov
  • 13,503
  • 5
  • 64
  • 87
Pir Fahim Shah
  • 10,505
  • 1
  • 82
  • 81
  • how can i confirm from this action dialog box that either user has pressed ok or cancel so that i can take the decision based on this – Erum Nov 25 '14 at 07:41
  • 2
    @Erum I've added an example for what you asked – Alex Lipov May 13 '15 at 07:17
  • On the installation, the cancel button did not get a result back to the onActivityResult method – diyoda_ Nov 19 '15 at 06:44
  • 2
    Starting with API 25, calling `ACTION_INSTALL_PACKAGE` will require the signature level [`REQUEST_INSTALL_PACKAGES`](https://developer.android.com/reference/android/Manifest.permission.html#REQUEST_INSTALL_PACKAGES) permission. Likewise, starting with API 28 (Android P), calling `ACTION_UNINSTALL_PACKAGE` will require the non-dangerous [`REQUEST_DELETE_PACKAGES`](https://developer.android.com/reference/android/Manifest.permission.html#REQUEST_DELETE_PACKAGES) permission. At least according to the docs. – Steve Blackwell Apr 03 '18 at 08:14
29

If you have Device Owner (or profile owner, I haven't tried) permission you can silently install/uninstall packages using device owner API.

for uninstalling:

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

and to install package:

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
  • 5,756
  • 3
  • 39
  • 36
  • I knew it had to be possible to do this being the owner device. Thanks for the answer! – Luke Cauthen Jul 26 '16 at 16:28
  • @sandeep it's just reads the APK's content into the output stream – Ohad Cohen Jan 17 '17 at 23:31
  • @LukeCauthen have your tried by being device owner ? did it worked ? – NetStarter Aug 19 '17 at 16:40
  • @NetStarter Yes I have. It is just a pain in the ass getting an app to be device owner. Once you do that, you get a lot of power that would normally require root. – Luke Cauthen Aug 20 '17 at 17:45
  • I have a working device owner app (COSU). I've tried using the method above to let it upgrade itself from a downloaded `.apk` file but the callback intent is never fired and the app is never upgraded. No errors or exceptions appear in `adb logcat`. (I changed all the `System.out`s to `Log.e`s.) – fadedbee Oct 27 '17 at 10:31
  • @LukeCauthen Does your Device Owner app upgrade itself, or install a different app? (I am having trouble getting a working Device Owner app to upgrade itself.) – fadedbee Oct 27 '17 at 11:03
  • @chrisdew Good question, the solution I came up with is having two apps running. Where the device owner app updates the apk of a separate app. If the owner app ever needs to update, the separate app can update it (because the app has to close when the apk is replaced). – Luke Cauthen Oct 28 '17 at 21:41
  • @LukeCauthen Thanks for your response. I have now tried the two-app solution and it doesn't work for me. https://stackoverflow.com/questions/47020457/upgrading-app-in-background-using-device-policy-controller Any advice? – fadedbee Oct 30 '17 at 17:09
  • 1
    Please note that you must add android.permission.DELETE_PACKAGES to your manifest for the uninstall to work (tested on Api level 22 or under) – benchuk Nov 26 '17 at 15:49
  • well played , will it work on an admin app ? can it delete it ? or is there a way to remove the admin and than delete ? – yanivtwin May 01 '18 at 07:00
  • It's a good solution. what is the `componentName` object and what is used for? – javadhme Jan 16 '20 at 13:50
  • @Ohad Cohen srry bumping an old thread - but can you please explain the call to `readTo()` on line 14 of your second code example? specifically, is it a method you wrote yourself that is not included here or is it an unqualified member of one of the components in your code? – sh7411usa Mar 25 '22 at 09:05
  • I no longer work for the company I wrote this for, so I don't have access to the code itself, for my memory it's like reading the entire file from the file path to the outputstream, considering that the streams might split it for some resoans – Ohad Cohen Mar 26 '22 at 10:08
4

The only way to access those methods is through reflection. You can get a handle on a PackageManager object by calling getApplicationContext().getPackageManager() and using reflection access these methods. Checkout this tutorial.

HandlerExploit
  • 8,131
  • 4
  • 31
  • 50
3

If you are passing package name as parameter to any of your user defined function then use the below code :

    Intent intent=new Intent(Intent.ACTION_DELETE);
    intent.setData(Uri.parse("package:"+packageName));
    startActivity(intent);
3

According to Froyo source code, the Intent.EXTRA_INSTALLER_PACKAGE_NAME extra key is queried for the installer package name in the PackageInstallerActivity.

njzk2
  • 38,969
  • 7
  • 69
  • 107
  • 1
    By looking at this [commit](https://android.googlesource.com/platform/packages/apps/PackageInstaller/+/fe069ea729efec01a8b6f16f9e76a2db4988404a) I think it should work – sergio91pt Aug 29 '12 at 15:35
2

On a rooted device, you might use:

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() is defined here.

Community
  • 1
  • 1
18446744073709551615
  • 16,368
  • 4
  • 94
  • 127
2

If you're using Kotlin, API 14+, and just wish to show uninstall dialog for your app:

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

You can change packageName to any other package name if you want to prompt the user to uninstall another app on the device

Louis CAD
  • 10,965
  • 2
  • 39
  • 58
1

Prerequisite:

Your APK needs to be signed by system as correctly pointed out earlier. One way to achieve that is building the AOSP image yourself and adding the source code into the build.

Code:

Once installed as a system app, you can use the package manager methods to install and uninstall an APK as following:

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

Uninstall:

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

To have a callback once your APK is installed/uninstalled you can use this:

/**
 * 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
  • 1,280
  • 1
  • 16
  • 36