Android NDEF message with two activities and wrong intent content - android

I'm new to NFC with Android and I'm trying to make a kind of messaging app with NFC.
I have a first activity that sends the content of an EditText view to the other phone when beaming and displays the incoming message on a TextView on the other phone. This works fine.
I have another activity which is used to add a contact to the contacts register, it should work as the following:
A wants to add B as a contact,
A goes to the AddContactActivity, enters the name of the contact in the EditText view,
then B touches A's phone (on the same activity) and sends their identifier (public key, for further encryption).
My problem is that even though the code concerning the sending via NFC is basically the same between the two activities, when I beam on the second activity (AddContactActivity), the action of the intent sent is ACTION_MAIN instead of ACTION_NDEF_DISCOVERED, which has the effect of opening the first activity and thus not going through the right treatment.
Here is the code of the MainActivity:
public class MainActivity extends Activity {
private TextView mTextView;
private EditText mEdit;
NfcAdapter nfcAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = getIntent();
mTextView = (TextView)findViewById(R.id.retour);
mEdit = (EditText)findViewById(R.id.editText);
nfcAdapter = NfcAdapter.getDefaultAdapter(getApplicationContext());
nfcAdapter.setNdefPushMessageCallback(new NfcAdapter.CreateNdefMessageCallback() {
#Override public NdefMessage createNdefMessage(NfcEvent event) {
String stringOut = mEdit.getText().toString();
byte[] bytesOut = stringOut.getBytes();
NdefRecord ndefRecordOut = new NdefRecord(
NdefRecord.TNF_MIME_MEDIA,
"text/plain".getBytes(),
new byte[] {},
bytesOut);
NdefMessage ndefMessageout = new NdefMessage(ndefRecordOut);
return ndefMessageout;
}
}, this);
checkAndProcessBeamIntent(intent);
}
#Override
public void onResume() {
super.onResume();
Intent intent = getIntent();
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
IntentFilter ndef = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
try {
ndef.addDataType("text/plain");
} catch (IntentFilter.MalformedMimeTypeException e) {
e.printStackTrace();
}
IntentFilter[] intentFiltersArray = new IntentFilter[] {ndef, };
nfcAdapter.enableForegroundDispatch(this, pendingIntent, intentFiltersArray, null);
}
private void checkAndProcessBeamIntent(Intent intent) {
String action = intent.getAction();
if(action.equals(NfcAdapter.ACTION_NDEF_DISCOVERED)){
Parcelable[] parcelables =
intent.getParcelableArrayExtra(
NfcAdapter.EXTRA_NDEF_MESSAGES);
NdefMessage inNdefMessage = (NdefMessage)parcelables[0];
NdefRecord[] inNdefRecords = inNdefMessage.getRecords();
NdefRecord NdefRecord_0 = inNdefRecords[0];
String inMsg = new String(NdefRecord_0.getPayload());
mTextView.setText(inMsg);
}
}
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Toast.makeText(MainActivity.this,
intent.getAction().toString(),
Toast.LENGTH_LONG).show();
checkAndProcessBeamIntent(intent);
}
#Override
public void onPause() {
super.onPause();
nfcAdapter.disableForegroundDispatch(this);
}
#TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
public void generateKeys(){
Calendar cal = Calendar.getInstance();
Date now = cal.getTime();
cal.add(Calendar.YEAR, 1);
Date end = cal.getTime();
KeyPairGenerator kpg = null;
try {
kpg = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchProviderException e) {
e.printStackTrace();
}
try {
kpg.initialize(new KeyPairGeneratorSpec.Builder(getApplicationContext())
.setAlias("Keys")
.setStartDate(now)
.setEndDate(end)
.setSerialNumber(BigInteger.valueOf(1))
.setSubject(new X500Principal("CN=test1"))
.build());
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
}
kpg.generateKeyPair();
}
public void goToAddContact(View view) {
Intent intent = new Intent(this, AddContactActivity.class);
intent.setAction("NewActivity");
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);
}
}
Here is the code of the AddContactActivity:
public class AddContactActivity extends Activity{
NfcAdapter nfcAdapter;
EditText editText;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add_contact);
editText = (EditText)findViewById(R.id.editText);
nfcAdapter = NfcAdapter.getDefaultAdapter(getApplicationContext());
Intent intent = getIntent();
nfcAdapter.setNdefPushMessageCallback(new NfcAdapter.CreateNdefMessageCallback() {
#Override public NdefMessage createNdefMessage(NfcEvent event) {
String stringOut = getMyPublicKey();
byte[] bytesOut = stringOut.getBytes();
NdefRecord ndefRecordOut = new NdefRecord(
NdefRecord.TNF_MIME_MEDIA,
"text/plain".getBytes(),
new byte[] {},
bytesOut);
NdefMessage ndefMessageout = new NdefMessage(ndefRecordOut);
return ndefMessageout;
}
}, this);
checkAndProcessBeamIntent(intent);
}
#Override
public void onResume() {
super.onResume();
Intent intent = getIntent();
Toast.makeText(AddContactActivity.this,
"onResume : "+intent.getAction().toString(),
Toast.LENGTH_LONG).show();
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
IntentFilter ndef = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
try {
ndef.addDataType("text/plain");
} catch (IntentFilter.MalformedMimeTypeException e) {
e.printStackTrace();
}
IntentFilter[] intentFiltersArray = new IntentFilter[] {ndef, };
nfcAdapter.enableForegroundDispatch(this, pendingIntent, intentFiltersArray, null);
}
public void addContactDataBase(String publicKey){
SQLiteHelper sqLiteHelper = new SQLiteHelper(this);
sqLiteHelper.addUser(new User(editText.getText().toString(), publicKey));
}
public void checkUserInDataBase(String publicKey){
SQLiteHelper sqLiteHelper = new SQLiteHelper(this);
User u = sqLiteHelper.getUser(publicKey);
Toast.makeText(AddContactActivity.this,
""+u.getName(),
Toast.LENGTH_LONG).show();
}
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Toast.makeText(AddContactActivity.this,
"OnNewIntent : "+intent.getAction().toString(),
Toast.LENGTH_LONG).show();
checkAndProcessBeamIntent(intent);
}
#Override
public void onPause() {
super.onPause();
nfcAdapter.disableForegroundDispatch(this);
}
private void checkAndProcessBeamIntent(Intent intent) {
String action = intent.getAction();
if(action.equals(NfcAdapter.ACTION_NDEF_DISCOVERED)){
Toast.makeText(AddContactActivity.this,
"COUCOU",
Toast.LENGTH_LONG).show();
Parcelable[] parcelables =
intent.getParcelableArrayExtra(
NfcAdapter.EXTRA_NDEF_MESSAGES);
NdefMessage inNdefMessage = (NdefMessage)parcelables[0];
NdefRecord[] inNdefRecords = inNdefMessage.getRecords();
NdefRecord NdefRecord_0 = inNdefRecords[0];
String inMsg = new String(NdefRecord_0.getPayload());
addContactDataBase(inMsg);
checkUserInDataBase(inMsg);
}
}
public String getMyPublicKey(){
KeyStore ks = null;
RSAPublicKey publicKey = null;
try {
ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null);
KeyStore.PrivateKeyEntry keyEntry = (KeyStore.PrivateKeyEntry)ks.getEntry("Keys", null);
publicKey = (RSAPublicKey) keyEntry.getCertificate().getPublicKey();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (UnrecoverableEntryException e) {
e.printStackTrace();
}
return publicKey.toString();
}
}
And here is the manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.bsauzet.testnfc" >
<uses-permission android:name="android.permission.NFC" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="#string/app_name">
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".AddContactActivity"
android:label="#string/title_activity_add_contact"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
</application>
</manifest>

