android - 使用 Android NFC 检测到 MIFARE Classic 时启动应用程序

标签 android android-intent nfc mifare

我有很多使用 MIFARE 的经验(从 1996 年开始使用 GEMPLUS 制造的卡)。我什至编写了低级代码来模拟 MIFARE 卡……但现在事情变得更简单、更高级别了,我无法让它与 Java、Android 和 Android Studio 一起工作!我觉得随着时间的推移我变得越来越笨了......

我要做的就是在检测到 MIFARE 卡时启动应用程序。我知道这是可以做到的,因为我在我的设备中使用了 NFC 卡信息应用程序,并且它在 MIFARE 卡存在的情况下正确启动。我已经卸载它以确保唯一的 NFC 应用程序是我自己的。我试图遵循在 http://developer.android.com/guide/topics/connectivity/nfc/nfc.html 上找到的不太令人满意的文档和 http://developer.android.com/guide/topics/connectivity/nfc/advanced-nfc.html .

问题是我的应用程序从未启动过。代码非常简单,而且 AFAIK 应该可以正常工作...这是应用程序 list :

    <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.nfctest.app" >

    <uses-sdk android:minSdkVersion="10"/>
    <uses-permission android:name="android.permission.NFC" />
    <uses-feature android:name="android.hardware.nfc" android:required="true" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.nfctest.app.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
            <intent-filter>
                <action android:name="android.nfc.action.TECH_DISCOVERED"/>
            </intent-filter>
            <meta-data android:name="android.nfc.action.TECH_DISCOVERED"
                android:resource="@xml/nfc_tech_filter" />
        </activity>
    </application>
</manifest>

它按 NDEF_DISCOVERED 过滤,因为 TECH_DISCOVERED 不能单独工作。

这里是 nfc_tech_filter 资源文件:

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
    <tech-list>
        <tech>android.nfc.tech.Ndef</tech>
        <tech>android.nfc.tech.NdefFormatable</tech>
        <tech>android.nfc.tech.MifareClassic</tech>
        <tech>android.nfc.tech.MifareUltralight</tech>
    </tech-list>
</resources>

这是 Activity 代码:

package com.example.nfctest.app;

import android.content.Intent;
import android.nfc.NfcAdapter;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;

public class MainActivity extends ActionBarActivity {

    TextView label;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        label = (TextView)findViewById(R.id.label);
    }

    @Override
    public void onResume() {
        super.onResume();
        if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) {
            label.setText("NDEF_DISCOVERED");
        } else if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(getIntent().getAction())) {
            label.setText("TECH_DISCOVERED");
        }
    }

    @Override
    public void onNewIntent(Intent intent) {
        label.setText("onNewIntent!!!");
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
}

Activity 布局非常简单:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    tools:context="com.example.nfctest.app.MainActivity">
    <TextView
        android:id="@+id/label"
        android:text="@string/hello_world"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</RelativeLayout>

开始编写实际 NFC 代码时,我需要做的就是在检测到 TAG 时启动应用程序。文档使它听起来如此简单,以至于我确定是我又一次变得愚蠢了......

更新:

通过将 Activity 代码更改为,我能够使用 foregroundDispatcher 正确读取标签 UID:

package com.example.nfctest.app;

import android.app.PendingIntent;
import android.content.Intent;
import android.content.IntentFilter;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.widget.TextView;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

public class MainActivity extends ActionBarActivity {

    TextView label;
    IntentFilter[] filters;
    String[][] techs;
    PendingIntent pendingIntent;
    NfcAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        label = (TextView)findViewById(R.id.label);
        pendingIntent = PendingIntent.getActivity(
                this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
        IntentFilter mifare = new IntentFilter((NfcAdapter.ACTION_TECH_DISCOVERED));
        filters = new IntentFilter[] { mifare };
        techs = new String[][] { new String[] {  NfcA.class.getName() } };
        adapter = NfcAdapter.getDefaultAdapter(this);
    }

    public void onPause() {
        super.onPause();
        adapter.disableForegroundDispatch(this);
    }

    public void onResume() {
        super.onResume();
        adapter.enableForegroundDispatch(this, pendingIntent, filters, techs);
    }

    public void onNewIntent(Intent intent) {
        Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
        byte[] id = tag.getId();
        ByteBuffer wrapped = ByteBuffer.wrap(id);
        wrapped.order(ByteOrder.LITTLE_ENDIAN);
        int signedInt = wrapped.getInt();
        long number = signedInt & 0xffffffffl;
        label.setText("Tag detected: " + number);
    }
}

我什至不需要在 list 中设置任何 Intent 过滤器就可以工作,只需一个像这样的简单 list 即可:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.nfctest.app" >

    <uses-sdk android:minSdkVersion="10"/>
    <uses-permission android:name="android.permission.NFC" />
    <uses-feature android:name="android.hardware.nfc" android:required="true" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.nfctest.app.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

