32

Plz check below classe & give me the suggestion for how to use them https://developer.android.com/reference/android/content/pm/PackageInstaller.html https://developer.android.com/reference/android/content/pm/PackageInstaller.Session.html

So please give me an example to install/update/remove app. Can it be possible that the new application will install in device profile owner?

Sud
  • 421
  • 1
  • 4
  • 8
  • just tell what u need u want to install an app in device – Naveen Tamrakar Nov 12 '14 at 10:42
  • Hello Naveen Tamrakar, I want to install an apk silently on device using class PackageInsatller introduced in android lollipop. – Sud Nov 12 '14 at 10:57
  • 1
    @Sud don't think it works silently, it works with an intent only. – zaitsman Nov 28 '14 at 11:26
  • @zaitsman: Can you please explain in details? – Sud Dec 04 '14 at 09:43
  • @zaitsman: I am asking about how to use PackageInstaller(introduced in android Lollipop) class for installing apk – Sud Jan 12 '15 at 12:13
  • Devs who are trying PackageInstaller for the first time don't forget to implement the broadcast listener passed to the pending intent. I was confused why was my code not working because I left the handling of the returned intent part. – Rookie Aug 05 '20 at 11:04

7 Answers7

32

It is possible without System permissions from Android M onwards.

if ((mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES, installerUid)
        == PackageManager.PERMISSION_GRANTED)
        || (installerUid == Process.ROOT_UID)
        || mIsInstallerDeviceOwner) {
    mPermissionsAccepted = true;
} else {
    mPermissionsAccepted = false;
}

Silent install and uninstall of apps by Device Owner:

A Device Owner can now silently install and uninstall applications using the PackageInstaller APIs, independent of Google Play for Work.

More in this link.


This is possible from Android 6.0 and up.

  • Make your app the Device owner.

Once your app gets the Device owner permission, we can install, uninstall and update silently without any user intervention.

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

    session.commit(createIntentSender(context, sessionId));
    return true;
}



private static IntentSender createIntentSender(Context context, int sessionId) {
        PendingIntent pendingIntent = PendingIntent.getBroadcast(
                context,
                sessionId,
                new Intent(ACTION_INSTALL_COMPLETE),
                0);
        return pendingIntent.getIntentSender();
    }

Uninstall:

String appPackage = "com.your.app.package";
Intent intent = new Intent(getActivity(), getActivity().getClass());
PendingIntent sender = PendingIntent.getActivity(getActivity(), 0, intent, 0);
PackageInstaller mPackageInstaller = getActivity().getPackageManager().getPackageInstaller();
mPackageInstaller.uninstall(appPackage, sender.getIntentSender());

Git repo here.

