Why NFC works with PendingIntent.FLAG_MUTABLE and not with PendingIntent.FLAG_IMMUTABLE

huangapple go评论79阅读模式
英文:

Why NFC works with PendingIntent.FLAG_MUTABLE and not with PendingIntent.FLAG_IMMUTABLE

问题

I have a question about NFC and the PendingIntent function. I am building an app that reads the content of an NDEF Tag and sends the value to the server. After much searching, I found a bug in the code, specifically in a function called PendingIntent.

This is the code of MainActivity.java. When I change FLAG_MUTABLE to IMMUTABLE, the value of the intent is null, and the app crashes. When it is set to MUTABLE, the value of the intent is android.nfc.action.NDEF_DISCOVERED, and everything works. I want to know why this is happening because every example and even somewhere in the documentation I found, it should be IMMUTABLE.

import androidx.appcompat.app.AppCompatActivity;
import android.app.PendingIntent;
import android.content.Intent;
import android.nfc.FormatException;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.Ndef;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
import android.widget.Toast;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;

public class MainActivity extends AppCompatActivity {
    private NfcAdapter nfcAdapter;
    private PendingIntent pendingIntent;

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

        nfcAdapter = NfcAdapter.getDefaultAdapter(this);

        if (nfcAdapter == null) {
            Toast.makeText(this, "Nepodporovaný telefon", Toast.LENGTH_LONG).show();
        } else if (!nfcAdapter.isEnabled()) {
            Toast.makeText(this, "NFC je vypnuté", Toast.LENGTH_LONG).show();
            startActivity(new Intent(Settings.ACTION_NFC_SETTINGS));
        }

        pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), PendingIntent.FLAG_MUTABLE);
    }

    // ... Rest of the code

    private String readText(NdefRecord record) throws UnsupportedEncodingException {
        byte[] payload = record.getPayload();

        String textEncoding = ((payload[0] & 128) == 0) ? "UTF-8" : "UTF-16";
        int languageCodeLength = payload[0] & 63;

        return new String(payload, languageCodeLength + 1, payload.length - languageCodeLength - 1, textEncoding);
    }
}

Manifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.NFC"/>
    <uses-feature android:name="android.hardware.nfc" android:required="true"/>
    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.APPNFC"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

I have a question about NFC and Pending Intent function. I am building an app, which reads content of NDEF Tag and send value to server.
After much searching I found a bug in the code, specifically in a function PendingIntent.

This is code of MainActivity.java. When I change FLAG_MUTABLE to IMMUTABLE, value of intent is null and app crash. When it is MUTABLE, value of intent is android.nfc.action.NDEF_DISCOVERED and everything works. I want to know, why it is, because every example and even somewhere in docs I found, that it should be IMMUTABLE.

