通过伪造NFC标签扫描来测试应用 [英] Testing an app by faking NFC tag scan

查看:127
本文介绍了通过伪造NFC标签扫描来测试应用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是Android的新手,致力于近距离通信,可从NFC标签读取数据.我既没有NFC支持的Android移动设备,也没有NFC标签来测试我创建的应用程序.

我找到了以下两篇文章,内容涉及通过触发Intent伪造NFC标签扫描.

  1. 启动伪造NFC(近距离通信)的可能性

  2. 需要伪造NFC标签在Android中被扫描

我根据第一篇文章更改了我的代码,在其中单击按钮时,我在第一个活动中触发了所需的Intent.而我又创建了一个能够处理相同意图的活动. NFC标签和处理数据的读取基于第二个活动上的按钮单击.

问题是:每当我从第一个活动中触发虚假的NFC标签扫描意图时,都会引发错误"未找到任何活动来处理意图{act = android.nfc.action.NDEF_DISCOVERED(有附加功能)} ".

清单文件是这样的:

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

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.NFC"/>

<uses-feature android:name="android.hardware.nfc" android:required="false" />

<application
    android:allowBackup="true"
    android:icon="@mipmap/icon1"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity
        android:name=".MainActivity"
        android:label="@string/app_name"
        android:theme="@style/AppTheme.NoActionBar">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <activity
        android:name=".Set_Monthly_Target"
        android:label="@string/title_activity_set__monthly__target"
        android:parentActivityName=".MainActivity">
        <meta-data
            android:name="android.support.PARENT_ACTIVITY"
            android:value="com.expensemanager.saubhattacharya.MainActivity" />
        <intent-filter>
            <action android:name="android.nfc.action.NDEF_DISCOVERED" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:mimeType="text/plain"/>
            <data android:mimeType="image/*" />
        </intent-filter>
    </activity>
    <activity
        android:name=".Add_Daily_Expense"
        android:label="@string/add_daily_expense_activity"
        android:parentActivityName=".MainActivity">
        <meta-data
            android:name="android.support.PARENT_ACTIVITY"
            android:value="com.expensemanager.saubhattacharya.MainActivity" />
        <intent-filter>
            <action android:name="android.nfc.action.NDEF_DISCOVERED" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:mimeType="text/plain"/>
            <data android:mimeType="image/*" />
        </intent-filter>
    </activity>
</application>

</manifest>

第一个活动的意图触发代码段如下:

public void scan_tag (View view)
{
    final Intent intent = new Intent(NfcAdapter.ACTION_NDEF_DISCOVERED);
    intent.putExtra(NfcAdapter.EXTRA_NDEF_MESSAGES, "Custom Messages");
    startActivity(intent);
}

处理上述触发器的第二个活动的代码段如下:

public class Add_Daily_Expense extends AppCompatActivity {

Button read_data;
TextView show_data;
Tag detected_tag;
NfcAdapter nfcAdapter;
IntentFilter[] intentFilters;
public static final String MIME_TEXT_PLAIN = "text/plain";
public static final String MIME_IMAGE_ALL = "image/*";

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_add__daily__expense);
    final PackageManager pm = this.getPackageManager();
    show_data = (TextView) findViewById(R.id.show_data);
    nfcAdapter = NfcAdapter.getDefaultAdapter(getApplicationContext());
    read_data = (Button) findViewById(R.id.read_nfc);
    read_data.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC)) {
                try {
                    AlertDialog.Builder builder = new AlertDialog.Builder(Add_Daily_Expense.this);
                    builder.setMessage("NFC feature is not available on this device!")
                            .setCancelable(false)
                            .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                                public void onClick(DialogInterface dialog, int id) {
                                    dialog.cancel();
                                }
                            });
                    AlertDialog alert = builder.create();
                    alert.show();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else {
                Toast.makeText(getApplicationContext(), "NFC feature is available on this device!", Toast.LENGTH_SHORT).show();
                HandleIntent(getIntent());
            }
        }
    });
}

public void HandleIntent(Intent intent)
{
    String action = intent.getAction();
    if(NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action))
    {
        detected_tag = getIntent().getParcelableExtra(nfcAdapter.EXTRA_TAG);
        NDefReaderTask NDefReader = new NDefReaderTask();
        NDefReader.execute();
    }
}