amalBit
  • 12,041
  • 6
  • 77
  • 94
  • 4
    thank you very much.. but I am not able to install apk..the whole code runs without errors but there is no apk installed. Is it my understanding wrong. Silent install apk needs rooted device..? – Sandeep R Jan 18 '17 at 05:23
  • 2
    Silent install does not need the device to be rooted. Your phone should run android M and up. And you should give device owner permission to ur app. @sandeep follow this.. https://codelabs.developers.google.com/codelabs/cosu/#0 – amalBit Jan 18 '17 at 07:27
  • I have same problem as @sandeep, i am running app as device owner on api level 22, the session commits ok, but then, if i register session callback on package installer the session progress got stuck at 0.8 always ... any thoughts ?? – Jakub Martyčák Feb 18 '17 at 21:54
  • @JakubMartyčák Device owner exists from api 21. But the package installer can only be accessed from API 23 and above. – amalBit Feb 20 '17 at 06:24
  • does this work to update your own application? I can install another apk, but not a new version of mine. – Maragues May 24 '17 at 11:55
  • @Maragues You should be able to update your application as well. I dont remember trying though. – amalBit May 25 '17 at 06:06
  • @amalBit from my tests, updating your own application works on 6.0+, not on 5.0. The latter allows you to install other apps. – Maragues May 25 '17 at 08:18
  • 1
    @Maragues The silent update works only from 6 and above. (For self and other apps) – amalBit May 25 '17 at 15:26
  • 1
    How do you "Make your app the Device owner." ? Is it possible via the UI of the OS ? Without root or connected PC ? – android developer Jul 21 '17 at 23:27
  • @androiddeveloper one is via ADB(connected computer). Then there is a way using nfc bump. More info here https://source.android.com/devices/tech/admin/provision – amalBit Jul 23 '17 at 08:53
  • @amalBit So it's impossible for the normal user to enable this, as it's not inside the UI of the OS, right? – android developer Jul 23 '17 at 09:03
  • 1
    @androiddeveloper this can be done on a fresh phone without any google account attached. This feature is for corporate owned single use devices.(Kiosk apps mostly) – amalBit Jul 23 '17 at 15:27
  • @androiddeveloper yup – amalBit Jul 24 '17 at 06:04
  • 4
    Note that ACTION_INSTALL_COMPLETE is a variable you must declare yourself. In the repo: https://github.com/googlesamples/android-testdpc/blob/5b8297e9d51983a774ba3391e28a6aa35ca3f062/app/src/main/java/com/afwsamples/testdpc/cosu/CosuUtils.java, it is defined as `public static final String ACTION_INSTALL_COMPLETE = "com.afwsamples.testdpc.INSTALL_COMPLETE";`. – Dick Lucas Sep 20 '17 at 18:07
  • 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:46
  • @amalBit @Maragues how i cn make my app device owner if I have root permission for my app as I don't want to use EMM (work profile). I tried `dpm create-profile-owner <>` but install/uninstall doesn't work while code runs w/o error...help would be really appreciated as I am stuck at this prob. from many days now – aki92 Mar 22 '18 at 06:17
  • 1
    To become device owner _adb shell dpm set-device-owner com.mycompany.realapp/.ota.OTAAdminReceiver_, which extended https://developer.android.com/reference/android/app/admin/DeviceAdminReceiver.html I needed a 2nd apk (called _relauncher.apk_) in order to listen to package installs and relaunching my real app. For this to work, I launched a Headless activity manually, otherwise Android doesn't notify BroadcastReceivers of new package installs _adb shell am start -n com.mycompany.relauncher/.HeadlessActivity_ I don't know if this'll work with Android 8 changes to BroadcastReceivers – Maragues Mar 23 '18 at 13:54
  • @Maragues thanks a lot for so detailed ans. it worked properly. Can I ask you for 1 more help that how I can access /data/data folder by being a system privileged app? SO ques. https://stackoverflow.com/questions/49463327/can-system-app-access-data-data-of-other-apps – aki92 Mar 24 '18 at 11:18
  • @amalBit why it can only work for installing new apk/update another apk, but not for update apk itself? There is no any action for the session.commit for upgrading itself apk, any suggestion? – paugoo Jul 12 '18 at 13:26
  • @amalBit so it means, we can't directly update device owner apk silently (using code above)? – paugoo Jul 13 '18 at 04:59
  • @paugoo We can update device owner app, but I do not remember how I did it. It has been more than two years since I did it. – amalBit Jul 13 '18 at 08:42
  • @amalBit oh okay I realize the problem, we need to make sure the keystore should be the same one, otherwise it might be failed. However I encounter another problem that how to update in case of the app is in lock mode. – paugoo Jul 13 '18 at 11:29
  • @paugoo You can be ready with the updated apk and trigger the installation when the app is unlocked. – amalBit Jul 13 '18 at 16:46
  • @amalBit the left problem is after the installation succeed, it back to home launcher, and closed the app, is it possible to relaunch it automatically? I've put the receiver for pending intent broadcast "ACTION_INSTALL_COMPLETE", but seems it call nothing after installation complete – paugoo Jul 16 '18 at 06:03
  • @paugoo I do not know a direct solution to this. But If you can make your app as the default launcher, then your app will be automatically launched for sure. – amalBit Jul 16 '18 at 08:41
  • @amalBit yes you're right it might be automatically launched if we set is as default launcher. However, some devices (android version 6.0) have some bugs with lock (pin app) if we set it as default launcher, so I can't set it as default. For now I do some tricky way, set another home launcher and put a receiver within, then broadcast before updating and let that home launcher call/launch my app. – paugoo Jul 16 '18 at 09:16
  • @paugoo You should write this as a question. That way it will get more visibility. – amalBit Jul 16 '18 at 11:13
7

You cannot silently install a third party application in the newly created user with PackageInstaller.Session.commit() without specific "rights".
You either need :

  • the INSTALL_PACKAGES permission. But this permission is not available for third-party application. So even with your profile owner app, you won't have this specific permission.
  • Run the process as ROOT_UID. Which means you'll have to root the device.

From the Android source code:

if ((mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES, installerUid) == PackageManager.PERMISSION_GRANTED) 
   || (installerUid == Process.ROOT_UID)) {
    mPermissionsAccepted = true;
} else {
    mPermissionsAccepted = false;
}

If you neither have root access and the INSTALL_PACKAGES permission, then a message will be prompted to the user to ask if he confirms the permissions. This confirmation is then used during the commit process of the PackageInstaller's session. Obviously, in this case, this is not transparent, since the user will have to manually confirm the installation of your apps.

