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