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.