Florent Dupont
  • 1,758
  • 18
  • 24
  • Could you please give more details regarding final stage? Based on your answer I see I still able to use this class on L, but with some action from the user. I implemented this logic but sadly I on commit it doesn't run an intent or show an ydialog – Gleichmut Dec 02 '16 at 09:47
  • Is it even possible to "Run the process as ROOT_UID" ? – dknchris Mar 03 '17 at 11:25
  • It seems to be the case. But to do that you'll have to root the device. (=all applications are run as root... more info on [wikipedia](https://en.wikipedia.org/wiki/Rooting_%28Android_OS%29)). – Florent Dupont Mar 03 '17 at 15:32
  • Very useful link that was: https://developer.android.com/reference/android/Manifest.permission.html#DELETE_PACKAGES thank you @FlorentDupont – benchuk Nov 26 '17 at 15:43
  • @FlorentDupont how I can run the process of installing/uninstalling packages as ROOT_UID if I have root permission for my app? – aki92 Mar 22 '18 at 06:20
  • Can you please show how to do it, using root? Is it possible to grant the app this permission using root (within the app itself) ? – android developer Jun 15 '18 at 09:52
7

The install method @amalBit provided did not work for me. It's strange since this is how it is implemented in the Google Sample.

This answer helped me to find a solution. I had to change some parts of the code. Here is my implementation:

public static void installPackage(Context context, InputStream inputStream)
        throws IOException {
    PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
    int sessionId = packageInstaller.createSession(new PackageInstaller
            .SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL));
    PackageInstaller.Session session = packageInstaller.openSession(sessionId);

    long sizeBytes = 0;

    OutputStream out = null;
    out = session.openWrite("my_app_session", 0, sizeBytes);

    int total = 0;
    byte[] buffer = new byte[65536];
    int c;
    while ((c = inputStream.read(buffer)) != -1) {
        total += c;
        out.write(buffer, 0, c);
    }
    session.fsync(out);
    inputStream.close();
    out.close();

    // fake intent
    IntentSender statusReceiver = null;
    Intent intent = new Intent(context, SomeActivity.class);
    PendingIntent pendingIntent = PendingIntent.getBroadcast(context,
            1337111117, intent, PendingIntent.FLAG_UPDATE_CURRENT);

    session.commit(pendingIntent.getIntentSender());
    session.close();
}

This method can be called like this:

        InputStream inputStream = getActivity().getAssets().open("my_awesome_app.apk");
        InstallationHelper.installPackage(getActivity(), inputStream);
Community
  • 1
  • 1
devz
  • 2,629
  • 2
  • 31
  • 37
4

You simply clear your restrictions