Check your appCompat libraries. Corruptness of appCompat libraries may cause wrong intent exceptions. Especially when you move your project from another PC or workspace (and/or IDE).

Getting an intent with ACTION_MAIN instead of ACTION_NDEF_DISCOVERED upon receiving an NFC event is a typical indication that an NDEF message containing an Android Application Record (AAR) was received and that the datatype of the first record of the NDEF message was not matched by any intent filter.
Your CreateNdefMessageCallback.createNdefMessage() method clearly does not add an AAR to the NDEF message. Consequently, the only reason why you would still receive an NDEF message containing an AAR would be that the createNdefMessage() raises an unhandled exception. In that case, the NFC stack will automatically generate an NDEF message containing a Play Store link and an AAR.
The most likely place that could cause createNdefmessage() to raise an unhandled exception is getMyPublicKey() (as that's the only part that differs between MainActivity and AddContactActivity).
So we can track the problem down to this part of your code:
ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null);
KeyStore.PrivateKeyEntry keyEntry = (KeyStore.PrivateKeyEntry)ks.getEntry("Keys", null);
publicKey = (RSAPublicKey) keyEntry.getCertificate().getPublicKey();
This code either
raises an unhandled runtime exception (e.g. if ks, keyEntry, or keyEntry.getCertificate() is null, or if ks.getEntry("Keys", null) cannot be cast to a KeyStore.PrivateKeyEntry), or
raises any of the handled exceptions, which causes publicKey to be null which results in an unhandled NullPointerException upon trying to invoke the toString() (in return publicKey.toString();).

