Android NFC reader gives "avc: denied"

Viewed 405

I tried making a app with the instructions from here: https://developer.android.com/guide/topics/connectivity/nfc/nfc

But when I compile the code and hold a card against my phone, I get a weird sound

My nfc is turned on, and I have a oneplus 6 running android 9

When I check logcat I see that I'm getting an error:

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"

Why am I getting this error instead of the textview displaying the info of the nfc card im scanning (I tried mulitple nfc cards but its gives the same error)

I found someone with somewhat of the same problem. they say you should follow the link and then watch comment #3. and then they say it has something to do with not being allowed to write to a tmp directory due to a security issue.

Please help me resolve this issue, here is my 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>

and here a picture of my activity_main.xml: activity

Thanks!

1 Answers

As NFC reading is handled by the Android OS, this will always make a sound when the card is read.

Depending on what NDEF records on the card and what you application is trying to do determines what the OS does with the NFC data.

You can download a generic NFC Application like https://play.google.com/store/apps/details?id=com.wakdev.wdnfc&hl=en_US to check what is on the card and write some sample data for you application to read.

It looks like you are trying to run this app on 2 phones and get them to talk using the deprecated Android Beam functionality https://developer.android.com/reference/android/nfc/NfcAdapter.CreateNdefMessageCallback (Which I found was never reliable - which is why they are removing it)

But you say

Why am I getting this error instead of the textview displaying the info of the nfc card im scanning

Which suggest you are trying to read an NFC card and the methods used are not for this.

The question is are you wanting to do with NFC?

1) Have a NFC card cause you APP to be launched by the Android OS?

Then use the data on the NFC some how.

If yes then you should just be putting the correct intent filters in your manifest and handle them in your MainActivity as you would do for any other intent, and the parsing the NDEF message from the intent data.

See https://developer.android.com/guide/components/intents-filters#ExampleFilters for some non NFC examples on how your App can register to the OS what Intent types it can handle.

2) In you App handle reading from the NFC card at certain times

This seems more what you are trying to do (and what my Apps do)

To do this you use enable the NFC Foreground dispatcher 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)

I use the following type of code to do this in my 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);
            }
        }
    }

The onResume stuff tells Android OS to send all NFC card type messages to my Application and the onNewIntent method gets the Intent passed to it by the OS and processes the Message if it is an NDEF message.

You can also when creating the Foreground Dispatcher in onResume add additional filters like

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

or remove filters for non NDEF cards (Usually add all types because I don't want other card types like contactless bank cards triggering other apps while my app is in the foreground)

Related