1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.nfc.nxp; 18 19 import com.android.nfc.DeviceHost.TagEndpoint; 20 21 import android.nfc.FormatException; 22 import android.nfc.NdefMessage; 23 import android.nfc.tech.IsoDep; 24 import android.nfc.tech.MifareClassic; 25 import android.nfc.tech.MifareUltralight; 26 import android.nfc.tech.Ndef; 27 import android.nfc.tech.NfcA; 28 import android.nfc.tech.NfcB; 29 import android.nfc.tech.NfcF; 30 import android.nfc.tech.NfcV; 31 import android.nfc.tech.TagTechnology; 32 import android.os.Bundle; 33 import android.util.Log; 34 35 /** 36 * Native interface to the NFC tag functions 37 */ 38 public class NativeNfcTag implements TagEndpoint { 39 static final boolean DBG = false; 40 41 static final int STATUS_CODE_TARGET_LOST = 146; 42 43 private int[] mTechList; 44 private int[] mTechHandles; 45 private int[] mTechLibNfcTypes; 46 private Bundle[] mTechExtras; 47 private byte[][] mTechPollBytes; 48 private byte[][] mTechActBytes; 49 private byte[] mUid; 50 51 // mConnectedHandle stores the *real* libnfc handle 52 // that we're connected to. 53 private int mConnectedHandle; 54 55 // mConnectedTechIndex stores to which technology 56 // the upper layer stack is connected. Note that 57 // we may be connected to a libnfchandle without being 58 // connected to a technology - technology changes 59 // may occur runtime, whereas the underlying handle 60 // could stay present. Usually all technologies are on the 61 // same handle, with the exception of multi-protocol 62 // tags. 63 private int mConnectedTechIndex; // Index in mTechHandles 64 65 private final String TAG = "NativeNfcTag"; 66 67 private boolean mIsPresent; // Whether the tag is known to be still present 68 69 private PresenceCheckWatchdog mWatchdog; 70 class PresenceCheckWatchdog extends Thread { 71 72 private int watchdogTimeout = 125; 73 74 private boolean isPresent = true; 75 private boolean isStopped = false; 76 private boolean isPaused = false; 77 private boolean doCheck = true; 78 79 public synchronized void pause() { 80 isPaused = true; 81 doCheck = false; 82 this.notifyAll(); 83 } 84 85 public synchronized void doResume() { 86 isPaused = false; 87 // We don't want to resume presence checking immediately, 88 // but go through at least one more wait period. 89 doCheck = false; 90 this.notifyAll(); 91 } 92 93 public synchronized void end() { 94 isStopped = true; 95 doCheck = false; 96 this.notifyAll(); 97 } 98 99 public synchronized void setTimeout(int timeout) { 100 watchdogTimeout = timeout; 101 doCheck = false; // Do it only after we have waited "timeout" ms again 102 this.notifyAll(); 103 } 104 105 @Override 106 public synchronized void run() { 107 if (DBG) Log.d(TAG, "Starting background presence check"); 108 while (isPresent && !isStopped) { 109 try { 110 if (!isPaused) { 111 doCheck = true; 112 } 113 this.wait(watchdogTimeout); 114 if (doCheck) { 115 isPresent = doPresenceCheck(); 116 } else { 117 // 1) We are paused, waiting for unpause 118 // 2) We just unpaused, do pres check in next iteration 119 // (after watchdogTimeout ms sleep) 120 // 3) We just set the timeout, wait for this timeout 121 // to expire once first. 122 // 4) We just stopped, exit loop anyway 123 } 124 } catch (InterruptedException e) { 125 // Activity detected, loop 126 } 127 } 128 mIsPresent = false; 129 // Restart the polling loop 130 131 Log.d(TAG, "Tag lost, restarting polling loop"); 132 doDisconnect(); 133 if (DBG) Log.d(TAG, "Stopping background presence check"); 134 } 135 } 136 137 private native int doConnect(int handle); 138 public synchronized int connectWithStatus(int technology) { 139 if (mWatchdog != null) { 140 mWatchdog.pause(); 141 } 142 int status = -1; 143 for (int i = 0; i < mTechList.length; i++) { 144 if (mTechList[i] == technology) { 145 // Get the handle and connect, if not already connected 146 if (mConnectedHandle != mTechHandles[i]) { 147 // We're not yet connected to this handle, there are 148 // a few scenario's here: 149 // 1) We are not connected to anything yet - allow 150 // 2) We are connected to a technology which has 151 // a different handle (multi-protocol tag); we support 152 // switching to that. 153 if (mConnectedHandle == -1) { 154 // Not connected yet 155 status = doConnect(mTechHandles[i]); 156 } else { 157 // Connect to a tech with a different handle 158 status = reconnectWithStatus(mTechHandles[i]); 159 } 160 if (status == 0) { 161 mConnectedHandle = mTechHandles[i]; 162 mConnectedTechIndex = i; 163 } 164 } else { 165 // 1) We are connected to a technology which has the same 166 // handle; we do not support connecting at a different 167 // level (libnfc auto-activates to the max level on 168 // any handle). 169 // 2) We are connecting to the ndef technology - always 170 // allowed. 171 if ((technology == TagTechnology.NDEF) || 172 (technology == TagTechnology.NDEF_FORMATABLE)) { 173 status = 0; 174 } else { 175 if ((technology != TagTechnology.ISO_DEP) && 176 (hasTechOnHandle(TagTechnology.ISO_DEP, mTechHandles[i]))) { 177 // Don't allow to connect a -4 tag at a different level 178 // than IsoDep, as this is not supported by 179 // libNFC. 180 status = -1; 181 } else { 182 status = 0; 183 } 184 } 185 if (status == 0) { 186 mConnectedTechIndex = i; 187 // Handle was already identical 188 } 189 } 190 break; 191 } 192 } 193 if (mWatchdog != null) { 194 mWatchdog.doResume(); 195 } 196 return status; 197 } 198 @Override 199 public synchronized boolean connect(int technology) { 200 return connectWithStatus(technology) == 0; 201 } 202 203 @Override 204 public synchronized void startPresenceChecking() { 205 // Once we start presence checking, we allow the upper layers 206 // to know the tag is in the field. 207 mIsPresent = true; 208 if (mWatchdog == null) { 209 mWatchdog = new PresenceCheckWatchdog(); 210 mWatchdog.start(); 211 } 212 } 213 214 @Override 215 public synchronized boolean isPresent() { 216 // Returns whether the tag is still in the field to the best 217 // of our knowledge. 218 return mIsPresent; 219 } 220 native boolean doDisconnect(); 221 @Override 222 public synchronized boolean disconnect() { 223 boolean result = false; 224 225 mIsPresent = false; 226 if (mWatchdog != null) { 227 // Watchdog has already disconnected or will do it 228 mWatchdog.end(); 229 try { 230 mWatchdog.join(); 231 } catch (InterruptedException e) { 232 // Should never happen. 233 } 234 mWatchdog = null; 235 result = true; 236 } else { 237 result = doDisconnect(); 238 } 239 240 mConnectedTechIndex = -1; 241 mConnectedHandle = -1; 242 return result; 243 } 244 245 native int doReconnect(); 246 public synchronized int reconnectWithStatus() { 247 if (mWatchdog != null) { 248 mWatchdog.pause(); 249 } 250 int status = doReconnect(); 251 if (mWatchdog != null) { 252 mWatchdog.doResume(); 253 } 254 return status; 255 } 256 @Override 257 public synchronized boolean reconnect() { 258 return reconnectWithStatus() == 0; 259 } 260 261 native int doHandleReconnect(int handle); 262 public synchronized int reconnectWithStatus(int handle) { 263 if (mWatchdog != null) { 264 mWatchdog.pause(); 265 } 266 int status = doHandleReconnect(handle); 267 if (mWatchdog != null) { 268 mWatchdog.doResume(); 269 } 270 return status; 271 } 272 273 private native byte[] doTransceive(byte[] data, boolean raw, int[] returnCode); 274 @Override 275 public synchronized byte[] transceive(byte[] data, boolean raw, int[] returnCode) { 276 if (mWatchdog != null) { 277 mWatchdog.pause(); 278 } 279 byte[] result = doTransceive(data, raw, returnCode); 280 if (mWatchdog != null) { 281 mWatchdog.doResume(); 282 } 283 return result; 284 } 285 286 private native int doCheckNdef(int[] ndefinfo); 287 private synchronized int checkNdefWithStatus(int[] ndefinfo) { 288 if (mWatchdog != null) { 289 mWatchdog.pause(); 290 } 291 int status = doCheckNdef(ndefinfo); 292 if (mWatchdog != null) { 293 mWatchdog.doResume(); 294 } 295 return status; 296 } 297 @Override 298 public synchronized boolean checkNdef(int[] ndefinfo) { 299 return checkNdefWithStatus(ndefinfo) == 0; 300 } 301 302 private native byte[] doRead(); 303 @Override 304 public synchronized byte[] readNdef() { 305 if (mWatchdog != null) { 306 mWatchdog.pause(); 307 } 308 byte[] result = doRead(); 309 if (mWatchdog != null) { 310 mWatchdog.doResume(); 311 } 312 return result; 313 } 314 315 private native boolean doWrite(byte[] buf); 316 @Override 317 public synchronized boolean writeNdef(byte[] buf) { 318 if (mWatchdog != null) { 319 mWatchdog.pause(); 320 } 321 boolean result = doWrite(buf); 322 if (mWatchdog != null) { 323 mWatchdog.doResume(); 324 } 325 return result; 326 } 327 328 native boolean doPresenceCheck(); 329 @Override 330 public synchronized boolean presenceCheck() { 331 if (mWatchdog != null) { 332 mWatchdog.pause(); 333 } 334 boolean result = doPresenceCheck(); 335 if (mWatchdog != null) { 336 mWatchdog.doResume(); 337 } 338 return result; 339 } 340 341 native boolean doNdefFormat(byte[] key); 342 @Override 343 public synchronized boolean formatNdef(byte[] key) { 344 if (mWatchdog != null) { 345 mWatchdog.pause(); 346 } 347 boolean result = doNdefFormat(key); 348 if (mWatchdog != null) { 349 mWatchdog.doResume(); 350 } 351 return result; 352 } 353 354 native boolean doMakeReadonly(byte[] key); 355 @Override 356 public synchronized boolean makeReadOnly() { 357 if (mWatchdog != null) { 358 mWatchdog.pause(); 359 } 360 boolean result; 361 if (hasTech(TagTechnology.MIFARE_CLASSIC)) { 362 result = doMakeReadonly(MifareClassic.KEY_DEFAULT); 363 } else { 364 // No key needed for other technologies 365 result = doMakeReadonly(new byte[] {}); 366 } 367 if (mWatchdog != null) { 368 mWatchdog.doResume(); 369 } 370 return result; 371 } 372 373 native boolean doIsIsoDepNdefFormatable(byte[] poll, byte[] act); 374 @Override 375 public synchronized boolean isNdefFormatable() { 376 if (hasTech(TagTechnology.MIFARE_CLASSIC) || hasTech(TagTechnology.MIFARE_ULTRALIGHT)) { 377 // These are always formatable 378 return true; 379 } 380 if (hasTech(TagTechnology.NFC_V)) { 381 // Currently libnfc only formats NXP NFC-V tags 382 if (mUid[5] >= 1 && mUid[5] <= 3 && mUid[6] == 0x04) { 383 return true; 384 } else { 385 return false; 386 } 387 } 388 // For ISO-DEP, call native code to determine at lower level if format 389 // is possible. It will need NFC-A poll/activation time bytes for this. 390 if (hasTech(TagTechnology.ISO_DEP)) { 391 int nfcaTechIndex = getTechIndex(TagTechnology.NFC_A); 392 if (nfcaTechIndex != -1) { 393 return doIsIsoDepNdefFormatable(mTechPollBytes[nfcaTechIndex], 394 mTechActBytes[nfcaTechIndex]); 395 } else { 396 return false; 397 } 398 } else { 399 // Formatting not supported by libNFC 400 return false; 401 } 402 } 403 404 @Override 405 public int getHandle() { 406 // This is just a handle for the clients; it can simply use the first 407 // technology handle we have. 408 if (mTechHandles.length > 0) { 409 return mTechHandles[0]; 410 } else { 411 return 0; 412 } 413 } 414 415 @Override 416 public byte[] getUid() { 417 return mUid; 418 } 419 420 @Override 421 public int[] getTechList() { 422 return mTechList; 423 } 424 425 private int getConnectedHandle() { 426 return mConnectedHandle; 427 } 428 429 private int getConnectedLibNfcType() { 430 if (mConnectedTechIndex != -1 && mConnectedTechIndex < mTechLibNfcTypes.length) { 431 return mTechLibNfcTypes[mConnectedTechIndex]; 432 } else { 433 return 0; 434 } 435 } 436 437 @Override 438 public int getConnectedTechnology() { 439 if (mConnectedTechIndex != -1 && mConnectedTechIndex < mTechList.length) { 440 return mTechList[mConnectedTechIndex]; 441 } else { 442 return 0; 443 } 444 } 445 native int doGetNdefType(int libnfctype, int javatype); 446 private int getNdefType(int libnfctype, int javatype) { 447 return doGetNdefType(libnfctype, javatype); 448 } 449 450 private void addTechnology(int tech, int handle, int libnfctype) { 451 int[] mNewTechList = new int[mTechList.length + 1]; 452 System.arraycopy(mTechList, 0, mNewTechList, 0, mTechList.length); 453 mNewTechList[mTechList.length] = tech; 454 mTechList = mNewTechList; 455 456 int[] mNewHandleList = new int[mTechHandles.length + 1]; 457 System.arraycopy(mTechHandles, 0, mNewHandleList, 0, mTechHandles.length); 458 mNewHandleList[mTechHandles.length] = handle; 459 mTechHandles = mNewHandleList; 460 461 int[] mNewTypeList = new int[mTechLibNfcTypes.length + 1]; 462 System.arraycopy(mTechLibNfcTypes, 0, mNewTypeList, 0, mTechLibNfcTypes.length); 463 mNewTypeList[mTechLibNfcTypes.length] = libnfctype; 464 mTechLibNfcTypes = mNewTypeList; 465 } 466 467 @Override 468 public void removeTechnology(int tech) { 469 synchronized (this) { 470 int techIndex = getTechIndex(tech); 471 if (techIndex != -1) { 472 int[] mNewTechList = new int[mTechList.length - 1]; 473 System.arraycopy(mTechList, 0, mNewTechList, 0, techIndex); 474 System.arraycopy(mTechList, techIndex + 1, mNewTechList, techIndex, 475 mTechList.length - techIndex - 1); 476 mTechList = mNewTechList; 477 478 int[] mNewHandleList = new int[mTechHandles.length - 1]; 479 System.arraycopy(mTechHandles, 0, mNewHandleList, 0, techIndex); 480 System.arraycopy(mTechHandles, techIndex + 1, mNewTechList, techIndex, 481 mTechHandles.length - techIndex - 1); 482 mTechHandles = mNewHandleList; 483 484 int[] mNewTypeList = new int[mTechLibNfcTypes.length - 1]; 485 System.arraycopy(mTechLibNfcTypes, 0, mNewTypeList, 0, techIndex); 486 System.arraycopy(mTechLibNfcTypes, techIndex + 1, mNewTypeList, techIndex, 487 mTechLibNfcTypes.length - techIndex - 1); 488 mTechLibNfcTypes = mNewTypeList; 489 } 490 } 491 } 492 493 public void addNdefFormatableTechnology(int handle, int libnfcType) { 494 synchronized (this) { 495 addTechnology(TagTechnology.NDEF_FORMATABLE, handle, libnfcType); 496 } 497 } 498 499 // This method exists to "patch in" the ndef technologies, 500 // which is done inside Java instead of the native JNI code. 501 // To not create some nasty dependencies on the order on which things 502 // are called (most notably getTechExtras()), it needs some additional 503 // checking. 504 public void addNdefTechnology(NdefMessage msg, int handle, int libnfcType, 505 int javaType, int maxLength, int cardState) { 506 synchronized (this) { 507 addTechnology(TagTechnology.NDEF, handle, libnfcType); 508 509 Bundle extras = new Bundle(); 510 extras.putParcelable(Ndef.EXTRA_NDEF_MSG, msg); 511 extras.putInt(Ndef.EXTRA_NDEF_MAXLENGTH, maxLength); 512 extras.putInt(Ndef.EXTRA_NDEF_CARDSTATE, cardState); 513 extras.putInt(Ndef.EXTRA_NDEF_TYPE, getNdefType(libnfcType, javaType)); 514 515 if (mTechExtras == null) { 516 // This will build the tech extra's for the first time, 517 // including a NULL ref for the NDEF tech we generated above. 518 Bundle[] builtTechExtras = getTechExtras(); 519 builtTechExtras[builtTechExtras.length - 1] = extras; 520 } 521 else { 522 // Tech extras were built before, patch the NDEF one in 523 Bundle[] oldTechExtras = getTechExtras(); 524 Bundle[] newTechExtras = new Bundle[oldTechExtras.length + 1]; 525 System.arraycopy(oldTechExtras, 0, newTechExtras, 0, oldTechExtras.length); 526 newTechExtras[oldTechExtras.length] = extras; 527 mTechExtras = newTechExtras; 528 } 529 530 531 } 532 } 533 534 private int getTechIndex(int tech) { 535 int techIndex = -1; 536 for (int i = 0; i < mTechList.length; i++) { 537 if (mTechList[i] == tech) { 538 techIndex = i; 539 break; 540 } 541 } 542 return techIndex; 543 } 544 545 private boolean hasTech(int tech) { 546 boolean hasTech = false; 547 for (int i = 0; i < mTechList.length; i++) { 548 if (mTechList[i] == tech) { 549 hasTech = true; 550 break; 551 } 552 } 553 return hasTech; 554 } 555 556 private boolean hasTechOnHandle(int tech, int handle) { 557 boolean hasTech = false; 558 for (int i = 0; i < mTechList.length; i++) { 559 if (mTechList[i] == tech && mTechHandles[i] == handle) { 560 hasTech = true; 561 break; 562 } 563 } 564 return hasTech; 565 566 } 567 568 private boolean isUltralightC() { 569 /* Make a best-effort attempt at classifying ULTRALIGHT 570 * vs ULTRALIGHT-C (based on NXP's public AN1303). 571 * The memory layout is as follows: 572 * Page # BYTE1 BYTE2 BYTE3 BYTE4 573 * 2 INT1 INT2 LOCK LOCK 574 * 3 OTP OTP OTP OTP (NDEF CC if NDEF-formatted) 575 * 4 DATA DATA DATA DATA (version info if factory-state) 576 * 577 * Read four blocks from page 2, which will get us both 578 * the lock page, the OTP page and the version info. 579 */ 580 boolean isUltralightC = false; 581 byte[] readCmd = { 0x30, 0x02 }; 582 int[] retCode = new int[2]; 583 byte[] respData = transceive(readCmd, false, retCode); 584 if (respData != null && respData.length == 16) { 585 // Check the lock bits (last 2 bytes in page2) 586 // and the OTP bytes (entire page 3) 587 if (respData[2] == 0 && respData[3] == 0 && respData[4] == 0 && 588 respData[5] == 0 && respData[6] == 0 && respData[7] == 0) { 589 // Very likely to be a blank card, look at version info 590 // in page 4. 591 if ((respData[8] == (byte)0x02) && respData[9] == (byte)0x00) { 592 // This is Ultralight-C 593 isUltralightC = true; 594 } else { 595 // 0xFF 0xFF would indicate Ultralight, but we also use Ultralight 596 // as a fallback if it's anything else 597 isUltralightC = false; 598 } 599 } else { 600 // See if we can find the NDEF CC in the OTP page and if it's 601 // smaller than major version two 602 if (respData[4] == (byte)0xE1 && ((respData[5] & 0xff) < 0x20)) { 603 // OK, got NDEF. Technically we'd have to search for the 604 // NDEF TLV as well. However, this would add too much 605 // time for discovery and we can make already make a good guess 606 // with the data we have here. Byte 2 of the OTP page 607 // indicates the size of the tag - 0x06 is UL, anything 608 // above indicates UL-C. 609 if ((respData[6] & 0xff) > 0x06) { 610 isUltralightC = true; 611 } 612 } else { 613 // Fall back to ultralight 614 isUltralightC = false; 615 } 616 } 617 } 618 return isUltralightC; 619 } 620 621 @Override 622 public Bundle[] getTechExtras() { 623 synchronized (this) { 624 if (mTechExtras != null) return mTechExtras; 625 mTechExtras = new Bundle[mTechList.length]; 626 for (int i = 0; i < mTechList.length; i++) { 627 Bundle extras = new Bundle(); 628 switch (mTechList[i]) { 629 case TagTechnology.NFC_A: { 630 byte[] actBytes = mTechActBytes[i]; 631 if ((actBytes != null) && (actBytes.length > 0)) { 632 extras.putShort(NfcA.EXTRA_SAK, (short) (actBytes[0] & (short) 0xFF)); 633 } else { 634 // Unfortunately Jewel doesn't have act bytes, 635 // ignore this case. 636 } 637 extras.putByteArray(NfcA.EXTRA_ATQA, mTechPollBytes[i]); 638 break; 639 } 640 641 case TagTechnology.NFC_B: { 642 // What's returned from the PN544 is actually: 643 // 4 bytes app data 644 // 3 bytes prot info 645 byte[] appData = new byte[4]; 646 byte[] protInfo = new byte[3]; 647 if (mTechPollBytes[i].length >= 7) { 648 System.arraycopy(mTechPollBytes[i], 0, appData, 0, 4); 649 System.arraycopy(mTechPollBytes[i], 4, protInfo, 0, 3); 650 651 extras.putByteArray(NfcB.EXTRA_APPDATA, appData); 652 extras.putByteArray(NfcB.EXTRA_PROTINFO, protInfo); 653 } 654 break; 655 } 656 657 case TagTechnology.NFC_F: { 658 byte[] pmm = new byte[8]; 659 byte[] sc = new byte[2]; 660 if (mTechPollBytes[i].length >= 8) { 661 // At least pmm is present 662 System.arraycopy(mTechPollBytes[i], 0, pmm, 0, 8); 663 extras.putByteArray(NfcF.EXTRA_PMM, pmm); 664 } 665 if (mTechPollBytes[i].length == 10) { 666 System.arraycopy(mTechPollBytes[i], 8, sc, 0, 2); 667 extras.putByteArray(NfcF.EXTRA_SC, sc); 668 } 669 break; 670 } 671 672 case TagTechnology.ISO_DEP: { 673 if (hasTech(TagTechnology.NFC_A)) { 674 extras.putByteArray(IsoDep.EXTRA_HIST_BYTES, mTechActBytes[i]); 675 } 676 else { 677 extras.putByteArray(IsoDep.EXTRA_HI_LAYER_RESP, mTechActBytes[i]); 678 } 679 break; 680 } 681 682 case TagTechnology.NFC_V: { 683 // First byte response flags, second byte DSFID 684 if (mTechPollBytes[i] != null && mTechPollBytes[i].length >= 2) { 685 extras.putByte(NfcV.EXTRA_RESP_FLAGS, mTechPollBytes[i][0]); 686 extras.putByte(NfcV.EXTRA_DSFID, mTechPollBytes[i][1]); 687 } 688 break; 689 } 690 691 case TagTechnology.MIFARE_ULTRALIGHT: { 692 boolean isUlc = isUltralightC(); 693 extras.putBoolean(MifareUltralight.EXTRA_IS_UL_C, isUlc); 694 break; 695 } 696 697 default: { 698 // Leave the entry in the array null 699 continue; 700 } 701 } 702 mTechExtras[i] = extras; 703 } 704 return mTechExtras; 705 } 706 } 707 708 @Override 709 public NdefMessage findAndReadNdef() { 710 // Try to find NDEF on any of the technologies. 711 int[] technologies = getTechList(); 712 int[] handles = mTechHandles; 713 NdefMessage ndefMsg = null; 714 boolean foundFormattable = false; 715 int formattableHandle = 0; 716 int formattableLibNfcType = 0; 717 int status; 718 719 for (int techIndex = 0; techIndex < technologies.length; techIndex++) { 720 // have we seen this handle before? 721 for (int i = 0; i < techIndex; i++) { 722 if (handles[i] == handles[techIndex]) { 723 continue; // don't check duplicate handles 724 } 725 } 726 727 status = connectWithStatus(technologies[techIndex]); 728 if (status != 0) { 729 Log.d(TAG, "Connect Failed - status = "+ status); 730 if (status == STATUS_CODE_TARGET_LOST) { 731 break; 732 } 733 continue; // try next handle 734 } 735 // Check if this type is NDEF formatable 736 if (!foundFormattable) { 737 if (isNdefFormatable()) { 738 foundFormattable = true; 739 formattableHandle = getConnectedHandle(); 740 formattableLibNfcType = getConnectedLibNfcType(); 741 // We'll only add formattable tech if no ndef is 742 // found - this is because libNFC refuses to format 743 // an already NDEF formatted tag. 744 } 745 reconnect(); 746 } 747 748 int[] ndefinfo = new int[2]; 749 status = checkNdefWithStatus(ndefinfo); 750 if (status != 0) { 751 Log.d(TAG, "Check NDEF Failed - status = " + status); 752 if (status == STATUS_CODE_TARGET_LOST) { 753 break; 754 } 755 continue; // try next handle 756 } 757 758 // found our NDEF handle 759 boolean generateEmptyNdef = false; 760 761 int supportedNdefLength = ndefinfo[0]; 762 int cardState = ndefinfo[1]; 763 byte[] buff = readNdef(); 764 if (buff != null) { 765 try { 766 ndefMsg = new NdefMessage(buff); 767 addNdefTechnology(ndefMsg, 768 getConnectedHandle(), 769 getConnectedLibNfcType(), 770 getConnectedTechnology(), 771 supportedNdefLength, cardState); 772 reconnect(); 773 } catch (FormatException e) { 774 // Create an intent anyway, without NDEF messages 775 generateEmptyNdef = true; 776 } 777 } else { 778 generateEmptyNdef = true; 779 } 780 781 if (generateEmptyNdef) { 782 ndefMsg = null; 783 addNdefTechnology(null, 784 getConnectedHandle(), 785 getConnectedLibNfcType(), 786 getConnectedTechnology(), 787 supportedNdefLength, cardState); 788 reconnect(); 789 } 790 break; 791 } 792 793 if (ndefMsg == null && foundFormattable) { 794 // Tag is not NDEF yet, and found a formattable target, 795 // so add formattable tech to tech list. 796 addNdefFormatableTechnology( 797 formattableHandle, 798 formattableLibNfcType); 799 } 800 801 return ndefMsg; 802 } 803 } 804