Silent Installation of APK on RK3399 Android 8.1

On RK3399 Android 8.1, we implement the silent installation feature for APKs. Below is our testing process:1. Generate the system signing KEY in the RK3399 Android 8.1 source directory: Navigate to the build/target/product/security directory:

cd build/target/product/security

Create a build_system_jks2.sh file with the following content:

#!/bin/bash
echo "Generating system signing file..."
openssl pkcs8 -inform DER -nocrypt -in platform.pk8 -out platform.pem
openssl pkcs12 -export \ 
          -in platform.x509.pem \ 
           -out platform.p12 \ 
           -inkey platform.pem \ 
           -password pass:android \ 
           -name system
keytool -importkeystore \ 
           -destkeystore ./systemkjd.jks \ 
           -srckeystore ./platform.p12 \ 
           -srcstoretype PKCS12 \ 
           -srcstorepass android
echo "Generation complete!"

After saving, execute the following commands:

chmod +x build_system_jks2.sh
./build_system_jks2.sh

After execution, the following message will be displayed:

Generating system signing file...
Importing keystore ./platform.p12 to ./systemkjd.jks...
Enter destination keystore password:
Enter the password you saved for the KEY, I entered stxinu, then press Enter:
Re-enter new password:
Enter stxinu again and press Enter:
Entry for alias system successfully imported.
Import command completed:  1 entries successfully imported, 0 entries failed or cancelled
Warning: The JKS keystore uses a proprietary format. It is recommended to migrate to PKCS12 which is an industry standard format using "keytool -importkeystore -srckeystore ./systemkjd.jks -destkeystore ./systemkjd.jks -deststoretype pkcs12".
Generation complete!

At this point, a systemkjd.jks file will be generated in the current directory, which is the system signing KEY needed for the Android Studio project.

2. Modifications related to the Android Studio project: We created a project named SilentInstallAPK, navigate to the root directory of the project and create a signature directory, then copy the generated systemkjd.jks file into this directory. Modify the app’s build.gradle file by adding the following content in the android configuration section:

signingConfigs {        rk3399_8 {            keyAlias 'system'            keyPassword 'android'            storeFile file('../signature/systemkjd.jks')            storePassword 'stxinu'        }}

The values system, android, and stxinu correspond to those used when generating the KEY, all highlighted in bold. Then modify the buildTypes section to the following:

buildTypes {    release {        minifyEnabled false        proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'        signingConfig signingConfigs.rk3399_8    }    debug {        minifyEnabled false        proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'        signingConfig signingConfigs.rk3399_8    }}

Next, add the UID and Permission configuration in AndroidManifest.xml, with the bold parts being the content to be added:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"    android:sharedUserId="android.uid.system"    package="com.slam.test">    <uses-permission android:name="android.permission.INSTALL_PACKAGES"/>

Next, we have the code content, with the key code encapsulated as follows:

public static boolean silentInstall(PackageManager packageManager, String apkPath) {    Class pmClz = packageManager.getClass();    try {        if (Build.VERSION.SDK_INT >= 21) {            Class aClass = Class.forName("android.app.PackageInstallObserver");            Constructor constructor = aClass.getDeclaredConstructor();            constructor.setAccessible(true);            Object installObserver = constructor.newInstance();            Method method = pmClz.getDeclaredMethod("installPackage", Uri.class, aClass, int.class, String.class);            method.setAccessible(true);            method.invoke(packageManager, Uri.fromFile(new File(apkPath)), installObserver, 2, null);        } else {            Method method = pmClz.getDeclaredMethod("installPackage", Uri.class, Class.forName("android.content.pm.IPackageInstallObserver"), int.class, String.class);            method.setAccessible(true);            method.invoke(packageManager, Uri.fromFile(new File(apkPath)), null, 2, null);        }        return true;    } catch (Exception e) {        Log.e("stxinu", e.toString());    }    return false;}

Then, we can call the above function where needed, below is our example code snippet:

PackageManager mPm = getApplicationContext().getPackageManager();if (silentInstall(mPm, "/data/test.apk")) {    Toast.makeText(getApplicationContext(), "silent install success!", Toast.LENGTH_LONG).show();} else {    Toast.makeText(getApplicationContext(), "silent install fail!", Toast.LENGTH_LONG).show();}

Next, we will place the APK to be silently installed in the Android machine’s /data directory with the name test.apk, as determined by the bold content in the test code above.

After running this test code, /data/test.apk will be automatically installed into the system without any interface changes during the process.

Leave a Comment