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