安卓NFC读取器显示“avc: denied”。

4
我尝试按照这里的指示制作一个应用程序: https://developer.android.com/guide/topics/connectivity/nfc/nfc 但是当我编译代码并将一张卡放在手机上时,我听到了奇怪的声音
我的nfc已经打开,我使用运行Android 9的oneplus 6。
当我检查logcat时,发现我遇到了一个错误:
56:27.888 7834-7850/com.appname  D/DecorView: onWindowFocusChangedFromViewRoot hasFocus: true, DecorView@6302a6f[MainActivity]
2019-10-12 22:56:27.913 7834-7834/com.appname  W/RenderThread: type=1400 audit(0.0:2504791): avc: denied { read } for name="u:object_r:vendor_default_prop:s0" dev="tmpfs" ino=21655 scontext=u:r:untrusted_app:s0:c16,c257,c512,c768 tcontext=u:object_r:vendor_default_prop:s0 tclass=file permissive=0
2019-10-12 22:56:27.928 7834-7880/com.appname E/libc: Access denied finding property "vendor.debug.egl.swapinterval"

为什么我会收到这个错误,而不是文本视图显示我正在扫描的NFC卡的信息(我尝试了多张NFC卡,但都出现相同的错误)

我发现有人遇到了类似的问题。他们说你应该按照链接中的说明并查看第三条评论。然后他们说这与由于安全问题而不允许写入tmp目录有关。

请帮助我解决这个问题,这是我的MainActivity.java:

package com.packagename;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.nfc.NfcAdapter.CreateNdefMessageCallback;
import android.nfc.NfcEvent;
import android.os.Bundle;
import android.os.Parcelable;
import android.renderscript.RenderScript;
import android.widget.TextView;
import android.widget.Toast;

import static android.nfc.NdefRecord.createMime;

public class MainActivity extends AppCompatActivity implements CreateNdefMessageCallback {
    NfcAdapter nfcAdapter;
    TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Check for available NFC Adapter
        nfcAdapter = NfcAdapter.getDefaultAdapter(this);
        if (nfcAdapter == null) {
            Toast.makeText(this, "NFC is not available", Toast.LENGTH_LONG).show();
            finish();
            return;
        }
        // Register callback
        nfcAdapter.setNdefPushMessageCallback(this, this);
    }

    @Override
    public NdefMessage createNdefMessage(NfcEvent event) {
        String text = ("Beam me up, Android!\n\n" +
                "Beam Time: " + System.currentTimeMillis());
        NdefMessage msg = new NdefMessage(
                new NdefRecord[] { createMime(
                        "application/vnd.com.example.android.beam", text.getBytes())
                        /**
                         * The Android Application Record (AAR) is commented out. When a device
                         * receives a push with an AAR in it, the application specified in the AAR
                         * is guaranteed to run. The AAR overrides the tag dispatch system.
                         * You can add it back in to guarantee that this
                         * activity starts when receiving a beamed message. For now, this code
                         * uses the tag dispatch system.
                        */
                        //,NdefRecord.createApplicationRecord("com.example.android.beam")
                });
        return msg;
    }

    @Override
    public void onResume() {
        super.onResume();
        // Check to see that the Activity started due to an Android Beam
        if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) {
            processIntent(getIntent());
        }
    }

    @Override
    public void onNewIntent(Intent intent) {
        // onResume gets called after this to handle the intent
        setIntent(intent);
    }

    /**
     * Parses the NDEF Message from the intent and prints to the TextView
     */
    void processIntent(Intent intent) {
        textView = findViewById(R.id.textView);
        Parcelable[] rawMsgs = intent.getParcelableArrayExtra(
                NfcAdapter.EXTRA_NDEF_MESSAGES);
        // only one message sent during the beam
        NdefMessage msg = (NdefMessage) rawMsgs[0];
        // record 0 contains the MIME type, record 1 is the AAR, if present
        textView.setText(new String(msg.getRecords()[0].getPayload()));
    }
}

AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.packagename">
    <uses-permission android:name="android.permission.NFC" />
    <uses-feature android:name="android.hardware.nfc" />

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

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

以下是我的activity_main.xml的图片:

activity

谢谢!

1个回答

1
由于NFC读取是由Android操作系统处理的,因此每当读取卡片时都会发出声音。取决于卡上的NDEF记录以及您的应用程序要做什么,确定操作系统如何处理NFC数据。您可以下载一个通用的NFC应用程序,例如https://play.google.com/store/apps/details?id=com.wakdev.wdnfc&hl=en_US,以检查卡上有什么并编写一些示例数据供您的应用程序读取。看起来您正在尝试在两个手机上运行此应用程序,并使用已弃用的Android Beam功能https://developer.android.com/reference/android/nfc/NfcAdapter.CreateNdefMessageCallback使它们通信(我发现这从未可靠过,这就是为什么他们正在删除它的原因)。但是你说:“为什么我收到了这个错误而不是文本视图显示我正在扫描的nfc卡的信息”,这表明您正在尝试读取NFC卡,并且所使用的方法不适用于此。
问题是,你想用NFC做什么?
1)是否希望通过NFC卡启动您的应用程序并使用NFC上的数据?
如果是,则应该在清单中放置正确的意图过滤器,并像处理任何其他意图一样在MainActivity中处理它们,然后从意图数据中解析NDEF消息。
有关一些非NFC示例以及您的应用程序如何向操作系统注册其可以处理的意图类型,请参见https://developer.android.com/guide/components/intents-filters#ExampleFilters
2)在您的应用程序中某些时间处理从NFC卡读取
这似乎更符合您尝试做什么(也是我的应用程序所做的)。
要做到这一点,您需要启用NFC前台分发器 https://developer.android.com/reference/android/nfc/NfcAdapter.html#enableForegroundDispatch(android.app.Activity,%20android.app.PendingIntent,%20android.content.IntentFilter%5B%5D,%20java.lang.String%5B%5D%5B%5D)
我在我的Activity中使用以下类型的代码来实现这一点。
public class ViewNFCCardsActivity extends AppCompatActivity {

