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