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