Related

Sending NdefRecord via NFC but Receiving android.nfc.tech.IsoDep

I have created Two App for NFC. 1. NFC Sender 2. NFC Receiver. NFC Sender sends simple text data to NFC Receiver App. But I am in a big problem here.
I am unable to read simple text from NFC. The text format I am sending using NFC is NdefRecord.
1. NFC Sender Activity code :
public class MainActivity extends AppCompatActivity {
private Button btnPay;
private LinearLayout llRootView;
private NfcAdapter mNfcAdapter;
private EditText edtAmount;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnPay = findViewById(R.id.btnPay);
llRootView = findViewById(R.id.rootView);
edtAmount = findViewById(R.id.etAmount);
btnPay.setOnClickListener(view -> {
hideKeyboard(MainActivity.this);
if (TextUtils.isEmpty(edtAmount.getText().toString().trim())) {
Snackbar.make(llRootView, "Please enter valid value.", Snackbar.LENGTH_SHORT).show();
return;
}
startNFC();
});
}
private void startNFC() {
//Check if NFC is available on device
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
if (mNfcAdapter != null) {
//This will be called if the message is sent successfully
mNfcAdapter.setOnNdefPushCompleteCallback(event -> runOnUiThread(() ->
Toast.makeText(MainActivity.this, "Message sent.", Toast.LENGTH_SHORT)
.show()), this);
//This will refer back to createNdefMessage for what it will send
mNfcAdapter.setNdefPushMessageCallback(event -> new NdefMessage(createRecords()), this);
Snackbar.make(llRootView, "Please attach receiver device to back of your device.", Snackbar.LENGTH_INDEFINITE).show();
} else {
Toast.makeText(this, "NFC not available on this device",
Toast.LENGTH_SHORT).show();
}
}
private NdefRecord[] createRecords() {
//Api is high enough that we can use createMime, which is preferred.
NdefRecord record = NdefRecord.createMime("text/plain", edtAmount.getText().toString().trim().getBytes(StandardCharsets.UTF_8));
return new NdefRecord[]{
record
, NdefRecord.createApplicationRecord("com.example.nfcreceiver")
};
}
public static void hideKeyboard(Activity activity) {
InputMethodManager imm = (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
//Find the currently focused view, so we can grab the correct window token from it.
View view = activity.getCurrentFocus();
//If no view currently has focus, create a new one, just so we can grab a window token from it
if (view == null) {
view = new View(activity);
}
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
}
2. NFC Receiver Manifest :
<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>
<intent-filter>
<action android:name="android.nfc.action.TECH_DISCOVERED" />
<action android:name="android.nfc.action.TAG_DISCOVERED" />
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
3. NFC Receiver Activity Code :
public class MainActivity extends AppCompatActivity {
NfcAdapter mAdapter;
PendingIntent mPendingIntent;
TextView tvReceived, tvTagTech;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvReceived = findViewById(R.id.tvReceived);
tvTagTech = findViewById(R.id.tvTagList);
Initialization();
}
#Override
protected void onResume() {
super.onResume();
if (!WebConstant.isOnline()) {
Snackbar.make(tvReceived, R.string.str_no_internet_connection, Snackbar.LENGTH_LONG).show();
}
resolveIntent(getIntent());
if (mAdapter != null) {
if (!mAdapter.isEnabled()) {
Toast.makeText(this, "NFC Not Enabled", Toast.LENGTH_LONG).show();
}
mAdapter.enableForegroundDispatch(this, mPendingIntent, null, null);
}
}
#Override
protected void onPause() {
super.onPause();
if (mAdapter != null) {
mAdapter.disableForegroundDispatch(this);
}
}
private void Initialization() {
mAdapter = NfcAdapter.getDefaultAdapter(this);
if (mAdapter == null) {
Toast.makeText(this, "NFC Not found", Toast.LENGTH_LONG).show();
// finish();
return;
}
mPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this,
getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
}
#Override
protected void onNewIntent(final Intent intent) {
super.onNewIntent(intent);
new Handler().postDelayed(() -> {
setIntent(intent);
}, 0);
}
private void resolveIntent(Intent intent) {
String action = intent.getAction();
Log.d("ActionType", action);
if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)
|| NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)
|| NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {
getTagInfo(intent);
}
}
private void getTagInfo(Intent intent) {
sendToFirebase(intent);
}
private void sendToFirebase(Intent intent) {
String title, message;
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
NdefMessage[] messages = getNdefMessages(intent);
if (messages != null && messages.length > 0) {
title = "Success";
message = "Received NDEF Messages";
showAlert(title, message);
try {
String msg = new String(messages[0].getRecords()[0].getPayload(), StandardCharsets.UTF_8);
message = String.format("You received %sQAR", msg);
tvReceived.setText(message);
} catch (Exception e) {
e.printStackTrace();
title = "Error";
message = e.getLocalizedMessage();
showAlert(title, message);
}
} else {
title = "Failure";
message = "Received empty NDEF Messages";
showAlert(title, message);
}
} else {
title = "Failure";
message = "Received non NDEF Messages";
showAlert(title, message);
}
Map<String, Object> body = new HashMap<>();
body.put("Title", title);
body.put("Message", message);
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
if (tag != null) {
body.put("TagList", Arrays.toString(tag.getTechList()));
} else {
body.put("TagList", "");
}
RestClient.getInstance().push("NFCDetails", body, mResponse -> {
});
}
private NdefMessage[] getNdefMessages(Intent intent) {
Parcelable[] rawMessages = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
if (rawMessages != null) {
NdefMessage[] messages = new NdefMessage[rawMessages.length];
for (int i = 0; i < messages.length; i++) {
messages[i] = (NdefMessage) rawMessages[i];
}
return messages;
} else {
return null;
}
}
public void showAlert(String title, String message) {
AlertDialog dialog = new AlertDialog.Builder(this)
.create();
dialog.setTitle(title);
dialog.setMessage(message);
dialog.setButton(DialogInterface.BUTTON_POSITIVE, "OK", (dialog1, which) -> dialog1.dismiss());
dialog.show();
}
}
Please Help me to solve this puzzle.
The tag technology I am receiving in the Receiver app is below :
[android.nfc.tech.IsoDep, android.nfc.tech.NfcA]
Android Beam was deprecated in Android 10
Therefore the Pixel phone does not have the capability to send or receive NDEF messages sent from other old Android phones via this method you are trying to use.
Update:
The IsoDep Tag you are seeing is most likely being generated by the secure element part of the NFC hardware in the device as part of the NFC wallet type functionality that can hold your contactless bank card details. You would read this as though you were reading a contactless bank cards with, using the right AID's and higher level protocols and standards (But if you have not loaded a credit/debit card in to Google Wallet, it won't respond to any of the standard AID's for credit/debit cards)
The only thing you could really read from at the IsoDep level is a randomly generated UID.
Android does allow you to do Host Card Emulation (HCE) which allows you to set the response to selected AID queries. There is an AID for NDef data, thus using HCE you could replicate the Android Beam type funtionality, but this is complicated.
The recommended way to send data between 2 devices is via Bluetooth or Wifi Direct

