Mobile phone app (Android)

The NBT Personalization mobile phone app (view on GitHub) is a Java-based Android Studio project which demonstrates the personalization of the OPTIGA™ Authenticate NBT via NFC (PERSO).

Installation

Instructions to build the app and install it on an Android device can be found in the README file of the GitHub repository (view on GitHub). For more detailed step-by-step instructions on how to install the app, refer to the use case’s step-by-step guide.

Software architecture

This Android app consists of multiple activities and utilizes the modules of the OPTIGA™ Authenticate NBT Host Library for Java to implement its application logic.

../../_images/nbt_perso_android_software_architecture.png

Fig. 123 High-level software architecture of the Android app.

Components

  1. Mobile application:

    • SplashScreenActivity: Displays the splash screen during app startup.

    • MainActivity: Displays the main menu and allows the user to select a use case configuration.

    • BrandProtectionActivity: Handles the brand protection-specific use case.

    • ConnectionHandoverActivity: Manages the connection handover-specific use case.

    • AdtActivity: Handles the asynchronous data transfer-specific use case.

    • PassThroughActivity: Manages the pass-through-specific use case.

    • ResetActivity: Provides functionality to reset the device.

  2. OPTIGA™ Authenticate NBT host library:

    • CommandSet: Sends APDU commands to the device.

    • CommandBuilder: Formats APDU commands.

    • ResponseParser: Extracts data from device responses.

    • Utilities: Offers cryptographic and data formatting tools.

    • IChannel: Interface, defining communication methods.

  3. Communication channel:

    • InterfaceChannel: Implementation of the IChannel interface for Android.

Application flow

This example application personalizes the OPTIGA™ Authenticate NBT device based on the selected use case configuration. By tapping the NFC antenna of the mobile phone to the device, the app executes the use-case-specific personalization process.

The application starts in the SplashScreenActivity, which displays a splash screen for a few seconds before transitioning to the main menu (MainActivity). The MainActivity displays a menu for selecting a use case configuration. Once a use case is selected, the corresponding activity (e.g., BrandprotectionActivity) is started. The personalization process is triggered when the NFC tag is detected, and the app establishes a communication channel with the device.

The application flow is shown in the following figure.

../../_images/nbt_perso_android_ui.png

Fig. 124 Visualization of the app’s activities during operation.

Processing steps

As soon as the personalization is triggered by tapping the antenna of the OPTIGA™ Authenticate NBT device to the mobile phone, a use case-specific state interface implementation (IState) is called (for example, the BrandprotectionState, which is representing the brand protection use case configuration). Each use case has a specific state configuration that implements the IState interface and holds all essential configurations such as a FileAccessPolicy for each file or certain interface configurations. The personalization itself is started by calling the execute() method of the state class (for example, BrandProtectionState.execute()).

Next to the state interfaces, there are also command flow interfaces (ICommandFlow). They represent essential cascades of commands needed to handle the personalization of the OPTIGA™ Authenticate NBT. The SetFileAccessPolicy class, for example, is generating the command sequence needed to write the file access policies.

Finally, there is also a NDEF file handler interface (INdefHandler) providing two ways of handling the NDEF file content. The AndroidHandler is using libraries provided by the Android itself, and the InfineonHandler is using the libraries provided by Infineon. In both cases ,the methods createConnectionHandoverNdefMessage() or createBrandprotectionNdefMessage() are provided.

Application details

The application is developed using Android Studio and follows the standard Android project structure.

Project layout

The project is structured as a standard Android Studio project. The main application is located in the app folder, and the library modules are located in the app/libs folder. The library modules are included in the project as dependencies.

The following is the directory structure of the project, highlighting the most important folders and files:

Listing 62 Project layout (simplified).
optiga-nbt-example-perso-android/
├── app/                          # Main Android application folder
│   ├── libs/                     # Includes the required library modules
│   │   ├── hsw-apdu-java/        # Dependent library for APDU commands
│   │   ├── hsw-apdu-nbt-java/    # Core library for NBT functionality
│   │   ├── hsw-channel-java/     # Dependent library for communication channels
│   │   ├── hsw-ndef-bp-java/     # Dependent library for NDEF brand protection
│   │   ├── hsw-ndef-java/        # Dependent library for NDEF handling
│   │   └── hsw-utils-java/       # Dependent library for utility functions
│   ├── src/main/                 # Application source code
│   │   ├── java/                 # Java source files
│   │   │   └── com.infineon.css.nbt_personalization/
│   │   │       ├── usecase_personalization/          # Package for personalization use case
│   │   │       ├── AdtActivity.java                  # Activity for ADT use case
│   │   │       ├── BrandProtectionActivity.java      # Activity for Brand Protection use case
│   │   │       ├── ConnectionHandoverActivity.java   # Activity for Connection Handover use case
│   │   │       ├── MainActivity.java                 # Main activity
│   │   │       ├── PassThroughActivity.java          # Activity for Pass-through use case
│   │   │       ├── ResetActivity.java                # Activity for Reset use case
│   │   │       └── SplashScreenActivity.java         # Splash screen activity
│   │   ├── res/                  # Application resources (layouts, strings, etc.)
│   │   └── AndroidManifest.xml   # Application manifest file
│   └── build.gradle              # App-specific Gradle build configuration
├── docs/                         # Documentation for the project
├── gradle/                       # Gradle wrapper configuration files
├── README.md                     # Overview of the Android application
├── gradlew                       # Gradle wrapper script
├── gradlew.bat                   # Gradle wrapper startup script for Windows
└── settings.gradle               # Gradle project settings file

Project configuration

Below is the configuration from this project in the build.gradle file of the app module:

Listing 63 Project configuration in build.gradle.
android {
   compileSdk 35  // Specifies the SDK version used to compile the app
   buildToolsVersion "35.0.0"  // Version of the build tools to use

   defaultConfig {
      applicationId "com.infineon.css.nbt_personalization"  // Unique application ID
      minSdk 30  // Minimum SDK version required to run the app
      targetSdk 34  // Target SDK version for which the app is optimized
      versionCode 20261103  // Build number, the release date (YYYYMMDD)
      versionName "1.0.3"  // User-visible version name
      ...
   }
   ...
}

Integration of the Java host library

This Android app includes the OPTIGA™ Authenticate NBT Host Library for Java. The library modules are manually copied from the GitHub repository, placed in the app/libs folder and added as dependencies to the project, as follows:

Listing 64 Dependencies declared in build.gradle.
   // filepath: app/build.gradle
dependencies {
   ...
   implementation project(':com.infineon.hsw.ndef')
   implementation project(':com.infineon.hsw.utils')
   implementation project(':com.infineon.hsw.channel')
   implementation project(':com.infineon.hsw.ndef')
   implementation project(':com.infineon.hsw.ndef.bp')
   implementation project(':com.infineon.hsw.apdu')
   implementation project(':com.infineon.hsw.apdu.nbt')
   }
Listing 65 Snippet of settings.gradle.
// filepath: settings.gradle
include ':app'
include('com.infineon.hsw.utils')
include('com.infineon.hsw.channel')
include('com.infineon.hsw.ndef')
include('com.infineon.hsw.ndef.bp')
include('com.infineon.hsw.apdu')
include('com.infineon.hsw.apdu.nbt')

// Specify the paths to the library modules
project(':com.infineon.hsw.utils').projectDir = file('app/libs/hsw-utils-java')
project(':com.infineon.hsw.channel').projectDir = file('app/libs/hsw-channel-java')
project(':com.infineon.hsw.ndef').projectDir = file('app/libs/hsw-ndef-java')
project(':com.infineon.hsw.ndef.bp').projectDir = file('app/libs/hsw-ndef-bp-java')
project(':com.infineon.hsw.apdu').projectDir = file('app/libs/hsw-apdu-java')
project(':com.infineon.hsw.apdu.nbt').projectDir = file('app/libs/hsw-apdu-nbt-java')

Handling NFC in the app

The app uses NFC to communicate with the OPTIGA™ Authenticate NBT device. Below is an overview of how NFC is configured and used in the project.

NFC configuration

Permissions: To allow the app to access and use the NFC interface, the following lines have to be added to the AndroidManifest:

Listing 66 Snippet of AndroidManifest.xml.
<uses-permission android:name="android.permission.NFC" />
<uses-feature android:name="android.hardware.nfc" android:required="true" />

Background dispatch

Background dispatch defines how the mobile phone behaves when an NFC tag is tapped when this app is not in the foreground (for example when the home screen is active). In order to open this application when an NFC tag is tapped, it can be configured to allow background dispatch. This registers the app for a certain type NFC tag content - and the app will be opened when the tag is tapped.

