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