public static DevicePolicyManager getDpm(Context context) {
    return (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
}

public static ComponentName getAdmin(Context context) {
    return new ComponentName(context, MyDevicePolicyReceiver.class);
}

public static void addMyRestrictions(Context context) {
    getDpm(context).addUserRestriction(getAdmin(context), UserManager.DISALLOW_INSTALL_APPS);
    getDpm(context).addUserRestriction(getAdmin(context), UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
}

public static void clearMyRestrictions(Context context) {
    getDpm(context).clearUserRestriction(getAdmin(context), UserManager.DISALLOW_INSTALL_APPS);
    getDpm(context).clearUserRestriction(getAdmin(context), UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
}

public static void installPackage(Context context, InputStream inputStream)
        throws IOException {
    PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
    int sessionId = packageInstaller.createSession(new PackageInstaller
            .SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL));

    //openSession checks for user restrictions
    clearMyRestrictions(context);
    PackageInstaller.Session session = packageInstaller.openSession(sessionId);

    long sizeBytes = 0;

    OutputStream out = null;
    out = session.openWrite("my_app_session", 0, sizeBytes);

    int total = 0;
    byte[] buffer = new byte[65536];
    int c;
    while ((c = inputStream.read(buffer)) != -1) {
        total += c;
        out.write(buffer, 0, c);
    }
    session.fsync(out);
    inputStream.close();
    out.close();

    // fake intent
    IntentSender statusReceiver = null;
    Intent intent = new Intent(context, SomeActivity.class);
    PendingIntent pendingIntent = PendingIntent.getBroadcast(context,
            1337111117, intent, PendingIntent.FLAG_UPDATE_CURRENT);

    session.commit(pendingIntent.getIntentSender());
    session.close();
}
3

This works for me as well, although my device owner restricts the installation of apps and unknown sources by the user. Even if I running this example as device admin, I've got the java.lang.SecurityException: User restriction prevents installing.

openSession is checking for permissions. With this simple modificaton it is possible to reset the user restrictions only during a short method call.

public static DevicePolicyManager getDpm(Context context) {
    return (DevicePolicyManager)context.getSystemService(Context.DEVICE_POLICY_SERVICE);
}

public static ComponentName getAdmin(Context context) {
    return new ComponentName(context, MyDevicePolicyReceiver.class);
}

public static void addMyRestrictions(Context context) {
   getDpm(context).addUserRestriction(getAdmin(context), UserManager.DISALLOW_INSTALL_APPS);
   getDpm(context).addUserRestriction(getAdmin(context), UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
}

public static void clearMyRestrictions(Context context) {    
   getDpm(context).clearUserRestriction(getAdmin(context), UserManager.DISALLOW_INSTALL_APPS);
   getDpm(context).clearUserRestriction(getAdmin(context), UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
}

public static void installPackage(Context context, InputStream inputStream)
    throws IOException {
    PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
    int sessionId = packageInstaller.createSession(new PackageInstaller
        .SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL));

    //openSession checks for user restrictions
    clearMyRestrictions(context);
    PackageInstaller.Session session = packageInstaller.openSession(sessionId);
    addMyRestrictions(context);

    long sizeBytes = 0;

    OutputStream out = null;
    out = session.openWrite("my_app_session", 0, sizeBytes);

    int total = 0;
    byte[] buffer = new byte[65536];
    int c;
    while ((c = inputStream.read(buffer)) != -1) {
        total += c;
        out.write(buffer, 0, c);
    }
    session.fsync(out);
    inputStream.close();
    out.close();

    // fake intent
    IntentSender statusReceiver = null;
    Intent intent = new Intent(context, SomeActivity.class);
    PendingIntent pendingIntent = PendingIntent.getBroadcast(context,
        1337111117, intent, PendingIntent.FLAG_UPDATE_CURRENT);

    session.commit(pendingIntent.getIntentSender());
    session.close();
}

Please take care of exception handling.

Mr. Fish
  • 827
  • 7
  • 18
0

In Android Api-21 below is code snippet via which we can install apk silently.

private void runInstallWrite() throws IOException, RemoteException {
        long sizeBytes = -1;

        String opt;
        while ((opt = nextOption()) != null) {
            if (opt.equals("-S")) {
                sizeBytes = Long.parseLong(nextOptionData());
            } else {
                throw new IllegalArgumentException("Unknown option: " + opt);
            }
        }

        final int sessionId = Integer.parseInt(nextArg());
        final String splitName = nextArg();

        String path = nextArg();
        if ("-".equals(path)) {
            path = null;
        } else if (path != null) {
            final File file = new File(path);
            if (file.isFile()) {
                sizeBytes = file.length();
            }
        }

        final SessionInfo info = mInstaller.getSessionInfo(sessionId);

        PackageInstaller.Session session = null;
        InputStream in = null;
        OutputStream out = null;
        try {
            session = new PackageInstaller.Session(mInstaller.openSession(sessionId));

            if (path != null) {
                in = new FileInputStream(path);
            } else {
                in = new SizedInputStream(System.in, sizeBytes);
            }
            out = session.openWrite(splitName, 0, sizeBytes);

            int total = 0;
            byte[] buffer = new byte[65536];
            int c;
            while ((c = in.read(buffer)) != -1) {
                total += c;
                out.write(buffer, 0, c);

                if (info.sizeBytes > 0) {
                    final float fraction = ((float) c / (float) info.sizeBytes);
                    session.addProgress(fraction);
                }
            }
            session.fsync(out);

            System.out.println("Success: streamed " + total + " bytes");
        } finally {
            IoUtils.closeQuietly(out);
            IoUtils.closeQuietly(in);
            IoUtils.closeQuietly(session);
        }
    }

The above code is been took from Framework here

Can i use this code with device_owner or normal user in LoLiipop ?

Answer - No Since there are apis which is been @hide tag in android frameworks, although PackageManager.Session is introduced in API 21 but we cannot use new PAckageManager.Session() since it @hide in API 21.

If you wanna still use this code via framework.jar , you need to build Lolippop source code and extract jar from out/..../framework.jar and call above apis.

KOTIOS
  • 11,177
  • 3
  • 39
  • 66
  • 3
    Now, with new Android M, silent install is possible as a device owner. I would like to use the PackageInstaller API but your code example didn't help unfortunately. Does someone has a better example of how to use PackageInstaller? – ZouBi Jun 25 '15 at 03:12
-3

INSTALL:

Intent promptInstall = new Intent(Intent.ACTION_VIEW);
        promptInstall.setDataAndType(Uri.fromFile(new File(Environment
                .getExternalStorageDirectory() + "/download/" + APK_NAME)),
                "application/vnd.android.package-archive");
        promptInstall.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(promptInstall);

UNINSTALL:

Intent intent = new Intent(Intent.ACTION_DELETE, Uri.fromParts("package",
getPackageManager().getPackageArchiveInfo(apkUri.getPath(), 0).packageName,null));
startActivity(intent);
Vibhor Chopra
  • 647
  • 4
  • 14
  • 1
    Please read my question carefully. I am asking about how to use PackageInstaller(introduced in android Lollipop) class for installing apk. – Sud Nov 12 '14 at 10:54