public void onResume()
{
    super.onResume();
    if(nfcAdapter != null)
    setupForeGroundDispatch(this, nfcAdapter);
}

public void onPause()
{
    super.onPause();
    if(nfcAdapter != null)
    stopForeGroundDispatch(this, nfcAdapter);
}

public void onNewIntent(Intent intent)
{
    HandleIntent(intent);
}

public void setupForeGroundDispatch (final Activity activity, NfcAdapter nfcAdapter)
{
    Intent new_intent = new Intent(getApplicationContext(),Add_Daily_Expense.class);
    new_intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);

    PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(),0,new_intent,0);
    intentFilters = new IntentFilter[1];
    String[][] techList = new String[][]{};

    intentFilters[0] = new IntentFilter();
    intentFilters[0].addAction(NfcAdapter.ACTION_NDEF_DISCOVERED);
    intentFilters[0].addCategory(Intent.CATEGORY_DEFAULT);
    try {
        intentFilters[0].addDataType(MIME_TEXT_PLAIN);
        intentFilters[0].addDataType(MIME_IMAGE_ALL);
    }
    catch(IntentFilter.MalformedMimeTypeException me)
    {
        me.printStackTrace();
    }

    nfcAdapter.enableForegroundDispatch(activity, pendingIntent, intentFilters, techList);
}

public void stopForeGroundDispatch (final Activity activity, NfcAdapter nfcAdapter)
{
    nfcAdapter.disableForegroundDispatch(activity);
}

public class NDefReaderTask extends AsyncTask <Tag, Void, String>
{
    @Override
    protected String doInBackground(Tag... params)
    {
        try
        {
            detected_tag = params[0];
            Ndef ndef = Ndef.get(detected_tag);
            ndef.connect();
            if(ndef != null)
            {
                NdefMessage ndefMessage = ndef.getCachedNdefMessage();
                NdefRecord[] records = ndefMessage.getRecords();
                for(NdefRecord ndefRecord : records)
                {
                    if((ndefRecord.getTnf() == NdefRecord.TNF_ABSOLUTE_URI) || (ndefRecord.getTnf() == NdefRecord.TNF_WELL_KNOWN))
                    {
                        byte[] payload = ndefRecord.getPayload();
                        String encoding1 = "UTF-8";
                        String encoding2 = "UTF-16";
                        String textEncoding = ((payload[0] & 128) == 0) ? encoding1 : encoding2;
                        int languageCodeLength = payload[0] & 0063;
                        return new String(payload, languageCodeLength + 1, payload.length - languageCodeLength - 1, textEncoding);
                    }
                }
            }
            ndef.close();
        }
        catch (UnsupportedEncodingException UE)
        {
            UE.printStackTrace();
        }
        catch (IOException IE)
        {
            IE.printStackTrace();
        }
        return null;
    }

    @Override
    protected void onPreExecute()
    {

    }

    protected void onPostExecute(String result)
    {
        if(result != null)
        {
            show_data.setText(result);
        }
    }
}
}

我的问题是:我在这里做什么错?还有其他方法可以通过伪造NFC标签扫描来测试我的应用程序吗?

解决方案

您为NDEF_DISCOVERED意向过滤器指定MIME类型过滤器:

<data android:mimeType="text/plain"/>
<data android:mimeType="image/*" />

因此,伪造的NFC意图需要包含这些MIME类型之一以匹配意图过滤器.您可以使用setType()方法将类型信息添加到意图中:

public void scan_tag (View view) {
    final Intent intent = new Intent(NfcAdapter.ACTION_NDEF_DISCOVERED);
    intent.setType("text/plain");
    intent.putExtra(NfcAdapter.EXTRA_NDEF_MESSAGES, ...);
    startActivity(intent);
}

还要注意,上面的代码不会将标签句柄添加到NFC意图中.因此,您无法通过

获得Tag对象

detected_tag = getIntent().getParcelableExtra(nfcAdapter.EXTRA_TAG);

因此,您也无法使用

