This explains the Selective Dex encryption feature added in version 3.2.0.0.
To use the Selective DEX encryption feature safely and effectively, compose the list of encryption targets based on the following four criteria. If the selection is inappropriate, the app may not run properly (crash), or to avoid runtime crashes, some classes may remain unencrypted.
For instructions on how to set up selective encryption in the developer console, please refer to the document below.
1. Avoid classes directly referenced at the app entry point
Android runtime verifies not only the class to be executed but also other classes referenced by it before execution. Since encrypted classes can only be used after decryption by the security module, classes that are directly created or called at entry points such as Application or Activity, and the classes they reference, are automatically excluded from encryption to ensure execution stability.
class MainActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// ServerManager is directly referenced in Activity,
// so it remains in plaintext for boot stability.
ServerManager.init(this)
}
}
Conversely, business logic that is not directly referenced anywhere in the screen start code but only runs when the user actually uses a specific feature is suitable for encryption, as shown below.
// Not referenced in Application/Activity start code
// Runs only when the user actually uses the feature.
class LicenseCalculator {
fun calculate(input: Input): Result { ... }
}Core algorithms, billing logic, and content processing logic that have high protection value but are unrelated to app startup are the best encryption targets.
2. If using obfuscation tools such as R8/ProGuard, update the list with each build
Obfuscated APKs do not retain the original source code class names. For example, if the mapping.txt generated during build contains the following,
# mapping.txt (generated by R8/ProGuard during build) com.example.app.DataModule -> a.b:
If you specify the original name (DataModule) in the list as is, it will not match because only the obfuscated name a.b exists in the actual APK, so it will be silently ignored. Since obfuscated names can change with each build, update the list based on the mapping.txt of each build or fix the class name with R8/ProGuard keep rules before specifying it.
3. Exclude classes accessed via reflection or JNI and third-party libraries
If classes are referenced only by strings as shown below, static analysis cannot detect this dependency.
// Dependency exists only as a string,
// so static analysis cannot detect it.
val tracker = Class.forName("com.example.analytics.Tracker")// The same applies to JNI, // where native code looks up classes by name at runtime. jclass cls = (*env)->FindClass(env, "com/example/analytics/Tracker");
If such classes are included in the encryption targets, a crash may occur at the moment the code runs. Third-party libraries such as analytics SDKs and crash reporting SDKs extensively use reflection and JNI internally, so limit encryption targets to your own business code.
4. Always test on actual devices after changing the list
Automatic decisions based on static analysis cannot predict all string-based accesses. After changing the encryption target list, test booting and key functional scenarios (including screens/features related to the changed targets) on actual devices to verify normal operation.