Conclusion
It is possible to Update a System App with the PackageInstaller
or Intent
Method IF it was firstly installed as a System App. Here is everything I tried and a full overview on all Methods to install an apk.
Approach
My first goal was to find all possible ways on how to install .apks programmatically on android. The most popular method was the Intent
method which is basically deprecated starting from API 21 followed by the PackageInstaller
, using the shell
method which requires super user (root) permissions or the OTA Updates
. I quickly describe all methods and explain some pros and cons.
Methods
1. Package Installer API
(from api 21 recommended to use from api 29)
Summary: pros and cons
still considered a System App with all System Permissions
the app will no longer be under the /system/ path (after updating) since it is a read-only storage under Android
APKs must have the exact same package name, version code, and signing
certificates
app must already be a system app, only then updating and keeping System Permissions is possible.
it is possible to update within the .apk itself or from another .apk doesn't matter (we are basically an AppStore)
PackageInstaller
is designed for more complex scenarios,
including dealing with split APKs, where a single app might require more
than one APK to completely install. As a result, it has a convoluted
API, to go along with the typical skimpy documentation. Keeping track of version etc is no problem unlike with the Intent method.
Source Code, Tutorial used
PackageInstaller Tutorial
See Source Code Example here
PackageInstaller API Documentation
My Approach and Conclusion
- I first checked if my app is correctly installed as a /system application
package:/system/app/AndroidCarHmi.apk=com.crossware.androidcarhmi
I used AppInstaller choosed new .apk and updated the System App
After PackageInstaller Update( inside /data/ )
package:/data/app/com.crossware.androidcarhmi-7eF4dQkhk2iA0OeDt0kXyA==/base.apk=com.crossware.androidcarhmi
- After restart (Still Considered a System App)
package:/data/app/com.crossware.androidcarhmi-7eF4dQkhk2iA0OeDt0kXyA==/base.apk=com.crossware.androidcarhmi
After Research I found that it doesn't have to be inside the system directories for android to consider it a system app SOURCES:
I tried to confirm it myself if it still is a System App using ADB command list with parameter -s which only prints System Apps. (Conclusion ADB itself still considers it a SystemApp)
//List all System Packages with location
adb shell pm list packages -s -f
- Printing a dumpsys of my apk package and check what permissions are granted.
adb shell dumpsys package my.package.name
ADB itself considers it a System App.
dumpsys.txt shows all permissions are still present.
2. Intent API
Same as above but for API smaller than 22
Add provider to AndroidManifest.xml
<application
........
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/>
</provider>
.........
</application>
Create the missing provider_paths.xml
file inside of res/xml/
provider_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="Download" path="/" />
</paths>
Choose the correct Provider path mapping. Example: If you are using Environment.getExternalStorageDirectory()
the correct mapping is <external-path>
<files-path/> --> Context.getFilesDir()
<cache-path/> --> Context.getCacheDir()
<external-path/> --> Environment.getExternalStorageDirectory()
<external-files-path/> --> Context.getExternalFilesDir(String)
<external-cache-path/> --> Context.getExternalCacheDir()
<external-media-path/> --> Context.getExternalMediaDirs()
Make sure under App-> Permission -> Allow management of all files! TODO ask for permission programmatically!
Source Code (Java)
//Context context = MainActivity.this;
File download_directory = new File(Environment.getExternalStorageDirectory(), "Download");
File apk_file = new File(download_directory, "app-debug.apk");
String file_path_apk = apk_file.getPath();
try {
Uri apkUri = FileProvider.getUriForFile(MainActivity.this, MainActivity.this.getPackageName() + ".provider", apk_file);
Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
intent.setData(apkUri);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
MainActivity.this.startActivity(intent);
} catch (IOException e) {
Log.e("Exception ", e.toString());
}
\pagebreak
3. Super User Application
- requires Rooted Device and application has to receive Super User permissions
- Give Application Super User Permissions and use Shell exactly as ADB sideloading
Example:
Process process = Runtime.getRuntime().exec("same command as adb just copy the .apk into /system/app set permissions to (rw-r-r) and reboot");
\pagebreak
4. ADB Sideloading
You can simply copy the .apk into /system/app or /system/priv-app, set the appropriate permissions (rw-r-r, root:root) and then do a reboot. That's it. The App is considered a System App.
//make the file system writable first--- Currently only for Emulator/test on physical device
emulator -writable-system @name_of_emulator
adb root
adb shell avbctl disable-verification
adb reboot
adb root
adb remount
adb push <Path\to\app.apk> /system/app
//set correct file permissions
adb shell
chmod 644 /system/app/<app.apk>
reboot
//Check if it is installed correctly
adb shell
cd system/app/
ls
4.5 System Updates/OTA Updates(Over-the-Air)
If you want to update a System App according to the Android Developer Site this is the correct way.
OTA updates are designed to upgrade the underlying operating system, the read-only apps installed on the system partition.
Needs signing keys for the Operating System on that device
A/B (Seamless) system update

Picture Source
See Source Code Example Here