public class Add_Daily_Expense extends AppCompatActivity {

Button read_data;
TextView show_data;
Tag detected_tag;
NfcAdapter nfcAdapter;
IntentFilter[] intentFilters;
public static final String MIME_TEXT_PLAIN = "text/plain";
public static final String MIME_IMAGE_ALL = "image/*";

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_add__daily__expense);
    final PackageManager pm = this.getPackageManager();
    show_data = (TextView) findViewById(R.id.show_data);
    nfcAdapter = NfcAdapter.getDefaultAdapter(getApplicationContext());
    read_data = (Button) findViewById(R.id.read_nfc);
    read_data.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC)) {
                try {
                    AlertDialog.Builder builder = new AlertDialog.Builder(Add_Daily_Expense.this);
                    builder.setMessage("NFC feature is not available on this device!")
                            .setCancelable(false)
                            .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                                public void onClick(DialogInterface dialog, int id) {
                                    dialog.cancel();
                                }
                            });
                    AlertDialog alert = builder.create();
                    alert.show();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else {
                Toast.makeText(getApplicationContext(), "NFC feature is available on this device!", Toast.LENGTH_SHORT).show();
                HandleIntent(getIntent());
            }
        }
    });
}

public void HandleIntent(Intent intent)
{
    String action = intent.getAction();
    if(NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action))
    {
        detected_tag = getIntent().getParcelableExtra(nfcAdapter.EXTRA_TAG);
        NDefReaderTask NDefReader = new NDefReaderTask();
        NDefReader.execute();
    }
}

public void onResume()
{
    super.onResume();
    if(nfcAdapter != null)
    setupForeGroundDispatch(this, nfcAdapter);
}

public void onPause()
{
    super.onPause();
    if(nfcAdapter != null)
    stopForeGroundDispatch(this, nfcAdapter);
}

public void onNewIntent(Intent intent)
{
    HandleIntent(intent);
}

public void setupForeGroundDispatch (final Activity activity, NfcAdapter nfcAdapter)
{
    Intent new_intent = new Intent(getApplicationContext(),Add_Daily_Expense.class);
    new_intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);

    PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(),0,new_intent,0);
    intentFilters = new IntentFilter[1];
    String[][] techList = new String[][]{};

    intentFilters[0] = new IntentFilter();
    intentFilters[0].addAction(NfcAdapter.ACTION_NDEF_DISCOVERED);
    intentFilters[0].addCategory(Intent.CATEGORY_DEFAULT);
    try {
        intentFilters[0].addDataType(MIME_TEXT_PLAIN);
        intentFilters[0].addDataType(MIME_IMAGE_ALL);
    }
    catch(IntentFilter.MalformedMimeTypeException me)
    {
        me.printStackTrace();
    }

    nfcAdapter.enableForegroundDispatch(activity, pendingIntent, intentFilters, techList);
}

public void stopForeGroundDispatch (final Activity activity, NfcAdapter nfcAdapter)
{
    nfcAdapter.disableForegroundDispatch(activity);
}

public class NDefReaderTask extends AsyncTask <Tag, Void, String>
{
    @Override
    protected String doInBackground(Tag... params)
    {
        try
        {
            detected_tag = params[0];
            Ndef ndef = Ndef.get(detected_tag);
            ndef.connect();
            if(ndef != null)
            {
                NdefMessage ndefMessage = ndef.getCachedNdefMessage();
                NdefRecord[] records = ndefMessage.getRecords();
                for(NdefRecord ndefRecord : records)
                {
                    if((ndefRecord.getTnf() == NdefRecord.TNF_ABSOLUTE_URI) || (ndefRecord.getTnf() == NdefRecord.TNF_WELL_KNOWN))
                    {
                        byte[] payload = ndefRecord.getPayload();
                        String encoding1 = "UTF-8";
                        String encoding2 = "UTF-16";
                        String textEncoding = ((payload[0] & 128) == 0) ? encoding1 : encoding2;
                        int languageCodeLength = payload[0] & 0063;
                        return new String(payload, languageCodeLength + 1, payload.length - languageCodeLength - 1, textEncoding);
                    }
                }
            }
            ndef.close();
        }
        catch (UnsupportedEncodingException UE)
        {
            UE.printStackTrace();
        }
        catch (IOException IE)
        {
            IE.printStackTrace();
        }
        return null;
    }

    @Override
    protected void onPreExecute()
    {

    }

    protected void onPostExecute(String result)
    {
        if(result != null)
        {
            show_data.setText(result);
        }
    }
}
}

