1

I have a Custom Launcher .apk that I ADB Sideload. I 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.

  • adb root
  • adb push <path/file.apk> /system/app/
  • adb shell
  • chmod 644 /system/app/HelloWorld.apk
  • reboot

The App is a "System-App" and should remain one after updating. How can I update it without using Google Play Store? It is a KIOSK Application and it should be updatable from the APP itself.

Android kiosks are customer facing Android devices that serve a single purpose by running only a single app.

What are my Options here?/What I have already found

OTA updates are designed to upgrade the underlying operating system, the read-only apps installed on the system partition.

  • What other Options do I have?

Android:

  • minSdkVersion: 29
  • targetSdkVersion: 31
Ojav
  • 678
  • 6
  • 22
  • 1
    Even the system app can be uploaded to the Google Play store and can offer OTA updates. Just make sure it has the same signature as the play store. Many apps like Youtube offer the OTA in such a way. – Dhruv Kaushal Sep 06 '22 at 13:01

2 Answers2

2

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

  1. I first checked if my app is correctly installed as a /system application
package:/system/app/AndroidCarHmi.apk=com.crossware.androidcarhmi
  1. I used AppInstaller choosed new .apk and updated the System App

  2. After PackageInstaller Update( inside /data/ )

package:/data/app/com.crossware.androidcarhmi-7eF4dQkhk2iA0OeDt0kXyA==/base.apk=com.crossware.androidcarhmi
  1. After restart (Still Considered a System App)
package:/data/app/com.crossware.androidcarhmi-7eF4dQkhk2iA0OeDt0kXyA==/base.apk=com.crossware.androidcarhmi
  1. After Research I found that it doesn't have to be inside the system directories for android to consider it a system app SOURCES:

  2. 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
  1. 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>

All Mappings

<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
  1. 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

OTA_Update.png

Picture Source

See Source Code Example Here

Ojav
  • 678
  • 6
  • 22
  • Is it possible install another app as system app from my system app (installed in /system/app)? I think not because the systme file is read only – Giuseppe Laera Oct 10 '22 at 07:24
  • I dont think so, system apps have to be pre-installed or through ADB forcing installation inside the /system/ folder. My goal was only updating System Apps, if your System App has super user permissions you can try method 3.) and keep me updated if it worked this way.(installing a new system app) But I assume method 1.) and 2.) will not work – Ojav Oct 10 '22 at 07:37
1

The app can't update itself from within itself. You would need to create a separate updater app, and put it on the system partition as well.

That updater app can use this technique, but unless the device is rooted, it'll prompt the user to allow app installs from your updater app.

And yes, you could also set up OTA updates, but that assumes that you actually have the signing keys for the OS on that device, and I think it's way more complex that just creating a separate updater app

user496854
  • 6,461
  • 10
  • 47
  • 84
  • hey i have found 5 methods to update/install an apk (Intent, PackageInstaller, ADB Sideloading, OTA Updates, With a rooted Device just using the shell and doing the same as ADB sideloading) – Ojav Sep 07 '22 at 13:27
  • I then used the PackageInstaller methods since this is the preferred method after API 29 and it works even within the API itself. The updated apk is saved inside data/app but is somehow still considered a System App. Thanks for your help – Ojav Sep 07 '22 at 13:28
  • I have to try it on a physical device because after restarting within the emulator(android restard) everything is still a system app but once I do a full restart of the emulator it is no longer a system app – Ojav Sep 07 '22 at 13:29