使用此代码,当我的应用程序在前台时,我可以读取 MIFARE 卡序列号,但我仍然无法进行设置,以便 ANDROID 在后台启动我的 Activity...

我仍然不知道如何让 ANDROID 在检测到标签(MIFARE 或 NOT)时启动我的应用程序/Activity 。

最佳答案

您的问题是技术过滤器 XML 文件(您自己终于正确地发现了这一点)。您最初使用的技术过滤器,

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
    <tech-list>
        <tech>android.nfc.tech.Ndef</tech>
        <tech>android.nfc.tech.NdefFormatable</tech>
        <tech>android.nfc.tech.MifareClassic</tech>
        <tech>android.nfc.tech.MifareUltralight</tech>
    </tech-list>
</resources>

没有任何意义。一个 <tech> 条目中的 <tech-filter> 条目使用逻辑与组合。

所以你需要有一个标签是 Ndef and NdefFormatable and MifareClassic and MifareUltralight 。这是不可能的,原因有二:

  1. NdefNdefFormatable 是互斥的。一个标签可以或者已经包含 NDEF 数据/一个空的 NDEF 消息(-> Ndef)或者它可能已经准备好用 NDEF 消息格式化(-> NdefFormatable)。
  2. MifareClassicMifareUltralight 是互斥的。标签可以是 MIFARE Classic 标签 MIFARE Ultralight 标签,但不能同时是两者。

因此,例如,在包含 NDEF 消息并且是 MIFARE Classic 或 MIFARE Ultralight 的标签上触发的适当技术过滤器将如下所示:

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
    <tech-list>
        <tech>android.nfc.tech.Ndef</tech>
        <tech>android.nfc.tech.MifareClassic</tech>
    </tech-list>
    <tech-list>
        <tech>android.nfc.tech.Ndef</tech>
        <tech>android.nfc.tech.MifareUltralight</tech>
    </tech-list>
</resources>

所有 <tech-list> 条目都用逻辑或组合,然后:(Ndef MifareClassic ) Ndef MifareUltralight)。

在您的情况下,您似乎对 NDEF 不太感兴趣,但对从 MIFARE 卡中获取任何东西更感兴趣。假设 MIFARE 你指的是 MIFARE Classic 卡(因为你在谈论 1996 ;-)),你的技术过滤器应该是这样的:

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
    <tech-list>
        <tech>android.nfc.tech.MifareClassic</tech>
    </tech-list>
</resources>

但请注意,这仅适用于带有 NXP 芯片组的设备(MIFARE Classic 是专有的 NXP 技术。虽然 NXP 将卡端许可给其他制造商,但他们不许可读卡器端。因此,仅1 NXP 的读卡器产品读取 MIFARE Classic。)。在带有 Broadcom 芯片组的设备上,MIFARE Classic 卡2 不会被检测为 MIFARE Classic。但是由于 MIFARE Classic 使用标准的 ISO 14443-3 Type A 防冲突和激活序列,这些设备通常3检测到诸如 NfcA 的卡:

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
    <tech-list>
        <tech>android.nfc.tech.NfcA</tech>
    </tech-list>
</resources>

请注意,使用带有 MifareClassic 标签技术的第二个条目是多余的,因为每个检测到 MIFARE Classic 的设备都会将其检测为 MifareClassic NfcA


1) 该规则也有异常(exception)情况,因为一些制造商不同意 NXP 的立场(或者干脆忽略它?),即在读取器端实现 MIFARE Classic 支持侵犯了他们的权利。

2) 并且只有 MIFARE Classic。这不适用于符合标准的 MIFARE 产品,如 Ultralight、DESFire、NTAG 等。

3) 一些带有 Broadcom NFC 芯片组的三星设备,如 S4,是该规则的异常(exception)。在这些设备上,三星决定完全禁止 MIFARE Classic,而是显示“不支持标签”错误。正如他们声称的那样,为了改善用户体验,否则用户将无法理解为什么他们不能将数据写入这些标签。或者按照的解释,让您的用户讨厌作为应用程序开发人员,因为无法让您的应用程序在他们的手机上运行他们的标签。查看 ReTag app 或我的 own app 上的负面评论。

关于android - 使用 Android NFC 检测到 MIFARE Classic 时启动应用程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23530457/

相关文章:

java - 检查 AsyncTask 上的连接

java - 模拟器尝试使用句柄0 android studio删除不存在的颜色缓冲区

当我开始一个 Intent 时 Android 应用程序崩溃

java - ACR122u直接通讯无 react

android - 在连接的任何(空)NFC 上启动应用程序

android - 无法在 Web View 中播放视频

Java mac sha256 哈希与使用 pack 的 php hmac sha256 不匹配?

android - 将 MvxDialogFragment 与 ViewModel 绑定(bind)

java - 在具有 Intent 的 Activity 之间传递文本

android - NFC 设备作为另一个 Android HCE 的阅读器