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