/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.nfc.nxp; import com.android.nfc.DeviceHost; import com.android.nfc.LlcpException; import com.android.nfc.NfcService; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.content.Context; import android.content.SharedPreferences; import android.nfc.ErrorCodes; import android.nfc.tech.Ndef; import android.nfc.tech.TagTechnology; import android.util.Log; import java.io.File; /** * Native interface to the NFC Manager functions */ public class NativeNfcManager implements DeviceHost { private static final String TAG = "NativeNfcManager"; private static final String NFC_CONTROLLER_FIRMWARE_FILE_NAME = "/vendor/firmware/libpn544_fw.so"; private static final String PREF = "NxpDeviceHost"; private static final String PREF_FIRMWARE_MODTIME = "firmware_modtime"; private static final long FIRMWARE_MODTIME_DEFAULT = -1; static { System.loadLibrary("nfc_jni"); } @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String INTERNAL_TARGET_DESELECTED_ACTION = "com.android.nfc.action.INTERNAL_TARGET_DESELECTED"; /* Native structure */ private int mNative; private final DeviceHostListener mListener; private final Context mContext; public NativeNfcManager(Context context, DeviceHostListener listener) { mListener = listener; initializeNativeStructure(); mContext = context; } public native boolean initializeNativeStructure(); private native boolean doDownload(); @Override public void checkFirmware() { // Check that the NFC controller firmware is up to date. This // ensures that firmware updates are applied in a timely fashion, // and makes it much less likely that the user will have to wait // for a firmware download when they enable NFC in the settings // app. Firmware download can take some time, so this should be // run in a separate thread. // check the timestamp of the firmware file File firmwareFile; int nbRetry = 0; try { firmwareFile = new File(NFC_CONTROLLER_FIRMWARE_FILE_NAME); } catch(NullPointerException npe) { Log.e(TAG,"path to firmware file was null"); return; } long modtime = firmwareFile.lastModified(); SharedPreferences prefs = mContext.getSharedPreferences(PREF, Context.MODE_PRIVATE); long prev_fw_modtime = prefs.getLong(PREF_FIRMWARE_MODTIME, FIRMWARE_MODTIME_DEFAULT); Log.d(TAG,"prev modtime: " + prev_fw_modtime); Log.d(TAG,"new modtime: " + modtime); if (prev_fw_modtime == modtime) { return; } // FW download. while(nbRetry < 5) { Log.d(TAG,"Perform Download"); if(doDownload()) { Log.d(TAG,"Download Success"); // Now that we've finished updating the firmware, save the new modtime. prefs.edit().putLong(PREF_FIRMWARE_MODTIME, modtime).apply(); break; } else { Log.d(TAG,"Download Failed"); nbRetry++; } } } @Override public native boolean initialize(); @Override public native boolean deinitialize(); @Override public native void enableDiscovery(); @Override public native void disableDiscovery(); @Override public native int[] doGetSecureElementList(); @Override public native void doSelectSecureElement(); @Override public native void doDeselectSecureElement(); @Override public native int doGetLastError(); private native NativeLlcpConnectionlessSocket doCreateLlcpConnectionlessSocket(int nSap); private native NativeLlcpServiceSocket doCreateLlcpServiceSocket(int nSap, String sn, int miu, int rw, int linearBufferLength); @Override public LlcpServerSocket createLlcpServerSocket(int nSap, String sn, int miu, int rw, int linearBufferLength) throws LlcpException { LlcpServerSocket socket = doCreateLlcpServiceSocket(nSap, sn, miu, rw, linearBufferLength); if (socket != null) { return socket; } else { /* Get Error Status */ int error = doGetLastError(); Log.d(TAG, "failed to create llcp socket: " + ErrorCodes.asString(error)); switch (error) { case ErrorCodes.ERROR_BUFFER_TO_SMALL: case ErrorCodes.ERROR_INSUFFICIENT_RESOURCES: throw new LlcpException(error); default: throw new LlcpException(ErrorCodes.ERROR_SOCKET_CREATION); } } } private native NativeLlcpSocket doCreateLlcpSocket(int sap, int miu, int rw, int linearBufferLength); @Override public LlcpSocket createLlcpSocket(int sap, int miu, int rw, int linearBufferLength) throws LlcpException { LlcpSocket socket = doCreateLlcpSocket(sap, miu, rw, linearBufferLength); if (socket != null) { return socket; } else { /* Get Error Status */ int error = doGetLastError(); Log.d(TAG, "failed to create llcp socket: " + ErrorCodes.asString(error)); switch (error) { case ErrorCodes.ERROR_BUFFER_TO_SMALL: case ErrorCodes.ERROR_INSUFFICIENT_RESOURCES: throw new LlcpException(error); default: throw new LlcpException(ErrorCodes.ERROR_SOCKET_CREATION); } } } @Override public native boolean doCheckLlcp(); @Override public native boolean doActivateLlcp(); private native void doResetTimeouts(); @Override public void resetTimeouts() { doResetTimeouts(); } public native void doAbort(); private native boolean doSetTimeout(int tech, int timeout); @Override public boolean setTimeout(int tech, int timeout) { return doSetTimeout(tech, timeout); } private native int doGetTimeout(int tech); @Override public int getTimeout(int tech) { return doGetTimeout(tech); } @Override public boolean canMakeReadOnly(int ndefType) { return (ndefType == Ndef.TYPE_1 || ndefType == Ndef.TYPE_2); } @Override public int getMaxTransceiveLength(int technology) { switch (technology) { case (TagTechnology.NFC_A): case (TagTechnology.MIFARE_CLASSIC): case (TagTechnology.MIFARE_ULTRALIGHT): return 253; // PN544 RF buffer = 255 bytes, subtract two for CRC case (TagTechnology.NFC_B): return 0; // PN544 does not support transceive of raw NfcB case (TagTechnology.NFC_V): return 253; // PN544 RF buffer = 255 bytes, subtract two for CRC case (TagTechnology.ISO_DEP): /* The maximum length of a normal IsoDep frame consists of: * CLA, INS, P1, P2, LC, LE + 255 payload bytes = 261 bytes * such a frame is supported. Extended length frames however * are not supported. */ return 261; // Will be automatically split in two frames on the RF layer case (TagTechnology.NFC_F): return 252; // PN544 RF buffer = 255 bytes, subtract one for SoD, two for CRC default: return 0; } } private native String doDump(); @Override public String dump() { return doDump(); } /** * Notifies Ndef Message (TODO: rename into notifyTargetDiscovered) */ private void notifyNdefMessageListeners(NativeNfcTag tag) { mListener.onRemoteEndpointDiscovered(tag); } /** * Notifies transaction */ private void notifyTargetDeselected() { mListener.onCardEmulationDeselected(); } /** * Notifies transaction */ private void notifyTransactionListeners(byte[] aid) { mListener.onCardEmulationAidSelected(aid); } /** * Notifies P2P Device detected, to activate LLCP link */ private void notifyLlcpLinkActivation(NativeP2pDevice device) { mListener.onLlcpLinkActivated(device); } /** * Notifies P2P Device detected, to activate LLCP link */ private void notifyLlcpLinkDeactivated(NativeP2pDevice device) { mListener.onLlcpLinkDeactivated(device); } private void notifySeFieldActivated() { mListener.onRemoteFieldActivated(); } private void notifySeFieldDeactivated() { mListener.onRemoteFieldDeactivated(); } private void notifySeApduReceived(byte[] apdu) { mListener.onSeApduReceived(apdu); } private void notifySeEmvCardRemoval() { mListener.onSeEmvCardRemoval(); } private void notifySeMifareAccess(byte[] block) { mListener.onSeMifareAccess(block); } }