import androidx.appcompat.app.AppCompatActivity;
import android.app.PendingIntent;
import android.content.Intent;
import android.nfc.FormatException;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.Ndef;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
import android.widget.Toast;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
public class MainActivity extends AppCompatActivity {
private NfcAdapter nfcAdapter;
private PendingIntent pendingIntent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
nfcAdapter = NfcAdapter.getDefaultAdapter(this);
if (nfcAdapter == null) {
Toast.makeText(this, &quot;Nepodporovan&#253; telefon&quot;, Toast.LENGTH_LONG).show();
} else if (!nfcAdapter.isEnabled()) {
Toast.makeText(this, &quot;NFC je vypnut&#233;&quot;, Toast.LENGTH_LONG).show();
startActivity(new Intent(Settings.ACTION_NFC_SETTINGS));
}
pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), PendingIntent.FLAG_MUTABLE);
}
@Override
protected void onResume() {
super.onResume();
if (nfcAdapter != null) {
nfcAdapter.enableForegroundDispatch(this, pendingIntent, null, null);
}
}
@Override
protected void onPause() {
super.onPause();
if (nfcAdapter != null) {
nfcAdapter.disableForegroundDispatch(this);
}
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Log.d(&quot;INTENT ACTION&quot;, intent.getAction());
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
if (tag != null) {
Ndef ndef = Ndef.get(tag);
if (ndef == null) {
Toast.makeText(this, &quot;Pr&#225;zdn&#253; NFC Tag&quot;, Toast.LENGTH_SHORT).show();
return;
}
try {
ndef.connect();
NdefMessage ndefMessage = ndef.getNdefMessage();
if (ndefMessage != null) {
NdefRecord[] records = ndefMessage.getRecords();
for (NdefRecord record : records) {
if (record.getTnf() == NdefRecord.TNF_WELL_KNOWN &amp;&amp; Arrays.equals(record.getType(), NdefRecord.RTD_TEXT)) {
String text = readText(record);
// TODO: Odeslat data do Google Sheets
Toast.makeText(this, text, Toast.LENGTH_SHORT).show();
}
}
}
} catch (IOException | FormatException e) {
e.printStackTrace();
} finally {
try {
ndef.close();
} catch (IOException e) {
e.printStackTrace();
}
}
} else {
Toast.makeText(this, &quot;Poškozen&#253; NFC Tag&quot;, Toast.LENGTH_LONG).show();
}
}
private String readText(NdefRecord record) throws UnsupportedEncodingException {
byte[] payload = record.getPayload();
String textEncoding = ((payload[0] &amp; 128) == 0) ? &quot;UTF-8&quot; : &quot;UTF-16&quot;;
int languageCodeLength = payload[0] &amp; 63;
return new String(payload, languageCodeLength + 1, payload.length - languageCodeLength - 1, textEncoding);
}
}

Manifest.xml

&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;manifest xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
xmlns:tools=&quot;http://schemas.android.com/tools&quot;&gt;
&lt;uses-permission android:name=&quot;android.permission.INTERNET&quot;/&gt;
&lt;uses-permission android:name=&quot;android.permission.NFC&quot;/&gt;
&lt;uses-feature android:name=&quot;android.hardware.nfc&quot; android:required=&quot;true&quot;/&gt;
&lt;application
android:allowBackup=&quot;true&quot;
android:dataExtractionRules=&quot;@xml/data_extraction_rules&quot;
android:fullBackupContent=&quot;@xml/backup_rules&quot;
android:icon=&quot;@mipmap/ic_launcher&quot;
android:label=&quot;@string/app_name&quot;
android:roundIcon=&quot;@mipmap/ic_launcher_round&quot;
android:supportsRtl=&quot;true&quot;
android:theme=&quot;@style/Theme.APPNFC&quot;
tools:targetApi=&quot;31&quot;&gt;
&lt;activity
android:name=&quot;.MainActivity&quot;
android:exported=&quot;true&quot;&gt;
&lt;intent-filter&gt;
&lt;action android:name=&quot;android.intent.action.MAIN&quot; /&gt;
&lt;category android:name=&quot;android.intent.category.LAUNCHER&quot; /&gt;
&lt;/intent-filter&gt;
&lt;/activity&gt;
&lt;/application&gt;
&lt;/manifest&gt;

答案1

得分: 2

FLAG_IMMUTABLE:标志表示创建的PendingIntent应该是不可变的。这意味着传递给send方法的附加意图参数,用于填充此意图的未填充属性,将被忽略。

FLAG_IMMUTABLE仅限制了由send()的调用者发送的意图的语义更改能力。PendingIntent的创建者始终可以通过FLAG_UPDATE_CURRENT来更新PendingIntent本身。

根据Android文档,FLAG_IMMUTABLE去除了更改语义的能力,因为这里不会将NFC标签的信息与意图一起发送。

但对于FLAG_MUTABLE:

只有在某些功能依赖于修改基础意图时,才应使用FLAG_MUTABLE,例如需要与内联回复或气泡一起使用的任何PendingIntent。

可变标志允许修改。

这可能是使用Mutable而不使用Immutable的原因。

这就是为什么当将PendingIntent标志设置为IMMUTABLE时,您的应用程序崩溃,因为NFC意图无法添加到您的PendingIntent,导致您的onNewIntent()方法中的意图值为空。

  1. FLAG_MUTABLE
  2. FLAG_IMMUTABLE
英文:

> FLAG_IMMUTABLE :
> Flag indicating that the created PendingIntent should be immutable.
> This means that the additional intent argument passed to the send
> methods to fill in unpopulated properties of this intent will be
> ignored.

>
> FLAG_IMMUTABLE only limits the ability to alter the semantics of the
> intent that is sent by send() by the invoker of send(). The creator of
> the PendingIntent can always update the PendingIntent itself via
> FLAG_UPDATE_CURRENT.

As per Android Documentation, FLAG_IMMUTABLE removes the ability of altering the semantics, as here the the info of NFC tag is not send with the intent.

But for FLAG_MUTABLE,

> FLAG_MUTABLE should only be used when some functionality relies on
> modifying the underlying intent, e.g. any PendingIntent that needs to
> be used with inline reply or bubbles.

Mutable Flag allows to modify.

That could be the reason for working with Mutable and Not working with Immutable.
That's' why when you set the PendingIntent flag to IMMUTABLE, your app crashes because the NFC intent cannot be added to your PendingIntent, leading to a null intent value in your onNewIntent() method.

  1. FLAG_MUTABLE
  2. FLAG_IMMUTABLE

huangapple
  • 本文由 发表于 2023年7月11日 00:40:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/76655730.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定