> For the complete documentation index, see [llms.txt](https://docs.mosip.io/1.2.0/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.mosip.io/1.2.0/id-lifecycle-management/identity-issuance/android-registration-client/develop/j11-to-j21.md).

# j11-to-j21

### Overview

This page documents the sequence of changes performed to migrate the project's Android modules from Java 11 to Java 21. Each step covers a specific configuration, dependency, or code change required to bring the build, tests, and runtime into compatibility with JDK 21 and Android Gradle Plugin 8.x.

### Step 1 — Upgrade JDK

Install JDK 21 and update environment variables.

**Windows — System Environment Variables**

```
JAVA_HOME = C:\Program Files\Java\jdk-21.x.x-hotspot
PATH      = %JAVA_HOME%\bin;%PATH%

# Verify
java -version
# openjdk version "21.x.x"
```

In Android Studio: *File > Settings > Build, Execution, Deployment > Build Tools > Gradle > Gradle JDK* → set to **JDK 21**.

***

### Step 2 — Upgrade Gradle and AGP

#### android/gradle/wrapper/gradle-wrapper.properties

**Before**

properties

```
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
```

**After**

properties

```
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-all.zip
```

#### android/build.gradle

**Before**

```
classpath 'com.android.tools.build:gradle:7.x.x'
```

**After**

```
classpath 'com.android.tools.build:gradle:8.3.2'
```

***

### Step 3 — Update compileSdkVersion and targetSdkVersion

In android/build.gradle (root ext block):

**Before**

```
compileSdkVersion = 32
targetSdkVersion  = 32
```

**After**

```
compileSdkVersion = 34
buildToolsVersion = "34.0.0"
targetSdkVersion  = 34
```

***

### Step 4 — Set Java 21 compileOptions in All Modules

Applied to clientmanager, keymanager, packetmanager, transliterationmanager, and the app module.

**Before**

```
compileOptions {
    sourceCompatibility JavaVersion.VERSION_11
    targetCompatibility JavaVersion.VERSION_11
}
```

**After**

```
compileOptions {
    sourceCompatibility JavaVersion.VERSION_21
    targetCompatibility JavaVersion.VERSION_21
}
```

***

### Step 5 — Add Namespace to build.gradle (AGP 8.x Requirement)

> ℹ️ **Note** — AGP 8.x rejects the package= attribute in AndroidManifest.xml. The package identifier must be declared as namespace inside build.gradle.

#### AndroidManifest.xml — remove the package attribute

**Before**

```
<manifest package="io.mosip.registration.clientmanager" ...>
```

**After**

```
<manifest ...>
```

#### build.gradle — add the namespace

```
android {
    namespace 'io.mosip.registration.clientmanager'  // added
}
```

Applied to all four library modules and the app module.

#### Auto-fallback for third-party Flutter plugins

Added to the root build.gradle:

```
subprojects {
    plugins.withId("com.android.library") {
        def androidExt = project.extensions.findByName("android")
        if (androidExt != null && androidExt.namespace == null) {
            androidExt.namespace = "com.auto.${project.name}"
        }
    }
}
```

***

### Step 6 — Enable BuildConfig Generation (AGP 8.x)

AGP 8.x disables BuildConfig class generation by default. It must be explicitly enabled in any module that references BuildConfig.\* constants.

Applied to clientmanager/build.gradle, keymanager/build.gradle, and app/build.gradle:

```
android {
    buildFeatures {
        buildConfig true   // required by AGP 8.x — disabled by default
    }
}
```

Without this, any class referencing BuildConfig.BASE\_URL, BuildConfig.CLIENT\_VERSION, etc. fails to compile.

***

### Step 7 — Enable Core Library Desugaring (app module)

In android/app/build.gradle:

```
compileOptions {
    sourceCompatibility JavaVersion.VERSION_21
    targetCompatibility JavaVersion.VERSION_21
    coreLibraryDesugaringEnabled true    // added
}

dependencies {
    coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:2.0.4"
}
```

Java 21 lets us use modern APIs like java.time.\* and streams, but older Android devices (below API 26) don't have these classes built in. Desugaring backports them into the APK so the app runs everywhere.

It is also required because BouncyCastle 1.78.1 (Step 7) and Jackson's JavaTimeModule (Step 8) internally use these modern APIs. Version **2.0.4** is used because AGP 8.x requires it.

***

### Step 8 — Upgrade BouncyCastle (keymanager)

> ⚠️ **Warning** — The bcprov-jdk15on and spongycastle artifacts are **not published for JDK 18+**.

#### keymanager/build.gradle

**Before**

```
implementation 'org.bouncycastle:bcprov-jdk15on:1.x'
implementation 'org.bouncycastle:bcpkix-jdk15on:1.x'
// or spongycastle:
implementation 'com.madgag.spongycastle:core:1.x'
```

**After**

```
implementation "org.bouncycastle:bcprov-jdk18on:1.78.1"
implementation "org.bouncycastle:bcpkix-jdk18on:1.78.1"
```

#### Update imports in LocalClientCryptoServiceImpl.java

**Before**

java

```
import org.spongycastle.crypto.InvalidCipherTextException;
import org.spongycastle.crypto.digests.SHA256Digest;
import org.spongycastle.crypto.encodings.OAEPEncoding;
import org.spongycastle.crypto.engines.RSAEngine;
import org.spongycastle.crypto.params.RSAKeyParameters;
```

**After**

java

```
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.digests.SHA256Digest;
import org.bouncycastle.crypto.encodings.OAEPEncoding;
import org.bouncycastle.crypto.engines.RSAEngine;
import org.bouncycastle.crypto.params.RSAKeyParameters;
```

#### Replace PEMWriter with JcaPEMWriter in CertificateManagerUtil.java

PEMWriter was removed in bcpkix-jdk18on.

**Before**

java

```
import org.bouncycastle.openssl.PEMWriter;
try (PEMWriter pemWriter = new PEMWriter(stringWriter)) { ... }
```

**After**

java

```
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
try (JcaPEMWriter pemWriter = new JcaPEMWriter(stringWriter)) { ... }
```

***

### Step 9 — Remove jackson-module-afterburner (keymanager)

jackson-module-afterburner uses bytecode generation that is incompatible with JDK 21. It was removed from dependencies and from JsonUtils.java.

#### JsonUtils.java

**Before**

java

```
import com.fasterxml.jackson.module.afterburner.AfterburnerModule;
static {
    objectMapper = new ObjectMapper();
    objectMapper.registerModule(new AfterburnerModule());   // removed
    objectMapper.registerModule(new JavaTimeModule());
}
```

**After**

java

```
import com.fasterxml.jackson.databind.json.JsonMapper;
static {
    objectMapper = JsonMapper.builder().build();
    objectMapper.registerModule(new JavaTimeModule());
    objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
    objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
}
```

***

### Step 10 — Upgrade Jackson, Mockito, Lombok, Robolectric and Biomterics-utils

Updated in all module build.gradle files (versions centralized in the root ext {} block).

| Dependency                            | Before  | After                                 |
| ------------------------------------- | ------- | ------------------------------------- |
| jackson-core / databind / annotations | 2.x     | 2.16.2                                |
| mockito-core                          | 4.x     | 5.3.1                                 |
| mockito-android                       | 4.x     | 5.3.1                                 |
| mockito-inline                        | present | removed (built into mockito-core 5.x) |
| Lombok                                | 1.18.x  | 1.18.32                               |
| Robolectric                           | 4.x     | 4.13                                  |
| Jacoco                                | < 0.8.9 | 0.8.12                                |
| biometrics-util                       | 1.2.0.2 | 1.3.0                                 |

> ℹ️ **Info** — Remove the separate mockito-inline dependency — Mockito 5.x includes inline mocking in mockito-core by default. No code change is required for MockedStatic usage.

***

### Step 11 — Add Jetifier Ignorelist

Jetifier was causing issues processing jackson-core and fastdoubleparser artifacts after the Jackson upgrade.

Added to android/gradle.properties:

properties

```
android.jetifier.ignorelist=jackson-core, fastdoubleparser
```

This tells the Jetifier tool to skip these artifacts, avoiding the conversion errors.

***

### Step 12 — Add JVM Arguments for Test Modules

Required for Mockito and byte-buddy to access JDK internal APIs under JPMS restrictions.

Added to each module's build.gradle:

```
testOptions {
    unitTests {
        all {
            jvmArgs(
                '-XX:+EnableDynamicAgentLoading',
                '-Dnet.bytebuddy.experimental=true',
                '--add-opens', 'java.base/java.lang=ALL-UNNAMED',
                '--add-opens', 'java.base/java.util=ALL-UNNAMED',
                '--add-opens', 'java.base/java.lang.reflect=ALL-UNNAMED'
            )
        }
    }
}
```

***

### Step 13 — Fix Kotlin JVM Target Mismatch for Flutter Plugins

Third-party Flutter plugins declare sourceCompatibility = 1.8, but Kotlin 1.9.x on JDK 21 defaults jvmTarget to 21, causing a build warning or error.

Added to the root build.gradle:

```
subprojects {
    plugins.withId('org.jetbrains.kotlin.android') {
        tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile).configureEach {
            kotlinOptions.jvmTarget = project.android.compileOptions.sourceCompatibility.toString()
        }
    }
}
```

Added to android/gradle.properties:

properties

```
kotlin.jvm.target.validation.mode=WARNING
```

***

### Step 14 — Disable Unit Tests for Broken Third-Party Flutter Plugins

These plugins use Mockito without the required JVM args, and their source cannot be modified.

Added to the root build.gradle:

```
def thirdPartyPluginsWithBrokenTests = [
    'flutter_plugin_android_lifecycle',
    'geolocator_android',
    'image_picker_android',
    'url_launcher_android',
    'shared_preferences_android',
    'webview_flutter_android'
]
if (thirdPartyPluginsWithBrokenTests.any { project.name.contains(it) }) {
    project.tasks.whenTaskAdded { task ->
        if ((task.name.contains("UnitTest") || task.name.contains("unitTest"))
                && !task.name.contains("LintModel")) {
            task.enabled = false
        }
    }
}
```

***

### Step 15 — Patch flutter\_config Manifest (AGP 8.x)

flutter\_config 2.0.2 still uses package= in its AndroidManifest.xml, which AGP 8.3+ rejects. Since the source cannot be modified, it is patched at build time.

Added to the root build.gradle:

```
subprojects { project ->
    if (project.name == 'flutter_config') {
        project.afterEvaluate {
            def manifest = new File(project.projectDir, "src/main/AndroidManifest.xml")
            if (manifest.exists()) {
                def original = manifest.text
                def patched  = original.replaceAll(/ package="[^"]*"/, '')
                if (original != patched) manifest.text = patched
            }
        }
    }
}
```

***

### Step 16 — Validate the Build

Run the following commands to validate the migration.

**Test each module individually**

powershell

```
.\gradlew.bat :clientmanager:testDebugUnitTest
.\gradlew.bat :keymanager:testDebugUnitTest
.\gradlew.bat :packetmanager:testDebugUnitTest
.\gradlew.bat :transliterationmanager:testDebugUnitTest
```

**Full project build**

powershell

```
.\gradlew.bat build
```

**Generate APK**

powershell

```
.\gradlew.bat assembleDebug
```

**Clean and refresh Flutter dependencies** (from the project root)

powershell

```
flutter clean
flutter pub get
```

**Build a debug APK**

powershell

```
flutter build apk --debug
```

**Build a release APK**

powershell

```
flutter build apk --release
```

The generated APK is written to build/app/outputs/flutter-apk/.

***

### Migration Summary

| Area                | Change                                                              |
| ------------------- | ------------------------------------------------------------------- |
| **Toolchain**       | JDK 11 → JDK 21, Gradle 7.5 → 8.5, AGP 7.x → 8.3.2                  |
| **Android SDK**     | compileSdk/targetSdk 32 → 34, buildTools 34.0.0                     |
| **Manifest**        | Migrated package attribute to namespace in build.gradle             |
| **App module**      | Enabled core library desugaring with desugar\_jdk\_libs 2.0.4       |
| **Security**        | BouncyCastle jdk15on / SpongyCastle → bcprov/bcpkix-jdk18on 1.78.1  |
| **JSON**            | Removed jackson-module-afterburner; upgraded Jackson to 2.16.2      |
| **Testing**         | Mockito 5.3.1, Robolectric 4.13, Lombok 1.18.32, Jacoco 0.8.12      |
| **JVM args**        | Added --add-opens and dynamic agent loading for tests               |
| **Flutter plugins** | Kotlin jvmTarget alignment, manifest patches, broken tests disabled |


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.mosip.io/1.2.0/id-lifecycle-management/identity-issuance/android-registration-client/develop/j11-to-j21.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