Why is my Android NFC scanner not working

I have read many posts related to NFC scanning on Android, however I am unable to make it work.
The onNewIntent function never gets fired by my app sample.
In my activity, I have the following code:
//The array lists to hold our messages
private ArrayList<String> messagesToSendArray = new ArrayList<>();
private ArrayList<String> messagesReceivedArray = new ArrayList<>();
//Text boxes to add and display our messages
private EditText txtBoxAddMessage;
private TextView txtReceivedMessages;
private TextView txtMessagesToSend;
private NfcAdapter mNfcAdapter;
public void addMessage(View view) {
String newMessage = txtBoxAddMessage.getText().toString();
messagesToSendArray.add(newMessage);
txtBoxAddMessage.setText(null);
updateTextViews();
Toast.makeText(this, "Added Message", Toast.LENGTH_LONG).show();
}
private void updateTextViews() {
txtMessagesToSend.setText("Messages To Send:\n");
//Populate Our list of messages we want to send
if(messagesToSendArray.size() > 0) {
for (int i = 0; i < messagesToSendArray.size(); i++) {
txtMessagesToSend.append(messagesToSendArray.get(i));
txtMessagesToSend.append("\n");
}
}
txtReceivedMessages.setText("Messages Received:\n");
//Populate our list of messages we have received
if (messagesReceivedArray.size() > 0) {
for (int i = 0; i < messagesReceivedArray.size(); i++) {
txtReceivedMessages.append(messagesReceivedArray.get(i));
txtReceivedMessages.append("\n");
}
}
}
//Save our Array Lists of Messages for if the user navigates away
#Override
public void onSaveInstanceState(#NonNull Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putStringArrayList("messagesToSend", messagesToSendArray);
savedInstanceState.putStringArrayList("lastMessagesReceived",messagesReceivedArray);
}
//Load our Array Lists of Messages for when the user navigates back
#Override
public void onRestoreInstanceState(#NonNull Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
messagesToSendArray = savedInstanceState.getStringArrayList("messagesToSend");
messagesReceivedArray = savedInstanceState.getStringArrayList("lastMessagesReceived");
}
private PendingIntent mPendingIntent;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
txtBoxAddMessage = (EditText) findViewById(R.id.txtBoxAddMessage);
txtMessagesToSend = (TextView) findViewById(R.id.txtMessageToSend);
txtReceivedMessages = (TextView) findViewById(R.id.txtMessagesReceived);
Button btnAddMessage = (Button) findViewById(R.id.buttonAddMessage);
btnAddMessage.setText("Add Message");
updateTextViews();
//Check if NFC is available on device
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
if(mNfcAdapter != null) {
//Handle some NFC initialization here
}
else {
Toast.makeText(this, "NFC not available on this device",
Toast.LENGTH_SHORT).show();
}
//Check if NFC is available on device
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
if(mNfcAdapter != null) {
//This will refer back to createNdefMessage for what it will send
mNfcAdapter.setNdefPushMessageCallback(this, this);
//This will be called if the message is sent successfully
mNfcAdapter.setOnNdefPushCompleteCallback(this, this);
}
mPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this,
getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
this.onNewIntent(getIntent());
}
#Override
protected void onResume() {
super.onResume();
mNfcAdapter.enableForegroundDispatch(this, mPendingIntent, null, null);
}
#Override
protected void onPause() {
super.onPause();
if (mNfcAdapter != null) {
mNfcAdapter.disableForegroundDispatch(this);
}
}
#Override
public NdefMessage createNdefMessage(NfcEvent nfcEvent) {
//This will be called when another NFC capable device is detected.
if (messagesToSendArray.size() == 0) {
return null;
}
//We'll write the createRecords() method in just a moment
NdefRecord[] recordsToAttach = createRecords();
//When creating an NdefMessage we need to provide an NdefRecord[]
return new NdefMessage(recordsToAttach);
}
#Override
public void onNdefPushComplete(NfcEvent nfcEvent) {
//This is called when the system detects that our NdefMessage was
//Successfully sent.
messagesToSendArray.clear();
}
public NdefRecord[] createRecords() {
NdefRecord[] records = new NdefRecord[messagesToSendArray.size()];
for (int i = 0; i < messagesToSendArray.size(); i++){
byte[] payload = messagesToSendArray.get(i).
getBytes(StandardCharsets.UTF_8);
NdefRecord record = new NdefRecord(
NdefRecord.TNF_WELL_KNOWN, //Our 3-bit Type name format
NdefRecord.RTD_TEXT, //Description of our payload
new byte[0], //The optional id for our Record
payload); //Our payload for the Record
records[i] = record;
}
return records;
}
#Override
protected void onNewIntent(Intent intent) {
Log.e("TAG", "onNewIntent: "+intent.getAction());
super.onNewIntent(intent);
}
This is my manifest:
<?xml version="1.0" encoding="utf-8"?>
<uses-permission android:name="android.permission.NFC" />
<uses-feature android:name="android.hardware.nfc" android:required="true"/>
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:launchMode="singleTask"
android:name="com.banalapps.nfcmesssenger.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"/>
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
</application>
I am aware that this is probably a duplicate, but any help would be greatly appreciated as I struggled for days trying to make this work.

