Monday, March 10, 2025

Getting Started with NFC on Android

OK, enough of trying to understand how the reader works and the APDU methods. Instead, I want to look at the Android end of things and see what, if anything, we can get running on the phone to try and talk to either cards or the reader.

Here is another medium blog post: this one is about using NFC Reader Mode on Android, which is something that, as the article suggests, does not seem to be well documented in Android.

The source code for this app can be downloaded from github.

Building on Android

It says it has a pre-compiled version in sampleapp, but as far as I can see that is not true. But it does have a gradlew to build it, so let's try that:
$ ./gradlew build
...
   > Android Gradle plugin requires Java 17 to run. You are currently using Java 11.
...
OK, fair enough. Let's install OpenJDK 17:
$ apt install openjdk-17-jdk
...
The following additional packages will be installed:
  libatk-wrapper-java libatk-wrapper-java-jni openjdk-17-jdk-headless openjdk-17-jre openjdk-17-jre-headless
Suggested packages:
  openjdk-17-demo openjdk-17-source visualvm fonts-ipafont-mincho
The following NEW packages will be installed
  libatk-wrapper-java libatk-wrapper-java-jni openjdk-17-jdk openjdk-17-jdk-headless openjdk-17-jre openjdk-17-jre-headless
0 to upgrade, 6 to newly install, 0 to remove and 30 not to upgrade.
OK, try again:
$ ./gradlew build
...
> SDK location not found. Define a valid SDK location with an ANDROID_HOME environment variable or by setting the sdk.dir path in your project's local properties file at '/home/gareth/acs1552u/AndroidBasicNfcReader/local.properties'.
...
Yeah, OK, I don't remember doing anything with Android on this computer, so I need to go through all of that. Bear with me if you are already familiar with all this:

Installing Android Studio

So, basically I'm going to follow these instructions.

Bizarrely, the first instruction is to unpack a .tar.gz file. I had to scroll back up to the top of the page to find the link and download it.

Then it says I need to install some 32-bit libraries as I am on a 64-bit system.
$ sudo apt-get install libc6:i386 libncurses5:i386 libstdc++6:i386 lib32z1 libbz2-1.0:i386
...
The following additional packages will be installed:
  gcc-12-base:i386 libc6-i386 libcom-err2:i386 libcrypt1:i386 libgcc-s1:i386 libgpm2:i386 libgssapi-krb5-2:i386 libidn2-0:i386 libk5crypto3:i386 libkeyutils1:i386 libkrb5-3:i386 libkrb5support0:i386
  libnsl2:i386 libnss-nis:i386 libnss-nisplus:i386 libssl3:i386 libtinfo5:i386 libtirpc3:i386 libunistring2:i386
Suggested packages:
  glibc-doc:i386 locales:i386 gpm:i386 krb5-doc:i386 krb5-user:i386
The following NEW packages will be installed
  gcc-12-base:i386 lib32z1 libbz2-1.0:i386 libc6:i386 libc6-i386 libcom-err2:i386 libcrypt1:i386 libgcc-s1:i386 libgpm2:i386 libgssapi-krb5-2:i386 libidn2-0:i386 libk5crypto3:i386 libkeyutils1:i386
  libkrb5-3:i386 libkrb5support0:i386 libncurses5:i386 libnsl2:i386 libnss-nis:i386 libnss-nisplus:i386 libssl3:i386 libstdc++6:i386 libtinfo5:i386 libtirpc3:i386 libunistring2:i386
0 to upgrade, 24 to newly install, 0 to remove and 30 not to upgrade.
Need to get 10.6 MB of archives.
After this operation, 40.9 MB of additional disk space will be used.
...
And then I can finally unpack the tarball:
$ cd /usr/local
$ sudo tar xfz ~/Downloads/android-studio-2024.2.2.13-linux.tar.gz
And run studio:
$ cd android-studio/bin
$ ./studio
...
A lot of output belches forth from this command, but ultimately a window opens and welcomes me "back" to Android. OK, apparently it can find the Android SDK (~/Android/Sdk apparently), even if the gradle build couldn't. It said it would give me the option to install a newer SDK, but that didn't happen, and I rattled along to the end very quickly. This may come back and bite me.

Back to Gradle