Intent filters: The app listens for NFC events using intent filters, also defined for the respective activity in app/src/AndroidManifest.xml. This is done by adding the following lines to the entry activity SplashScreenActivity of the AndroidManifest:

Listing 67 Snippet of AndroidManifest.xml.
<activity android:name="com.infineon.css.nbt_personalization.SplashScreenActivity">
   <intent-filter>
      <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
      <category android:name="android.intent.category.DEFAULT"/>
      <data android:mimeType="text/plain" />
   </intent-filter>
   ....
</activity>

As seen in the snippet above, the app is configured to listen for ACTION_NDEF_DISCOVERED intents. This intent has the highest priority in the Android tag dispatch system, other intents such as ACTION_TECH_DISCOVERED or ACTION_TAG_DISCOVERED will not be dispatched to the app as long as the app is registered for this intent. See developer.android.com for more details.

Technology filters: Supported NFC technologies are defined in res/xml/nfx_tech_filter.xml:

To filter for ACTION_TECH_DISCOVERED a dedicated XML file needs to be created, specifying the technologies supported by the app.

Listing 68 Snippet of the nfx_tech_filter.xml file.
<tech-list>
      <tech>android.nfc.tech.Ndef</tech>
</tech-list>

If the scanned tag supports all technologies of a tech-list set defined in the nfx_tech_filter.xml file, the app will be opened when the tag is tapped. To register the app for the ACTION_TECH_DISCOVERED intent, the following lines have to be added to the entry activity SplashScreenActivity of the app/src/AndroidManifest.xml file:

Listing 69 Snippet of AndroidManifest.xml.
<activity android:name="com.infineon.css.nbt_personalization.SplashScreenActivity">
   ....
   <intent-filter>
      <action android:name="android.nfc.action.TECH_DISCOVERED"/>
   </intent-filter>
   <meta-data android:name="android.nfc.action.TECH_DISCOVERED" android:resource="@xml/nfx_tech_filter" />
   ....
</activity>

It makes sense to register the ACTION_TECH_DISCOVERED intent for applications intended to interact with NFC tags on custom APDU level (not using the NDEF file). Since this is the case for the ADT application, the app is registered for the ACTION_TECH_DISCOVERED intent as well.

Foreground dispatch

Foreground dispatch is used to ensure that the app receives NFC intents when it is in the foreground. This is configured in the source code of the activities that handle NFC events.

Intent filters: For foreground dispatch, the Intent filter is configured in the onResume() method of each activity.

Listing 70 Snippet of MainActivity.java.
//Setting intent to recognized NFC Type A tags with NFC interface
pendingIntent = PendingIntent.getActivity(this, 0,
         new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), PendingIntent.FLAG_MUTABLE);
intentFiltersArray = null; /
techListsArray =  new String[][] { new String[] { NfcA.class.getName() } };

For more information, refer to the official documentation on developer.android.com.

Enable/Disable: The app uses foreground dispatch to ensure that it receives NFC intents when it is in the foreground. This is done by calling enableForegroundDispatch() in the onResume() method of the activities and disableForegroundDispatch() in the onPause() method.

Listing 71 Snippet of MainActivity.java.
@Override
protected void onResume() {
   super.onResume();
   NfcAdapter.getDefaultAdapter(this).enableForegroundDispatch(this, pendingIntent, intentFiltersArray, techListsArray);
}

@Override
protected void onPause() {
   super.onPause();
   NfcAdapter.getDefaultAdapter(this).disableForegroundDispatch(this);
}

Handling NFC intents: The app handles NFC intents in the onNewIntent() method of the activities. The app retrieves the NFC tag from the intent and uses it to establish a communication channel (ApduChannel) with the OPTIGA™ Authenticate NBT device.

Listing 72 Snippet of MainActivity.java.
@Override
protected void onNewIntent(Intent intent) {
   super.onNewIntent(intent);

   Log.d("NFC-debug", "Intent.getAction() = " + intent.getAction().toString());
   if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(intent.getAction())) {
      Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
      IsoDep com = IsoDep.get(tag);
      IChannel channel = InitializeChannel(com);
      ApduChannel apduChannel = new ApduChannel(channel);

      handleUsecase(apduChannel);
   }
}