    private NfcAdapter mNfcAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_view_nfccards);

        mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
    }


@Override
    protected void onResume() {
        super.onResume();

        IntentFilter tagDetected = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);
        IntentFilter ndefDetected = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
        IntentFilter techDetected = new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED);
        IntentFilter[] nfcIntentFilter = new IntentFilter[]{techDetected,tagDetected,ndefDetected};

        try {
                ndefDetected.addDataType("*/*");
            } catch (IntentFilter.MalformedMimeTypeException e) {}

        PendingIntent pendingIntent = PendingIntent.getActivity(
                this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
        if(mNfcAdapter!= null)
            mNfcAdapter.enableForegroundDispatch(this, pendingIntent, nfcIntentFilter, null);

    }

    @Override
    protected void onPause() {
        super.onPause();
        if(mNfcAdapter!= null)
            mNfcAdapter.disableForegroundDispatch(this);
    }


@Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);

        TextView textView = (TextView) findViewById(R.id.MessageText);

        // High level way to get Ndef records from what is already been read from the tag
        if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
            Parcelable[] rawMessages = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
            if (rawMessages != null) {
                // Only need the first message
                NdefMessage msg = (NdefMessage) rawMessages[0];
                // Only need the first record in message
                String message = new String(msg.getRecords()[0].getPayload());
                textView.setText(message);
            }
        }
    }

onResume 的部分告诉 Android 操作系统将所有 NFC 卡类型的消息发送到我的应用程序,onNewIntent 方法通过操作系统传递给它的意图处理消息,如果是 NDEF 消息则进行处理。

在创建前台调度程序时,您还可以在 onResume 中添加其他过滤器。

try {
            ndefDetected.addDataType("custom/text");
        } catch (IntentFilter.MalformedMimeTypeException e) {}

或者移除非NDEF卡的过滤器(通常添加所有类型,因为我不希望其他卡类型(比如非接触式银行卡)在我的应用程序处于前台时触发其他应用程序)。

所以我尝试使用你的代码,但是它给了我完全相同的问题:TextView没有改变。我修改了一件事:我在protected void onResume()中放置了super.onResume();,并将textView id更改为我在程序中使用的textView id。我需要在oncreate中放置其他东西吗?我需要添加implements吗?我甚至看到有些人在创建nfc读卡器时有额外的文件,我需要那些吗?谢谢(我确实正在尝试制作一个从nfc卡中读取数据并将文本放入textView的应用程序) - novun
我已更新我的示例以显示Activity和onCreate,其中我只获取默认的NFC适配器和其他提示。你是否检查过卡片是NDEF类型和NDEF格式?并且卡片上有一个带有一些有效负载的NDEF记录可供读取吗?因为如果卡片没有要显示的内容,它将不会显示任何内容。我添加了一个链接到一个通用的应用程序,有助于查看卡片的格式和编写测试数据。 - Andrew
我仍然遇到了super.onResume();的同样问题,它仍然不起作用。我已经使用了7张卡进行了检查,但它们都没有起作用。我又看到这个人使用了额外的文件(Textrecord、urirecord),我不需要那些吗?除了代码之外,还有其他需要更改的地方吗?我的应用程序的其余部分是否正常?因为如果它对你有效,那么为什么对我无效? - novun
请使用我回答中提供的NFC Tools应用程序来读取卡片,您可以使用它来复制和粘贴“标签类型”、“可用技术”,然后向下滚动以复制“记录0”的详细信息,并将结果粘贴到您的问题中。 NFC卡有许多不同的格式,示例代码不会尝试确定卡上的数据类型。视频中的额外文件用于封装不同的卡类型,并根据卡上的数据类型执行更有意义的操作。您可能希望根据以后卡上的数据执行类似的操作。 - Andrew
嗨,我使用了该应用程序进行检查,发现在任何NFC卡上都没有找到文本。然后我拿了一张空白的卡,在你提供的应用程序上写下了“hiii”。它还显示该卡是NfcA、MifareClassic和Ndef类型。但是当我使用你提供的代码时,它没有读取“hiii”。 - novun
我已经更新了代码,很抱歉在从我的应用程序中剪切和粘贴时错过了super.onResume(),我还删除了自定义的mimeType代码,我已经添加回来了一个*/*的mimeType,因为我忘记了根据https://developer.android.com/guide/topics/connectivity/nfc/nfc#dispatching注册mimeType(s),如果您不注册mimeType(s),则处理操作系统会将其作为ACTION_TAG_DISCOVERED Intent而不是ACTION_NDEF_DISCOVERED给您。 */* mimeType应该涵盖所有NDEF消息,现在您实际上有了一些可以从卡片中读取的内容(这已经作为独立应用程序测试,读取text/plain)。 - Andrew

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接