okadan / flutter-nfc-manager

A Flutter plugin for accessing the NFC features on Android and iOS.
https://pub.dev/packages/nfc_manager
MIT License
190 stars 125 forks source link

Cannot detect an NfcV tag while other Android apps can #182

Closed nicolascrochet7 closed 4 months ago

nicolascrochet7 commented 4 months ago

Hi,

I am getting a weird behavior with this library.

I am using a tag that is configurable em4425 tag.

Before being configured I can easily detect and scan the tag and interact with it. Then I activate a feature on the tag and I am not getting any response from the app nor in the log ; while I can with any other NFC tool existing on Android and the tag still appears as a Iso15693 tag.

I would like to dig into what is going on, but I am not sure where I should start. How could I access deeper logs from the library to see what is making the library not detect the tag in the first place ?

Any advice appreciated :)

/// ISO 15693 NFC client.
class Iso15693NfcClient implements Iso15693NfcClientInterface {
  Completer _completer = Completer();

  Iso15693TagInstance? _iso15693tagInstance;

  @override
  Future<bool> get isNfcAvailable async =>
      await NfcManager.instance.isAvailable();

  @override
  Future<Iso15693NfcUid> get uid async => await poll(() async {});

  @override
  Future<Iso15693NfcUid> poll(sequence, {Iso15693NfcUid? uid}) async {
    // Check platform
    if (env.platform != Platform.android && env.platform != Platform.iOS) {
      throw const NfcUnavailableReaderException(
          message: 'Platform has to be mobile to poll');
    }
    // Check Nfc availability
    if (await isNfcAvailable == false) {
      throw const NfcUnavailableReaderException(
          message: 'Nfc reader unavailable, check permissions');
    }
    // Init completer session.
    _completer = Completer();
    // Listen for an ISO 15693 NFC tag.
    await NfcManager.instance.startSession(
        pollingOptions: {NfcPollingOption.iso15693},
        onDiscovered: (tag) async {
          log('NFC')
              .fine("NFC tag discovered : ${tag.data.values.toString()}");
          // Check tag validity
          _iso15693tagInstance = Iso15693TagInstance(tag: tag);
          final discoveredUid =
              Iso15693NfcUid.fromUint8List(_iso15693tagInstance!.uid);
          // Compare to uid
          if (uid != null && discoveredUid != uid) {
            throw NfcInvalidUidException(
                message:
                    'Tag discovered ${discoveredUid.uid.toHexString()} is not the exptected one ${uid.uid.toHexString()}');
          }
          // Init user sequence
          await sequence()
              .then((value) => _completer.complete(discoveredUid))
              .catchError((error) async {
            _completer.completeError(error);
            throw NfcCommandFailureException(
                message: 'Command failure with error $error');
          });
        },
        onError: _onError);

    // Return its uid.
    return await _completer.future;
  }

  Future<void> _onError(NfcError error) async {
    log('NFC').severe('NFC error on session : ${error.message}');
    _completer.completeError(error);
  }

  @override
  Future<Iso15693NfcResponse> transceiveCommand(
      Iso15693NfcCommand command) async {
            log('NFC').fine("${command.commandName} : ${command.fullBytes.toHexString()}");
    if (_iso15693tagInstance != null) {
      final fullBytes = await _iso15693tagInstance!.transceiveCommand(command);
      log('NFC').fine("Response : ${fullBytes.toHexString()}");
      return Iso15693NfcResponse(fullBytes: fullBytes);
    } else {
      throw const NfcCommandFailureException(message: 'No tag discovered');
    }
  }

  Future<void> stopPolling() async {
    log('NFC').fine("NFC session stoped");
    await NfcManager.instance.stopSession();
    _iso15693tagInstance = null;
  }
}

/// Generic NFC Manager library tag instance to transceive command
/// the same way between Android and iOS.
class Iso15693TagInstance {
  final NfcTag tag;

  // Provision android or iOS tag instance.
  Iso15693TagInstance({required this.tag}) {
    androidTag = NfcV.from(tag);
    iOStag = Iso15693.from(tag);
    if (androidTag == null && iOStag == null) {
      throw const NfcInvalidUidException(
          message: 'Discovered tag is not an ISO15693 tag.');
    }
  }

  late final NfcV? androidTag;

  late final Iso15693? iOStag;

  /// Transceives command to Android or iOS tag instance depending on platform.
  Future<Uint8List> transceiveCommand(Iso15693NfcCommand command) async {
    if (env.platform == Platform.android) {
      return await androidTag!.transceive(data: command.fullBytes);
    } else if (env.platform == Platform.iOS) {
      return await iOStag!.customCommand(
          requestFlags: command.flags.toIso15693RequestFlags(),
          customCommandCode: command.commandOpcode[0],
          customRequestParameters: command.parameters);
    } else {
      throw const NfcUnavailableReaderException(
          message:
              'NFC is unavailable for platform other than Android and iOS');
    }
  }

  /// Returns uid depending on Android and iOS.
  ///
  /// Android returns uid in LSB-MSB while iOS MSB-LSB.
  Uint8List get uid {
    if (env.platform == Platform.android) {
      return androidTag!.identifier.toReversed();
    } else if (env.platform == Platform.iOS) {
      return iOStag!.identifier;
    } else {
      throw const NfcUnavailableReaderException(
          message:
              'NFC is unavailable for platform other than Android and iOS');
    }
  }
}
nicolascrochet7 commented 4 months ago

Looking at my code, I see that I thought I had removed the polling option but in fact not. It works without it. Which is still weird.

IgorBialek commented 1 month ago

@nicolascrochet7 Could you please suggest a convenient way to contact you regarding EM4425 tags? Thank you!