1 /******************************************************************************
2 *
3 * Copyright (C) 2010-2014 Broadcom Corporation
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at:
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 ******************************************************************************/
18
19 /******************************************************************************
20 *
21 * Handle ndef messages
22 *
23 ******************************************************************************/
24 #include <android-base/logging.h>
25 #include <android-base/stringprintf.h>
26 #include <string.h>
27
28 #include "ndef_utils.h"
29 #include "nfa_api.h"
30 #include "nfa_dm_int.h"
31
32 using android::base::StringPrintf;
33
34 /*******************************************************************************
35 * URI Well-known-type prefixes
36 *******************************************************************************/
37 const uint8_t* nfa_dm_ndef_wkt_uri_str_tbl[] = {
38 nullptr, /* 0x00 */
39 (const uint8_t*)"http://www.", /* 0x01 */
40 (const uint8_t*)"https://www.", /* 0x02 */
41 (const uint8_t*)"http://", /* 0x03 */
42 (const uint8_t*)"https://", /* 0x04 */
43 (const uint8_t*)"tel:", /* 0x05 */
44 (const uint8_t*)"mailto:", /* 0x06 */
45 (const uint8_t*)"ftp://anonymous:anonymous@", /* 0x07 */
46 (const uint8_t*)"ftp://ftp.", /* 0x08 */
47 (const uint8_t*)"ftps://", /* 0x09 */
48 (const uint8_t*)"sftp://", /* 0x0A */
49 (const uint8_t*)"smb://", /* 0x0B */
50 (const uint8_t*)"nfs://", /* 0x0C */
51 (const uint8_t*)"ftp://", /* 0x0D */
52 (const uint8_t*)"dav://", /* 0x0E */
53 (const uint8_t*)"news:", /* 0x0F */
54 (const uint8_t*)"telnet://", /* 0x10 */
55 (const uint8_t*)"imap:", /* 0x11 */
56 (const uint8_t*)"rtsp://", /* 0x12 */
57 (const uint8_t*)"urn:", /* 0x13 */
58 (const uint8_t*)"pop:", /* 0x14 */
59 (const uint8_t*)"sip:", /* 0x15 */
60 (const uint8_t*)"sips:", /* 0x16 */
61 (const uint8_t*)"tftp:", /* 0x17 */
62 (const uint8_t*)"btspp://", /* 0x18 */
63 (const uint8_t*)"btl2cap://", /* 0x19 */
64 (const uint8_t*)"btgoep://", /* 0x1A */
65 (const uint8_t*)"tcpobex://", /* 0x1B */
66 (const uint8_t*)"irdaobex://", /* 0x1C */
67 (const uint8_t*)"file://", /* 0x1D */
68 (const uint8_t*)"urn:epc:id:", /* 0x1E */
69 (const uint8_t*)"urn:epc:tag:", /* 0x1F */
70 (const uint8_t*)"urn:epc:pat:", /* 0x20 */
71 (const uint8_t*)"urn:epc:raw:", /* 0x21 */
72 (const uint8_t*)"urn:epc:", /* 0x22 */
73 (const uint8_t*)"urn:nfc:" /* 0x23 */
74 };
75 #define NFA_DM_NDEF_WKT_URI_STR_TBL_SIZE \
76 (sizeof(nfa_dm_ndef_wkt_uri_str_tbl) / sizeof(uint8_t*))
77
78 /*******************************************************************************
79 **
80 ** Function nfa_dm_ndef_dereg_hdlr_by_handle
81 **
82 ** Description Deregister NDEF record type handler
83 **
84 ** Returns TRUE (message buffer to be freed by caller)
85 **
86 *******************************************************************************/
nfa_dm_ndef_dereg_hdlr_by_handle(tNFA_HANDLE ndef_type_handle)87 void nfa_dm_ndef_dereg_hdlr_by_handle(tNFA_HANDLE ndef_type_handle) {
88 tNFA_DM_CB* p_cb = &nfa_dm_cb;
89 uint16_t hdlr_idx;
90 hdlr_idx = (uint16_t)(ndef_type_handle & NFA_HANDLE_MASK);
91
92 if (p_cb->p_ndef_handler[hdlr_idx]) {
93 GKI_freebuf(p_cb->p_ndef_handler[hdlr_idx]);
94 p_cb->p_ndef_handler[hdlr_idx] = nullptr;
95 }
96 }
97
98 /*******************************************************************************
99 **
100 ** Function nfa_dm_ndef_dereg_all
101 **
102 ** Description Deregister all NDEF record type handlers (called during
103 ** shutdown(.
104 **
105 ** Returns Nothing
106 **
107 *******************************************************************************/
nfa_dm_ndef_dereg_all(void)108 void nfa_dm_ndef_dereg_all(void) {
109 tNFA_DM_CB* p_cb = &nfa_dm_cb;
110 uint32_t i;
111
112 for (i = 0; i < NFA_NDEF_MAX_HANDLERS; i++) {
113 /* If this is a free slot, then remember it */
114 if (p_cb->p_ndef_handler[i] != nullptr) {
115 GKI_freebuf(p_cb->p_ndef_handler[i]);
116 p_cb->p_ndef_handler[i] = nullptr;
117 }
118 }
119 }
120
121 /*******************************************************************************
122 **
123 ** Function nfa_dm_ndef_reg_hdlr
124 **
125 ** Description Register NDEF record type handler
126 **
127 ** Returns TRUE if message buffer is to be freed by caller
128 **
129 *******************************************************************************/
nfa_dm_ndef_reg_hdlr(tNFA_DM_MSG * p_data)130 bool nfa_dm_ndef_reg_hdlr(tNFA_DM_MSG* p_data) {
131 tNFA_DM_CB* p_cb = &nfa_dm_cb;
132 uint32_t hdlr_idx, i;
133 tNFA_DM_API_REG_NDEF_HDLR* p_reg_info = (tNFA_DM_API_REG_NDEF_HDLR*)p_data;
134 tNFA_NDEF_REGISTER ndef_register;
135
136 /* If registering default handler, check to see if one is already registered
137 */
138 if (p_reg_info->tnf == NFA_TNF_DEFAULT) {
139 /* check if default handler is already registered */
140 if (p_cb->p_ndef_handler[NFA_NDEF_DEFAULT_HANDLER_IDX]) {
141 LOG(WARNING) << StringPrintf("Default NDEF handler being changed.");
142
143 /* Free old registration info */
144 nfa_dm_ndef_dereg_hdlr_by_handle(
145 (tNFA_HANDLE)NFA_NDEF_DEFAULT_HANDLER_IDX);
146 }
147 LOG(VERBOSE) << StringPrintf("Default NDEF handler successfully registered.");
148 hdlr_idx = NFA_NDEF_DEFAULT_HANDLER_IDX;
149 }
150 /* Get available entry in ndef_handler table, and check if requested type is
151 already registered */
152 else {
153 hdlr_idx = NFA_HANDLE_INVALID;
154
155 /* Check if this type is already registered */
156 for (i = (NFA_NDEF_DEFAULT_HANDLER_IDX + 1); i < NFA_NDEF_MAX_HANDLERS;
157 i++) {
158 /* If this is a free slot, then remember it */
159 if (p_cb->p_ndef_handler[i] == nullptr) {
160 hdlr_idx = i;
161 break;
162 }
163 }
164 }
165
166 if (hdlr_idx != NFA_HANDLE_INVALID) {
167 /* Update the table */
168 p_cb->p_ndef_handler[hdlr_idx] = p_reg_info;
169
170 p_reg_info->ndef_type_handle =
171 (tNFA_HANDLE)(NFA_HANDLE_GROUP_NDEF_HANDLER | hdlr_idx);
172
173 ndef_register.ndef_type_handle = p_reg_info->ndef_type_handle;
174 ndef_register.status = NFA_STATUS_OK;
175
176 LOG(VERBOSE) << StringPrintf(
177 "NDEF handler successfully registered. Handle=0x%08x",
178 p_reg_info->ndef_type_handle);
179 tNFA_NDEF_EVT_DATA nfa_ndef_evt_data;
180 nfa_ndef_evt_data.ndef_reg = ndef_register;
181 (*(p_reg_info->p_ndef_cback))(NFA_NDEF_REGISTER_EVT, &nfa_ndef_evt_data);
182
183 /* indicate that we will free message buffer when type_handler is
184 * deregistered */
185 return false;
186 } else {
187 /* Error */
188 LOG(ERROR) << StringPrintf("NDEF handler failed to register.");
189 ndef_register.ndef_type_handle = NFA_HANDLE_INVALID;
190 ndef_register.status = NFA_STATUS_FAILED;
191 tNFA_NDEF_EVT_DATA nfa_ndef_evt_data;
192 nfa_ndef_evt_data.ndef_reg = ndef_register;
193 (*(p_reg_info->p_ndef_cback))(NFA_NDEF_REGISTER_EVT, &nfa_ndef_evt_data);
194
195 return true;
196 }
197 }
198
199 /*******************************************************************************
200 **
201 ** Function nfa_dm_ndef_dereg_hdlr
202 **
203 ** Description Deregister NDEF record type handler
204 **
205 ** Returns TRUE (message buffer to be freed by caller)
206 **
207 *******************************************************************************/
nfa_dm_ndef_dereg_hdlr(tNFA_DM_MSG * p_data)208 bool nfa_dm_ndef_dereg_hdlr(tNFA_DM_MSG* p_data) {
209 tNFA_DM_API_DEREG_NDEF_HDLR* p_dereginfo =
210 (tNFA_DM_API_DEREG_NDEF_HDLR*)p_data;
211
212 /* Make sure this is a NDEF_HDLR handle */
213 if (((p_dereginfo->ndef_type_handle & NFA_HANDLE_GROUP_MASK) !=
214 NFA_HANDLE_GROUP_NDEF_HANDLER) ||
215 ((p_dereginfo->ndef_type_handle & NFA_HANDLE_MASK) >=
216 NFA_NDEF_MAX_HANDLERS)) {
217 LOG(ERROR) << StringPrintf("Invalid handle for NDEF type handler: 0x%08x",
218 p_dereginfo->ndef_type_handle);
219 } else {
220 nfa_dm_ndef_dereg_hdlr_by_handle(p_dereginfo->ndef_type_handle);
221 }
222
223 return true;
224 }
225
226 /*******************************************************************************
227 **
228 ** Function nfa_dm_ndef_find_next_handler
229 **
230 ** Description Find next ndef handler for a given record type
231 **
232 ** Returns void
233 **
234 *******************************************************************************/
nfa_dm_ndef_find_next_handler(tNFA_DM_API_REG_NDEF_HDLR * p_init_handler,uint8_t tnf,uint8_t * p_type_name,uint8_t type_name_len,uint8_t * p_payload,uint32_t payload_len)235 tNFA_DM_API_REG_NDEF_HDLR* nfa_dm_ndef_find_next_handler(
236 tNFA_DM_API_REG_NDEF_HDLR* p_init_handler, uint8_t tnf,
237 uint8_t* p_type_name, uint8_t type_name_len, uint8_t* p_payload,
238 uint32_t payload_len) {
239 tNFA_DM_CB* p_cb = &nfa_dm_cb;
240 uint8_t i;
241
242 /* if init_handler is NULL, then start with the first non-default handler */
243 if (!p_init_handler)
244 i = NFA_NDEF_DEFAULT_HANDLER_IDX + 1;
245 else {
246 /* Point to handler index after p_init_handler */
247 i = (p_init_handler->ndef_type_handle & NFA_HANDLE_MASK) + 1;
248 }
249
250 /* Look for next handler */
251 for (; i < NFA_NDEF_MAX_HANDLERS; i++) {
252 /* Check if TNF matches */
253 if ((p_cb->p_ndef_handler[i]) && (p_cb->p_ndef_handler[i]->tnf == tnf)) {
254 if (p_type_name == nullptr) {
255 break;
256 }
257 /* TNF matches. */
258 /* If handler is for a specific URI type, check if type is WKT URI, */
259 /* and that the URI prefix abrieviation for this handler matches */
260 if (p_cb->p_ndef_handler[i]->flags & NFA_NDEF_FLAGS_WKT_URI) {
261 /* This is a handler for a specific URI type */
262 /* Check if this recurd is WKT URI */
263 if ((p_payload) && (type_name_len == 1) && (*p_type_name == 'U')) {
264 /* Check if URI prefix abrieviation matches */
265 if ((payload_len > 1) &&
266 (p_payload[0] == p_cb->p_ndef_handler[i]->uri_id)) {
267 /* URI prefix abrieviation matches */
268 /* If handler does not specify an absolute URI, then match found. */
269 /* If absolute URI, then compare URI for match (skip over uri_id in
270 * ndef payload) */
271 if ((p_cb->p_ndef_handler[i]->uri_id != NFA_NDEF_URI_ID_ABSOLUTE) ||
272 ((payload_len > p_cb->p_ndef_handler[i]->name_len) &&
273 (memcmp(&p_payload[1], p_cb->p_ndef_handler[i]->name,
274 p_cb->p_ndef_handler[i]->name_len) == 0))) {
275 /* Handler found. */
276 break;
277 }
278 }
279 /* Check if handler is absolute URI but NDEF is using prefix
280 abrieviation */
281 else if ((p_cb->p_ndef_handler[i]->uri_id ==
282 NFA_NDEF_URI_ID_ABSOLUTE) &&
283 (p_payload[0] != NFA_NDEF_URI_ID_ABSOLUTE)) {
284 /* Handler is absolute URI but NDEF is using prefix abrieviation.
285 * Compare URI prefix */
286 if ((p_payload[0] < NFA_DM_NDEF_WKT_URI_STR_TBL_SIZE) &&
287 strlen(
288 (const char*)nfa_dm_ndef_wkt_uri_str_tbl[p_payload[0]]) >=
289 p_cb->p_ndef_handler[i]->name_len &&
290 (memcmp(p_cb->p_ndef_handler[i]->name,
291 (char*)nfa_dm_ndef_wkt_uri_str_tbl[p_payload[0]],
292 p_cb->p_ndef_handler[i]->name_len) == 0)) {
293 /* Handler found. */
294 break;
295 }
296 }
297 /* Check if handler is using prefix abrieviation, but NDEF is using
298 absolute URI */
299 else if ((p_cb->p_ndef_handler[i]->uri_id !=
300 NFA_NDEF_URI_ID_ABSOLUTE) &&
301 (p_payload[0] == NFA_NDEF_URI_ID_ABSOLUTE)) {
302 /* Handler is using prefix abrieviation, but NDEF is using absolute
303 * URI. Compare URI prefix */
304 if ((p_cb->p_ndef_handler[i]->uri_id <
305 NFA_DM_NDEF_WKT_URI_STR_TBL_SIZE) &&
306 payload_len > strlen((const char*)nfa_dm_ndef_wkt_uri_str_tbl
307 [p_cb->p_ndef_handler[i]->uri_id]) &&
308 (memcmp(&p_payload[1],
309 nfa_dm_ndef_wkt_uri_str_tbl[p_cb->p_ndef_handler[i]
310 ->uri_id],
311 strlen((const char*)nfa_dm_ndef_wkt_uri_str_tbl
312 [p_cb->p_ndef_handler[i]->uri_id])) == 0)) {
313 /* Handler found. */
314 break;
315 }
316 }
317 }
318 }
319 /* Not looking for specific URI. Check if type_name for this handler
320 matches the NDEF record's type_name */
321 else if (p_cb->p_ndef_handler[i]->name_len == type_name_len) {
322 if ((type_name_len == 0) || (memcmp(p_cb->p_ndef_handler[i]->name,
323 p_type_name, type_name_len) == 0)) {
324 /* Handler found */
325 break;
326 }
327 }
328 }
329 }
330
331 if (i < NFA_NDEF_MAX_HANDLERS)
332 return (p_cb->p_ndef_handler[i]);
333 else
334 return (nullptr);
335 }
336
337 /*******************************************************************************
338 **
339 ** Function nfa_dm_ndef_clear_notified_flag
340 **
341 ** Description Clear 'whole_message_notified' flag for all the handlers
342 ** (flag used to indicate that this handler has already
343 ** handled the entire incoming NDEF message)
344 **
345 ** Returns void
346 **
347 *******************************************************************************/
nfa_dm_ndef_clear_notified_flag(void)348 void nfa_dm_ndef_clear_notified_flag(void) {
349 tNFA_DM_CB* p_cb = &nfa_dm_cb;
350 uint8_t i;
351
352 for (i = 0; i < NFA_NDEF_MAX_HANDLERS; i++) {
353 if (p_cb->p_ndef_handler[i]) {
354 p_cb->p_ndef_handler[i]->flags &= ~NFA_NDEF_FLAGS_WHOLE_MESSAGE_NOTIFIED;
355 }
356 }
357 }
358
359 /*******************************************************************************
360 **
361 ** Function nfa_dm_ndef_handle_message
362 **
363 ** Description Handle incoming ndef message
364 **
365 ** Returns void
366 **
367 *******************************************************************************/
nfa_dm_ndef_handle_message(tNFA_STATUS status,uint8_t * p_msg_buf,uint32_t len)368 void nfa_dm_ndef_handle_message(tNFA_STATUS status, uint8_t* p_msg_buf,
369 uint32_t len) {
370 tNFA_DM_CB* p_cb = &nfa_dm_cb;
371 tNDEF_STATUS ndef_status;
372 uint8_t *p_rec, *p_ndef_start, *p_type, *p_payload, *p_rec_end;
373 uint32_t payload_len;
374 uint8_t tnf, type_len, rec_hdr_flags, id_len;
375 tNFA_DM_API_REG_NDEF_HDLR* p_handler;
376 tNFA_NDEF_DATA ndef_data;
377 uint8_t rec_count = 0;
378 bool record_handled, entire_message_handled;
379
380 LOG(VERBOSE) << StringPrintf("nfa_dm_ndef_handle_message status=%i, len=%i",
381 status, len);
382
383 if (status != NFA_STATUS_OK) {
384 /* If problem reading NDEF message, then exit (no action required) */
385 return;
386 }
387
388 /* If in exclusive RF mode is activer, then route NDEF message callback
389 * registered with NFA_StartExclusiveRfControl */
390 if ((p_cb->flags & NFA_DM_FLAGS_EXCL_RF_ACTIVE) &&
391 (p_cb->p_excl_ndef_cback)) {
392 /* No ndef-handler handle, since this callback is not from
393 * RegisterNDefHandler */
394 ndef_data.ndef_type_handle = 0;
395 ndef_data.p_data = p_msg_buf;
396 ndef_data.len = len;
397 tNFA_NDEF_EVT_DATA nfa_ndef_evt_data;
398 nfa_ndef_evt_data.ndef_data = ndef_data;
399 (*p_cb->p_excl_ndef_cback)(NFA_NDEF_DATA_EVT, &nfa_ndef_evt_data);
400 return;
401 }
402
403 /* Handle zero length - notify default handler */
404 if (len == 0) {
405 p_handler = p_cb->p_ndef_handler[NFA_NDEF_DEFAULT_HANDLER_IDX];
406 if (p_handler != nullptr) {
407 LOG(VERBOSE) << StringPrintf(
408 "Notifying default handler of zero-length NDEF message...");
409 ndef_data.ndef_type_handle = p_handler->ndef_type_handle;
410 ndef_data.p_data = nullptr; /* Start of record */
411 ndef_data.len = 0;
412 tNFA_NDEF_EVT_DATA nfa_ndef_evt_data;
413 nfa_ndef_evt_data.ndef_data = ndef_data;
414 (*p_handler->p_ndef_cback)(NFA_NDEF_DATA_EVT, &nfa_ndef_evt_data);
415 }
416 return;
417 }
418
419 /* Validate the NDEF message */
420 ndef_status = NDEF_MsgValidate(p_msg_buf, len, true);
421 if (ndef_status != NDEF_OK) {
422 LOG(ERROR) << StringPrintf(
423 "Received invalid NDEF message. NDEF status=0x%x", ndef_status);
424 return;
425 }
426
427 /* NDEF message received from backgound polling. Pass the NDEF message to the
428 * NDEF handlers */
429
430 /* New NDEF message. Clear 'notified' flag for all the handlers */
431 nfa_dm_ndef_clear_notified_flag();
432
433 /* Indicate that no handler has handled this entire NDEF message (e.g.
434 * connection-handover handler *) */
435 entire_message_handled = false;
436
437 /* Get first record in message */
438 p_rec = p_ndef_start = p_msg_buf;
439
440 /* Check each record in the NDEF message */
441 while (p_rec != nullptr) {
442 /* Get record type */
443 p_type = NDEF_RecGetType(p_rec, &tnf, &type_len);
444
445 /* Indicate record not handled yet */
446 record_handled = false;
447
448 /* Get pointer to record payload */
449 p_payload = NDEF_RecGetPayload(p_rec, &payload_len);
450
451 /* Find first handler for this type */
452 p_handler = nfa_dm_ndef_find_next_handler(nullptr, tnf, p_type, type_len,
453 p_payload, payload_len);
454 if (p_handler == nullptr) {
455 /* Not a registered NDEF type. Use default handler */
456 p_handler = p_cb->p_ndef_handler[NFA_NDEF_DEFAULT_HANDLER_IDX];
457 if (p_handler != nullptr) {
458 LOG(VERBOSE) << StringPrintf(
459 "No handler found. Using default handler...");
460 }
461 }
462
463 while (p_handler) {
464 /* If handler is for whole NDEF message, and it has already been notified,
465 * then skip notification */
466 if (p_handler->flags & NFA_NDEF_FLAGS_WHOLE_MESSAGE_NOTIFIED) {
467 /* Look for next handler */
468 p_handler = nfa_dm_ndef_find_next_handler(
469 p_handler, tnf, p_type, type_len, p_payload, payload_len);
470 continue;
471 }
472
473 /* Get pointer to record payload */
474 LOG(VERBOSE) << StringPrintf("Calling ndef type handler (%x)",
475 p_handler->ndef_type_handle);
476
477 ndef_data.ndef_type_handle = p_handler->ndef_type_handle;
478 ndef_data.p_data = p_rec; /* Start of record */
479
480 /* Calculate length of NDEF record */
481 if (p_payload != nullptr)
482 ndef_data.len = payload_len + (uint32_t)(p_payload - p_rec);
483 else {
484 /* If no payload, calculate length of ndef record header */
485 p_rec_end = p_rec;
486
487 /* First byte is the header flags */
488 rec_hdr_flags = *p_rec_end++;
489
490 /* Next byte is the type field length */
491 type_len = *p_rec_end++;
492
493 /* Next is the payload length (1 or 4 bytes) */
494 if (rec_hdr_flags & NDEF_SR_MASK) {
495 p_rec_end++;
496 } else {
497 p_rec_end += 4;
498 }
499
500 /* ID field Length */
501 if (rec_hdr_flags & NDEF_IL_MASK)
502 id_len = *p_rec_end++;
503 else
504 id_len = 0;
505 p_rec_end += id_len;
506
507 ndef_data.len = (uint32_t)(p_rec_end - p_rec);
508 }
509
510 /* If handler wants entire ndef message, then pass pointer to start of
511 * message and */
512 /* set 'notified' flag so handler won't get notified on subsequent records
513 * for this */
514 /* NDEF message. */
515 if (p_handler->flags & NFA_NDEF_FLAGS_HANDLE_WHOLE_MESSAGE) {
516 ndef_data.p_data = p_ndef_start; /* Start of NDEF message */
517 ndef_data.len = len;
518 p_handler->flags |= NFA_NDEF_FLAGS_WHOLE_MESSAGE_NOTIFIED;
519
520 /* Indicate that at least one handler has received entire NDEF message
521 */
522 entire_message_handled = true;
523 }
524
525 /* Notify NDEF type handler */
526 tNFA_NDEF_EVT_DATA nfa_ndef_evt_data;
527 nfa_ndef_evt_data.ndef_data = ndef_data;
528 (*p_handler->p_ndef_cback)(NFA_NDEF_DATA_EVT, &nfa_ndef_evt_data);
529
530 /* Indicate that at lease one handler has received this record */
531 record_handled = true;
532
533 /* Look for next handler */
534 p_handler = nfa_dm_ndef_find_next_handler(
535 p_handler, tnf, p_type, type_len, p_payload, payload_len);
536 }
537
538 /* Check if at least one handler was notified of this record (only happens
539 * if no default handler was register) */
540 if ((!record_handled) && (!entire_message_handled)) {
541 /* Unregistered NDEF record type; no default handler */
542 LOG(WARNING) << StringPrintf("Unhandled NDEF record (#%i)", rec_count);
543 }
544
545 rec_count++;
546 p_rec = NDEF_MsgGetNextRec(p_rec);
547 }
548 }
549