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