Illegal character in NDEF URI payload

2019-05-30 07:44发布

问题:

I'm writing NFC based app that would scan a NFC tag with a Url. Once, the tag is scanned, the app should retrieve information from a database and display it in a ListView.

The error occurs when I scan the NFC tag.

 Error in http connection java.lang.IllegalArgumentException: Illegal character in scheme at index 0: ??http://abc.com/~090123/get_items.php

The logcat displays some weird ?? characters before http://.

I'm writing the Url in the tag using following code:

private boolean writeTag(Tag tag) {         
    byte[] uriField = "http://abc.com/~090123/get_items.php".getBytes(Charset.forName("US-ASCII"));
    byte[] payload = new byte[uriField.length + 1];

    System.arraycopy(uriField, 0, payload, 1, uriField.length);         

    NdefRecord uriRecord = new NdefRecord(NdefRecord.TNF_WELL_KNOWN,
                NdefRecord.RTD_URI, new byte[0], payload);
    NdefMessage message = new NdefMessage(new NdefRecord[] { uriRecord});
    // ....
}

I'm receiving intent from the NFC tag like this:

protected void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
    setContentView(R.layout.content_display);           

    Intent intent =  getIntent();    

    if(NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) {

        Parcelable[] rawMsgs = intent.getParcelableArrayExtra(
            NfcAdapter.EXTRA_NDEF_MESSAGES);

        if(rawMsgs != null) {           

            NdefMessage msg = (NdefMessage) rawMsgs[0];        

            GetData getData = new GetData(this);
                getData.execute(new String(msg.getRecords()[0].getPayload()));
        }
    }
}

And I'm retrieving information from database using the following code:

protected BufferedReader doInBackground(String... params) {
    ArrayList<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
    try {
        HttpClient httpclient = new DefaultHttpClient();

        for (int i = 0; i < params.length; i++) 
        {
            HttpPost httppost = new HttpPost(params[i]);
            httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
            HttpResponse response = httpclient.execute(httppost);
            HttpEntity entity = response.getEntity();
             is = entity.getContent();
        }

    } catch (Exception e) {
            Log.e("log_tag", "Error in http connection " + e.toString());
    }

    Log.e("Input Stream", "Input Stream:" + is);

    BufferedReader myReader = new BufferedReader(new InputStreamReader(is));

    return myReader;
}

I have the intent filter for the previous NDEF records like this:

<activity
            android:name=".DisplayContentActivity"
            android:label="@string/app_name" >

            <intent-filter>
                <action android:name="android.nfc.action.NDEF_DISCOVERED" />
                <data
                    android:host="abc.com"
                    android:pathPrefix="/~090123/get_items.php"
                    android:scheme="http" />

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

Any help would be appreciated.

回答1:

The first byte of URI payload is the URI identifier code, it is an URI prefix like http://www. or mailto:. You correctly added a byte to the payload length and used an offset of 1 when copying your URL. You never write this byte so it remains 0x00 (no prefix), which means you need write the whole URL. This is fine, though you could set it to 0x03 for a http:// prefix if you want to save a few bytes.

When reading the payload with msg.getRecords()[0].getPayload(), you need to cut off that byte and add the prefix yourself. I don't think there's a native Android API for this, so you would have to lookup the prefix yourself or try the library mentioned in the other answer.

To read the uri correctly you should use something like this:

byte[] payload = msg.getRecords()[0].getPayload();
byte identifierCode = payload[0];

String prefix = getUrlPrefix(identifierCode); // you need to implement this one
String url = prefix +
    new String(payload, 1, payload.length -1, Charset.forName("US-ASCII"));

getData.execute(url);

The method getUrlPrefix could contain a simple switch statement to return a prefix string for an identifier code.

You can see a list of codes here: About the NDEF Format

Value    Protocol
-----    --------
0x00     No prepending is done ... the entire URI is contained in the URI Field
0x01     http://www.
0x02     https://www.
0x03     http://
0x04     https://
0x05     tel:
0x06     mailto:
0x07     ftp://anonymous:anonymous@
0x08     ftp://ftp.
0x09     ftps://
0x0A     sftp://
...


回答2:

You are parsing the NDEF Record incorrectly. The first byte is not a character. Either read in the NFC Forum standard or see this lib I've created for helping in this situation. You might check out the Android source code to see how the record you're trying to read is constructed.



标签: android nfc ndef