获得Ndef连接类的实例.

Ndef ndef = Ndef.get(detected_tag);

您可能要研究以下与模拟标签对象有关的问题/答案:

最后,请注意您的代码中还有其他几个问题.

I am new to Android, working on Near Field Communication for reading data from NFC tags. Neither I have NFC supported Android mobile nor NFC tags to test the application I created.

I found the below two posts which says about faking NFC tag scans by triggering an Intent.

  1. Possibility for Fake NFC(Near Field Communication) Launch

  2. Need To Fake an NFC Tag Being Scanned In Android

I changed my code according to the first post, where on click of a button I am triggering the required Intent in the 1st activity. Whereas I have created one more activity capable of handling that same intent. The reading of NFC tag and handling data is based on a button click on the 2nd activity.

The problem is: Whenever I am triggering the fake NFC tag scan intent from the 1st activity, it is throwing an error "No Activity found to handle Intent { act=android.nfc.action.NDEF_DISCOVERED (has extras) }".

The Manifest file goes like this:

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

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.NFC"/>

<uses-feature android:name="android.hardware.nfc" android:required="false" />

<application
    android:allowBackup="true"
    android:icon="@mipmap/icon1"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity
        android:name=".MainActivity"
        android:label="@string/app_name"
        android:theme="@style/AppTheme.NoActionBar">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <activity
        android:name=".Set_Monthly_Target"
        android:label="@string/title_activity_set__monthly__target"
        android:parentActivityName=".MainActivity">
        <meta-data
            android:name="android.support.PARENT_ACTIVITY"
            android:value="com.expensemanager.saubhattacharya.MainActivity" />
        <intent-filter>
            <action android:name="android.nfc.action.NDEF_DISCOVERED" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:mimeType="text/plain"/>
            <data android:mimeType="image/*" />
        </intent-filter>
    </activity>
    <activity
        android:name=".Add_Daily_Expense"
        android:label="@string/add_daily_expense_activity"
        android:parentActivityName=".MainActivity">
        <meta-data
            android:name="android.support.PARENT_ACTIVITY"
            android:value="com.expensemanager.saubhattacharya.MainActivity" />
        <intent-filter>
            <action android:name="android.nfc.action.NDEF_DISCOVERED" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:mimeType="text/plain"/>
            <data android:mimeType="image/*" />
        </intent-filter>
    </activity>
</application>

</manifest>

The intent trigger code snippet from the 1st activity is below:

public void scan_tag (View view)
{
    final Intent intent = new Intent(NfcAdapter.ACTION_NDEF_DISCOVERED);
    intent.putExtra(NfcAdapter.EXTRA_NDEF_MESSAGES, "Custom Messages");
    startActivity(intent);
}

The code snippet from the 2nd activity, which handles the above trigger is below:

public class Add_Daily_Expense extends AppCompatActivity {

Button read_data;
TextView show_data;
Tag detected_tag;
NfcAdapter nfcAdapter;
IntentFilter[] intentFilters;
public static final String MIME_TEXT_PLAIN = "text/plain";
public static final String MIME_IMAGE_ALL = "image/*";

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_add__daily__expense);
    final PackageManager pm = this.getPackageManager();
    show_data = (TextView) findViewById(R.id.show_data);
    nfcAdapter = NfcAdapter.getDefaultAdapter(getApplicationContext());
    read_data = (Button) findViewById(R.id.read_nfc);
    read_data.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC)) {
                try {
                    AlertDialog.Builder builder = new AlertDialog.Builder(Add_Daily_Expense.this);
                    builder.setMessage("NFC feature is not available on this device!")
                            .setCancelable(false)
                            .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                                public void onClick(DialogInterface dialog, int id) {
                                    dialog.cancel();
                                }
                            });
                    AlertDialog alert = builder.create();
                    alert.show();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else {
                Toast.makeText(getApplicationContext(), "NFC feature is available on this device!", Toast.LENGTH_SHORT).show();
                HandleIntent(getIntent());
            }
        }
    });
}

public void HandleIntent(Intent intent)
{
    String action = intent.getAction();
    if(NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action))
    {
        detected_tag = getIntent().getParcelableExtra(nfcAdapter.EXTRA_TAG);
        NDefReaderTask NDefReader = new NDefReaderTask();
        NDefReader.execute();
    }
}

