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