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