public void onResume()
{
    super.onResume();
    if(nfcAdapter != null)
    setupForeGroundDispatch(this, nfcAdapter);
}

public void onPause()
{
    super.onPause();
    if(nfcAdapter != null)
    stopForeGroundDispatch(this, nfcAdapter);
}

public void onNewIntent(Intent intent)
{
    HandleIntent(intent);
}

public void setupForeGroundDispatch (final Activity activity, NfcAdapter nfcAdapter)
{
    Intent new_intent = new Intent(getApplicationContext(),Add_Daily_Expense.class);
    new_intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);

    PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(),0,new_intent,0);
    intentFilters = new IntentFilter[1];
    String[][] techList = new String[][]{};

    intentFilters[0] = new IntentFilter();
    intentFilters[0].addAction(NfcAdapter.ACTION_NDEF_DISCOVERED);
    intentFilters[0].addCategory(Intent.CATEGORY_DEFAULT);
    try {
        intentFilters[0].addDataType(MIME_TEXT_PLAIN);
        intentFilters[0].addDataType(MIME_IMAGE_ALL);
    }
    catch(IntentFilter.MalformedMimeTypeException me)
    {
        me.printStackTrace();
    }

    nfcAdapter.enableForegroundDispatch(activity, pendingIntent, intentFilters, techList);
}

public void stopForeGroundDispatch (final Activity activity, NfcAdapter nfcAdapter)
{
    nfcAdapter.disableForegroundDispatch(activity);
}

public class NDefReaderTask extends AsyncTask <Tag, Void, String>
{
    @Override
    protected String doInBackground(Tag... params)
    {
        try
        {
            detected_tag = params[0];
            Ndef ndef = Ndef.get(detected_tag);
            ndef.connect();
            if(ndef != null)
            {
                NdefMessage ndefMessage = ndef.getCachedNdefMessage();
                NdefRecord[] records = ndefMessage.getRecords();
                for(NdefRecord ndefRecord : records)
                {
                    if((ndefRecord.getTnf() == NdefRecord.TNF_ABSOLUTE_URI) || (ndefRecord.getTnf() == NdefRecord.TNF_WELL_KNOWN))
                    {
                        byte[] payload = ndefRecord.getPayload();
                        String encoding1 = "UTF-8";
                        String encoding2 = "UTF-16";
                        String textEncoding = ((payload[0] & 128) == 0) ? encoding1 : encoding2;
                        int languageCodeLength = payload[0] & 0063;
                        return new String(payload, languageCodeLength + 1, payload.length - languageCodeLength - 1, textEncoding);
                    }
                }
            }
            ndef.close();
        }
        catch (UnsupportedEncodingException UE)
        {
            UE.printStackTrace();
        }
        catch (IOException IE)
        {
            IE.printStackTrace();
        }
        return null;
    }

    @Override
    protected void onPreExecute()
    {

    }

    protected void onPostExecute(String result)
    {
        if(result != null)
        {
            show_data.setText(result);
        }
    }
}
}

My question is: What is wrong I am doing here? Is there any other way to test my app by faking the NFC tag scan?

解决方案

You specify a MIME type filter for the NDEF_DISCOVERED intent filter:

<data android:mimeType="text/plain"/>
<data android:mimeType="image/*" />

Consequently, the fake NFC intent needs to contain one of these MIME types to match the intent filter. You can add the type information to the intent using the setType() method:

public void scan_tag (View view) {
    final Intent intent = new Intent(NfcAdapter.ACTION_NDEF_DISCOVERED);
    intent.setType("text/plain");
    intent.putExtra(NfcAdapter.EXTRA_NDEF_MESSAGES, ...);
    startActivity(intent);
}

Also note that the above code won't add a tag handle to the NFC intent. Hence, you can't obtain a Tag object with

detected_tag = getIntent().getParcelableExtra(nfcAdapter.EXTRA_TAG);

Consequently, you also can't obtain an instance of the Ndef connection class using

Ndef ndef = Ndef.get(detected_tag);

You might want to look into the following questions/answers regarding mock tag objects:

Finally, be aware that there are several other issues in your code.

这篇关于通过伪造NFC标签扫描来测试应用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