Understanding the magic of APK installation process in Android 🪄
Learn about the android package installation process, install location, components involved, and how they are managed.
8 min read
Hello folks 👋, We install applications daily from various sources. Do you know how android handles the installation process? 🤔 No? Don't worry, That's what we are gonna discuss in this article. So grab a cup of tea ☕ with you and jump straight into the article.
What will you get out of this article?
This article covers how android handles the application installation process.
- What are the different ways we can install applications?
- How does android handle the installation process?
- How the default application
PackageInstallerperforms its magic?
- How the flow goes from one activity to another?
- What are the various components involved in this process?
Diffrent ways to install APK 🌿
There are a few ways to install Android applications:
Using an app store client (i.e. Google Play Store). This is how most of the users install applications.
Downloading the APK file to the device and then opening it. This can only be installed if the "Unknown sources" option is enabled in the setting. We will consider this method throughout this blog.
adb installcommand of Android SDK, which at the end invokes the
pm installcommand which goes to
cmd package install.
By directly copying an APK file to one of the system application directories. When an APK file is copied directly to one of the application directories, it automatically gets detected and installed.
The almighty Android OS 💪🏼
Recently, Android has become the world’s most popular mobile platform. Originally it was designed for smartphones but now powers tablets, TVs, and wearable devices and will soon even be found in cars.
Android is built on top of the Linux kernel. In the android system architecture, Applications lies at the highest level. In Android, each app gets its own directory for saving data. Android assigns each application a UID (userID) at the installation time. It is a constant value that does not change until the app is reinstalled. It is different from the PID (processID), which keeps changing.
Android uses the UID to set up a kernel-level Application Sandbox. The kernel enforces security between apps and the system at the process level through standard Linux facilities, such as user and group IDs assigned to apps. By default, apps can’t interact with each other and have limited access to the OS.
There are mainly two categories of Android applications.
1. System Apps
Included in the OS image, it is a read-only application that cannot be uninstalled or changed by a normal user. System applications can be found in
/system/app/ directory, while some privileged applications are present in
/system/priv-app/ directory. The
/system/vendor/app/ directory hosts vendor-specific applications.
2. User Apps
User-installed apps are installed on a dedicated read-write partition (typically
mounted as /data) that hosts user data and can be uninstalled or changed. User-installed applications can be found in
Both system and the user-installed application create a data directory at the
/data/data/ directory. The user data partition also hosts the optimized DEX files for user-installed applications in
Here, the first column indicates permission for the application data folder. The third and fourth column indicate owner and group, respectively. last is the application package name directory, where the data is stored.
Installation process flow 🍂
PackageInstaller and PackageManager
PackageInstaller is the default application for installing any application on your android device. It provides an interactive interface to install a normal package.
PackageManager is a class for retrieving various information related to the application packages currently installed on the device. It is abstract class and concrete implementation is provided by
ApplicationPackageManager which is created in
Any application installed using its APK file is considered an "unknown source". The actual definition of unknown source is a bit broader; when started, PackageInstaller retrieves the UID and the app package that requested APK installation. It checks if requesting app is privileged (present in
/system/priv-app/, i.e. GooglePackageInstaller), if not, then it is considered an unknown source.
The package that is to be installed can be in the form of a content URI, or it can be a file URI. The first activity that takes place is the
InstallStart activity which decides which activity is the first visible activity of the installation and forwards the intent to it.
If a package gets installed from a content URI (e.g. "content://com.android.externalstorage.docum.."), then the
InstallStaging activity is started. It loads the package and turns it into an installation from a file.
StagingAsyncTask and gets a package file (e.g. "file:///data/user_de/0/com.google.android.packageinstaller/no_backup/package318526161049147654.apk") from the content URI.
The output of this task could result in success or failure.
If any error occurs in this process, the error result is set, and
showError() is called. If the file is staged, then it starts
DeleteStagedFileOnResult, which at the end calls
PackageInstallerActivity and deletes the staged install file.
Installing a package from an APK file is also referred to as a side-loading app.
PackageInstallerActivity is launched when an application is installed via sideloading.
PackageInstallerActivity, the package is first parsed, and the user is notified of parse errors via a dialog. If the package is successfully parsed, the user is notified to turn on the "Unknown sources" option in the setting. If the package already exists on the device, a confirmation dialog (to replace the existing package) is presented to the user. All state transitions are handled in this activity.
Activity parses the package and checks for any error. If the package is parsed properly, then it sets up the installer for this package.
startInstall() which starts subactivity
InstallInstalling to actually install the application.
InstallInstalling activity sends the package to the package manager and handles the results from the package manager. This has two phases: First, send the data to the package manager, then wait until the package manager processes the result.
This activity checks for the package if there is already an application with the given package name installed on the system for other users, then it calls
PackageManager#installExistingPackage() and installs it for the calling user.
Actual installation is here 👇🏻
InstallInstalling activity creates
PackageInstaller.SessionParams and set some important attributes:
- Package Source
- Package Name
- Size of the Package
- Installation location
- all other important attributes can be found in the Documentation.
This information session is created by
InstallInstalling activity creates
InstallingAsyncTask, AsyncTask that sends the package to the installer. It opens
PackageInstaller.Session, which is retrieved by
openSession() and opens OutputStream using
openWrite(String name, long offsetBytes, long lengthBytes) which opens a stream to write an APK file into the session. Progress of installation is set using
addProgress(float). At the end, commit everything staged in this session.
Result Activity 📲
InstallInstalling activity an broadcast receiver
InstallEventReceiver is registred with observer
When the installation process is finished,
InstallEventReceiver notifies the observer.
launchFinishBasedOnResult launches the appropriate finish activity based on the result.
launchSuccess()if the process is completed successfully.
launchFailure()with the appropriate statusCode, legacyStatus and statusMessage for the failed result.
Updating information in the system database 🗄️
Android OS has two profiles for saving app information in the Android system.
packages.xml. Both files can be located in the
com.google.android.carriersetup 10073 0 /data/user/0/com.google.android.carriersetup default:privapp:targetSdkVersion=28 3003 com.android.wallpaperbackup 1000 0 /data/user/0/com.android.wallpaperbackup platform:privapp:targetSdkVersion=28 1065,3002,1023,3003,3001 com.innersloth.spacemafia 10090 0 /data/user/0/com.innersloth.spacemafia default:targetSdkVersion=30 3003 ... com.kruna1pate1.pictionaryapp 10089 1 /data/user/0/com.kruna1pate1.pictionaryapp default:targetSdkVersion=32 3003
Here, the space is divided into 6 columns, which contain 6 app-related information:
- Name of the application package
- UID of the application. (by looking at them, one can easily map that
- Whether the app is in debug mode, specified by
- Datastore path of the application. (usually
- SEinfo information of the app, includes targetSdkVersion.
- User group to which the app belongs.
A huge file has 5500+ lines. Here is an abstract view of its content.
<packages> <version ... /> <permission-trees> <item name="xyz" package="xyz" /> ... </permission-trees> <permissions> ... </permissions> <package ...> <sigs count="1" schemeVersion="3"> <cert ... /> </sigs> <perms> <item name="xyz" granted="true" flags="0" /> ... </perms> <proper-signing-keyset identifier="6" /> </package> <updated-package ... > <perms> ... </perms> </updated-package> <shared-user ... > <sigs count="1" schemeVersion="3"> <cert index="4" /> </sigs> <perms> ... </perms> </shared-user> <keyset-settings version="1"> <keys> <public-key ... /> ... </keys> <keysets> <keyset identifier="1"> <key-id identifier="1" /> </keyset> </keysets> <lastIssuedKeyId value="17" /> <lastIssuedKeySetId value="17" /> </keyset-settings> </packages>
Key elements of this file:
- Permission block: List of the permissions defined in the system.
- Package block: Details of the installed application.
- Updated-package block: Information associated with updated packages.
- Shared-user block: Information of system-defined share user.
- Keyset-settings block: contains the public key information of the installed app signature.
Notify other components 🔔
Finally, changes to the package database (new package entry and any new permissions) are persisted on the disk.
PackageManagerService sends the
ACTION_PACKAGE_REPLACED in case of an update to notify other components about the newly added application.
To conclude this article, Here, we have seen there are various ways one can install an application.
The package is first parsed by
PackageInstaller and then the session is created. An app is delivered for installation through a
PackageInstaller.Session. Once the session is created, the installer can stream APK into place until it decides to either commit or destroy the session. We have also seen various components involved in this process and the flow of the process.
Once the application is installed system
packages.xml file is updated.
I hope you have got something out of it. Feel free to give your reaction and leave a comment down below 📝. Any feedback would be greatly appreciated 🙂.
Android Security Internals: An In-Depth Guide to Android's Security Architecture
Dzone - depth android package manager
Android development system packages file parsing