Well, if I have done nothing else there, I have "discovered" my Android SDK, so let's go back to gradle and tell it where that is and see if it can make it any further.
ANDROID_HOME=$HOME/Android/Sdk JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64/ ./gradlew build
...
> Task :app:lintReportDebug
Wrote HTML report to file:///home/gareth/acs1552u/AndroidBasicNfcReader/app/build/reports/lint-results-debug.html


BUILD SUCCESSFUL in 2m 7s
90 actionable tasks: 90 executed
OK, that looks promising. Do I have an apk or equivalent?
$ find . -name '*.apk'
./app/build/outputs/apk/debug/app-debug.apk
./app/build/outputs/apk/release/app-release-unsigned.apk
$ file ./app/build/outputs/apk/debug/app-debug.apk
./app/build/outputs/apk/debug/app-debug.apk: Zip archive data, at least v0.0 to extract, compression method=deflate
$ ls -l ./app/build/outputs/apk/debug/app-debug.apk
-rw-rw-r-- 1 gareth gareth 5953392 Jan 14 16:42 ./app/build/outputs/apk/debug/app-debug.apk
OK, very good.

Installing on my Phone

Let's try installing that onto my phone (I really don't think that anything NFC related can be reliably tested in the emulator).

So, let's plug the phone in and check that everything works:
$ adb devices
List of devices attached
OK, no it doesn't. I'm going to say "this happens to me all the time", but one of the things is that I do this relatively rarely, so it doesn't happen "all the time" and then, the next time it does happen, I've forgotten both why it happens and how to fix it. Let me wander off and Google a few things.

Oh, yes, right. You can't have your "everyday" phone in "developer mode" all the time because there are a handful of apps (for me, all banking apps) that refuse to start with "USB debugging" turned on. So I turn it off, but that is what you need in order to be able to use adb. So I have just turned on "USB Debugging" and "Stay awake" for now. Hopefully I will remember this after I have turned them off and want to come back to this tomorrow :-)
$ adb install app/build/outputs/apk/debug/app-debug.apk
Performing Streamed Install
Success

Testing out what I can see

OK, now can I find that on the phone? Yes, it's where I would expect it to be and called AndroidBasicNfcReader. When I start it up, it says "Please tap a tag to the reader". Let's start with my door key:



The first thing that I notice and find surprising is that it says it has an entry NdefFormatable but then goes on to say "NOT supporting the NDEF class".

What happens when I tap it onto my USB reader? Uh, nothing. Huh? I suppose this has something to do with the fact that it is a reader and not a card. What if I run up show-atr and then tap it?

Hmmm ... still nothing. If I close the app, it beeps in the normal way. OK, what if I try the apdu card emulation that I tried at the end of last episode that failed?

It just says "please insert smart card" as if I wasn't in contact with the reader.

What I can't do, of course, is to tap the phone to itself, but I can use another Android phone with NFC enabled to do that.



So that works at least and gives a reasonable set of answers, but it's interesting that it still isn't supporting the NDEF class.

Looking into the code, it seems the most important thing it does is call enableReaderMode:
   protected void onResume() {
        super.onResume();
        if (myNfcAdapter != null) {
            if (!myNfcAdapter.isEnabled())
                showWirelessSettings();
            Bundle options = new Bundle();
            // Work around for some broken Nfc firmware implementations that poll the card too fast
            options.putInt(NfcAdapter.EXTRA_READER_PRESENCE_CHECK_DELAY, 250);
            // Enable ReaderMode for all types of card and disable platform sounds
            // The option NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK is NOT set
            // to get the data of the tag after reading
            myNfcAdapter.enableReaderMode(this,
                    this,
                    NfcAdapter.FLAG_READER_NFC_A |
                    NfcAdapter.FLAG_READER_NFC_B |
                    NfcAdapter.FLAG_READER_NFC_F |
                    NfcAdapter.FLAG_READER_NFC_V |
                    NfcAdapter.FLAG_READER_NFC_BARCODE |
                    NfcAdapter.FLAG_READER_NO_PLATFORM_SOUNDS,
                    options);
        }
    }
This is described in the manual page for NfcAdapter and makes it clear that doing this will turn off peer-peer ("Android Beam") mode. So maybe that's something I should be looking for.

Conclusion

It is possible to read "tags" at least from the phone by putting the phone in "reader" mode. But this still doesn't seem to get us where we want to be, which is having a complex conversation between computer and phone over NFC. I suspect there is still something I'm missing. Let's keep looking.

No comments:

Post a Comment