Testing an app by faking NFC tag scan

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.
Possibility for Fake NFC(Near Field Communication) Launch
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:
How to mock a Android NFC Tag object for unit testing
Is there a way to create an ACTION_NDEF_DISCOVERED intent from code
How to simulate the tag touch from other application
Finally, be aware that there are several other issues in your code.

NFC tag with text data and package name not working properly

I am developing small android application in which i wanted to use NFC functionality. I did this in following ways
On writer side :
boolean addAAR = true;
String uniqueId = "qrfid://nilkash";
byte[] uriField = uniqueId.getBytes(Charset.forName("US-ASCII"));
System.arraycopy(uriField, 0, payload, 0, uriField.length);
NdefRecord rtdUriRecord = new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, new byte[0], uriField);
if(addAAR)
{
return new NdefMessage(new NdefRecord[] {
rtdUriRecord, NdefRecord.createApplicationRecord("com.example.androidnfcurlreader")
});
And On receiver side I am using this in manifest file
<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" />
<data android:mimeType="application/com.example.androidnfcurlreader" />
</intent-filter>
According to my few things are working fine like when i read tag it launch my application but few things are going wrong when i read tag it launch my application but not reading data from it. once app is opened I have to again read tag for getting data. I check for this so when application launch it shows intent action as main instead of NDEF_DISCOVERED. while application is open and if I read tag it shows me data and at that time action is NDEF_DISCOVERED. But what I want when i read tag it opens my application and mush show me data as well.
What to do? How to solve this problem? Am I doing something wrong? Please help. Thank you .
//Reading of data from tag is like this
// inside oncreate activity
handleIntent(intent);
// inside onresume
setupForegroundDispatch(activity, mNfcAdapter);
// inside on pause
stopForegroundDispatch(activity, mNfcAdapter);
// on new intent
handleIntent(intent);
private void handleIntent(Intent intent)
{
String action = intent.getAction();
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {
String type = intent.getType();
if (MIME_TEXT_PLAIN.equals(type)) {
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
sendData(ndefmessage(tag));
} else {
Log.d(TAG, "Wrong mime type: " + type);
}
} else if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)) {
// In case we would still use the Tech Discovered Intent
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
String[] techList = tag.getTechList();
String searchedTech = Ndef.class.getName();
for (String tech : techList) {
if (searchedTech.equals(tech)) {
//new NdefReaderTask().execute(tag);
ndefmessage(tag);
break;
}
}
}
}
private void setupForegroundDispatch(final Activity activity, NfcAdapter adapter) {
final Intent intent = new Intent(activity.getApplicationContext(), activity.getClass());
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
final PendingIntent pendingIntent = PendingIntent.getActivity(activity.getApplicationContext(), 0, intent, 0);
IntentFilter[] filters = new IntentFilter[1];
String[][] techList = new String[][]{};
// Notice that this is the same filter as in our manifest.
filters[0] = new IntentFilter();
filters[0].addAction(NfcAdapter.ACTION_NDEF_DISCOVERED);
filters[0].addCategory(Intent.CATEGORY_DEFAULT);
try {
filters[0].addDataType(MIME_TEXT_PLAIN);
} catch (MalformedMimeTypeException e) {
throw new RuntimeException("Check your mime type.");
}
adapter.enableForegroundDispatch(activity, pendingIntent, filters, techList);
}
private static void stopForegroundDispatch(final Activity activity, NfcAdapter adapter)
{
adapter.disableForegroundDispatch(activity);
}
private String ndefmessage(Tag tag)
{
Tag tag1 = tag;
Ndef ndef = Ndef.get(tag);
if (ndef == null) {
// NDEF is not supported by this Tag.
return null;
}
NdefMessage ndefMessage = ndef.getCachedNdefMessage();
NdefRecord[] records = ndefMessage.getRecords();
for (NdefRecord ndefRecord : records) {
if (ndefRecord.getTnf() == NdefRecord.TNF_WELL_KNOWN && Arrays.equals(ndefRecord.getType(), NdefRecord.RTD_TEXT)) {
try {
return readText1(ndefRecord);
} catch (UnsupportedEncodingException e) {
Log.e(TAG, "Unsupported Encoding", e);
}
}
}
return null;
}
private String readText1(NdefRecord record) throws UnsupportedEncodingException
{
byte[] payload = record.getPayload();
String textEncoding = ((payload[0] & 128) == 0) ? "UTF-8" : "UTF-16";
int languageCodeLength = payload[0] & 0063;
return new String(payload, 0, payload.length, textEncoding);
}
private void sendData(String result)
{
eventlistsetter.getRfidListener().handleEvent(0, result);
}
}

NFC read not working

I am newbie in developing android application. Currently I am trying to develop my own tag reader which can read MiFare Ultralight tag.
BUt I failed to read the tag as NfcAdapter.ACTION_TECH_DISCOVERED.equals(action) always return false. Could somebody help me out?
NfcReader.java
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mText = (TextView) findViewById(R.id.text);
mText.setText("Scan a tag");
mAdapter = NfcAdapter.getDefaultAdapter();
// Create a generic PendingIntent that will be deliver to this activity. The NFC stack
// will fill in the intent with the details of the discovered tag before delivering to
// this activity.
mPendingIntent = PendingIntent.getActivity(this, 0,
new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
// Setup an intent filter for all MIME based dispatches
IntentFilter ndef = new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED);
try {
ndef.addDataType("*/*");
} catch (MalformedMimeTypeException e) {
throw new RuntimeException("fail", e);
}
mFilters = new IntentFilter[] {
ndef,
};
// Setup a tech list for all NfcF tags
mTechLists = new String[][] { new String[] { NfcF.class.getName() } };
Intent intent = getIntent();
getNdefMessages(intent);
}
public void getNdefMessages(Intent intent) {
// Parse the intent
NdefMessage[] msgs = null;
String action = intent.getAction();
if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)) {
Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
if (rawMsgs != null) {
msgs = new NdefMessage[rawMsgs.length];
for (int i = 0; i < rawMsgs.length; i++) {
msgs[i] = (NdefMessage) rawMsgs[i];
}
}
else {
// Unknown tag type
byte[] empty = new byte[] {};
NdefRecord record = new NdefRecord(NdefRecord.TNF_UNKNOWN, empty, empty, empty);
NdefMessage msg = new NdefMessage(new NdefRecord[] {record});
msgs = new NdefMessage[] {msg};
}
}
else {
//Log.e(TAG, "Unknown intent " + intent);
finish();
}
}
#Override
public void onResume() {
super.onResume();
mAdapter.enableForegroundDispatch(this, mPendingIntent, mFilters, mTechLists);
}
#Override
public void onNewIntent(Intent intent) {
Log.i("Foreground dispatch", "Discovered tag with intent: " + intent);
getNdefMessages(intent);
}
#Override
public void onPause() {
super.onPause();
//mAdapter.disableForegroundDispatch(this);
throw new RuntimeException("onPause not implemented to fix build");
}
Manifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.nfc.reader"
android:versionCode="1"
android:versionName="1.0">
<uses-permission android:name="android.permission.NFC" />
<uses-sdk android:minSdkVersion="10"/>
<uses-feature android:name="android.hardware.nfc" android:required="true" />
<application android:icon="#drawable/icon" android:label="#string/app_name">
<activity android:name="NfcReader"
android:theme="#android:style/Theme.NoTitleBar">
<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.TECH_DISCOVERED"/>
<data android:mimeType="text/plain" />
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
<meta-data android:name="android.nfc.action.TECH_DISCOVERED"
android:resource="#xml/nfctech" />
</activity>
</application>
</manifest>
When a tag with an NDEF message is detected, android looks for any Application with an Activity with intent-filter for action "android.nfc.action.NDEF_DISCOVERED". If two or more applications can handle this action, the one with the most precise filter receives the intent. When applications with the same filter are found, is the user who chooses one. So in your AndroidManifest.xml you have to use this action for detecting NDEF formatted tags.
If you want to read NDEF messages don't worry about which type of tag you are reading (Ultralight, Felica, Topaz,...). Android uses android.nfc.tech.Ndef for dispatch NDEF formatted tags. So for getting all NdefMessages when you activity is on foreground, you can use this snippet:
//Called in onCreate()
private void nfcConfig() {
mAdapter = NfcAdapter.getDefaultAdapter(this);
pendingIntent = PendingIntent.getActivity(
this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
techListsArray = new String[][]{new String[]{
Ndef.class.getName()
}};
}
#Override
public void onResume() {
super.onResume();
mAdapter.enableForegroundDispatch(this, pendingIntent, null, techListsArray);
}
#Override
public void onPause() {
super.onPause();
mAdapter.disableForegroundDispatch(this);
}
#Override
public void onNewIntent(Intent intent) {
getNdefMessages(intent);
}
If you want to read an Ultralight tag ( or another technology), it should be because you need the raw data from the tag. It must not be executed from the main application thread. For example:
private void nfcConfig(){
...
//Mifare Ultralight filter
techListsArray = new String[][]{new String[]{
NfcA.class.getName(),
MifareUltralight.class.getName()
}};
...
}
#Override
public void onNewIntent(Intent intent) {
getRawData(intent);
}
private void getRawData(Intent intent){
final Tag tag = (Tag) intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
MifareUltralight ulTag = MifareUltralight.getTag();
new ReadMFULTask(ulTag).execute();
}
private static class ReadMFULTask extends AsyncTask<Void, Void, Void> {
private Tag mTag;
public ReadMFULTask(Tag tag){
mTag = tag;
}
#Override
protected Void doInBackground(Void... arg0) {
MifareUltralight ulTag = MifareUltralight.get(mTag);
try {
ulTag.connect();
//Read all pages from an Ul tag
for(int i=0; i < 16; i=i+4){
byte[] data = ulTag.readPages(i);
String dataString = HexUtilities.getHexString(data);
Log.d("RAW DATA", dataString);
}
ulTag.close();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
In your code you are setting your intent filter based on ACTION_TECH_DISCOVERED, but then you try ndef.addDataType(/), which is the way you would go about setting up an intent filter for ACTION_NDEF_DISCOVERED. Instead you could simply do something like this:
IntentFilter ntech = new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED);
mFilters = new IntentFilter[]{ ntech };
make sure you have nfc_tech_list.xml set up properly:
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<tech-list>
<tech>android.nfc.tech.NfcA</tech>
<tech>android.nfc.tech.MifareUltralight</tech>
</tech-list>
</resources>

Categories

Resources