| /* |
| * Copyright (c) 2015, The Linux Foundation. All rights reserved. |
| * Not a Contribution. |
| * |
| * Copyright (C) 2015 NXP Semiconductors |
| * |
| * 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.gsma.nfc.internal; |
| |
| import android.content.Context; |
| import android.content.Intent; |
| |
| import java.util.List; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Map; |
| import android.content.BroadcastReceiver; |
| |
| import android.util.Log; |
| import com.nxp.nfc.gsma.internal.INxpNfcController; |
| import com.android.nfc.cardemulation.CardEmulationManager; |
| import com.android.nfc.cardemulation.RegisteredAidCache; |
| import android.nfc.cardemulation.NQApduServiceInfo; |
| import android.os.Binder; |
| import android.content.ComponentName; |
| |
| import com.android.nfc.NfcPermissions; |
| import com.android.nfc.NfcService; |
| import com.nxp.nfc.NxpConstants; |
| |
| |
| public class NxpNfcController { |
| |
| private static int ROUTING_TABLE_EE_MAX_AID_CFG_LEN = 580; |
| public static final int PN65T_ID = 2; |
| public static final int PN66T_ID = 4; |
| |
| |
| private Context mContext; |
| final NxpNfcControllerInterface mNxpNfcControllerInterface; |
| final RegisteredNxpServicesCache mServiceCache; |
| private RegisteredAidCache mRegisteredAidCache; |
| static final String TAG = "NxpNfcController"; |
| boolean DBG = true; |
| |
| public ArrayList<String> mEnabledMultiEvts = new ArrayList<String>(); |
| public final HashMap<String, Boolean> mMultiReceptionMap = new HashMap<String, Boolean>(); |
| //private NfcService mService; |
| private Object mWaitCheckCert = null; |
| private boolean mHasCert = false; |
| private Object mWaitOMACheckCert = null; |
| private boolean mHasOMACert = false; |
| |
| |
| public NxpNfcController(Context context, CardEmulationManager cardEmulationManager) { |
| mContext = context; |
| mServiceCache = cardEmulationManager.getRegisteredNxpServicesCache(); |
| mRegisteredAidCache = cardEmulationManager.getRegisteredAidCache(); |
| mNxpNfcControllerInterface = new NxpNfcControllerInterface(); |
| } |
| |
| public INxpNfcController getNxpNfcControllerInterface() { |
| if(mNxpNfcControllerInterface != null) { |
| Log.d("NxpNfcController", "GSMA: mNxpNfcControllerInterface is not Null"); |
| return mNxpNfcControllerInterface; |
| } |
| return null; |
| } |
| |
| public ArrayList<String> getEnabledMultiEvtsPackageList() { |
| return mEnabledMultiEvts; |
| } |
| |
| public void setResultForCertificates(boolean result) { |
| Log.d(TAG, "setResultForCertificates() Start"); |
| synchronized (mWaitCheckCert) { |
| if (mWaitCheckCert != null) { |
| if (result) { |
| mHasCert = true; |
| } else { |
| mHasCert = false; |
| } |
| mWaitCheckCert.notify(); |
| } |
| } |
| Log.d(TAG, "setResultForCertificates() End"); |
| } |
| |
| private boolean checkCertificatesFromUICC(String pkg, String seName) { |
| Log.d(TAG, "checkCertificatesFromUICC() " + pkg + ", " + seName); |
| Intent CertificateIntent = new Intent(); |
| CertificateIntent.setAction(NxpConstants.ACTION_CHECK_X509); |
| CertificateIntent.setPackage(NxpConstants.SET_PACKAGE_NAME); |
| CertificateIntent.putExtra(NxpConstants.EXTRA_SE_NAME, seName); |
| CertificateIntent.putExtra(NxpConstants.EXTRA_PKG, pkg); |
| mContext.sendBroadcast(CertificateIntent); |
| |
| mWaitCheckCert = new Object(); |
| mHasCert = false; |
| try { |
| synchronized (mWaitCheckCert) { |
| mWaitCheckCert.wait(1000); // timeout ms |
| } |
| } catch (InterruptedException e) { |
| Log.w(TAG, "interruped exception ."); |
| } |
| mWaitCheckCert = null; |
| |
| if (mHasCert) { |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| private boolean checkX509CertificatesFromSim (String pkg, String seName) { |
| if (DBG) Log.d(TAG, "checkX509CertificatesFromSim() " + pkg + ", " + seName); |
| |
| Intent checkCertificateIntent = new Intent(); |
| checkCertificateIntent.setAction("org.simalliance.openmobileapi.service.ACTION_CHECK_X509"); |
| checkCertificateIntent.setPackage("org.simalliance.openmobileapi.service"); |
| checkCertificateIntent.putExtra("org.simalliance.openmobileapi.service.EXTRA_SE_NAME", seName); |
| checkCertificateIntent.putExtra("org.simalliance.openmobileapi.service.EXTRA_PKG", pkg); |
| mContext.sendBroadcast(checkCertificateIntent); |
| |
| mWaitOMACheckCert = new Object(); |
| mHasOMACert = false; |
| try { |
| synchronized (mWaitOMACheckCert) { |
| mWaitOMACheckCert.wait(1000); //add timeout ms |
| } |
| } catch (InterruptedException e) { |
| // Should not happen; fall-through to abort. |
| Log.w(TAG, "interruped."); |
| } |
| mWaitOMACheckCert = null; |
| |
| if (mHasOMACert) { |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| public void setResultForX509Certificates(boolean result) { |
| Log.d(TAG, "setResultForX509Certificates() Start"); |
| synchronized (mWaitOMACheckCert) { |
| if (mWaitOMACheckCert != null) { |
| if (result) { |
| mHasOMACert = true; |
| } else { |
| mHasOMACert = false; |
| } |
| mWaitOMACheckCert.notify(); |
| } |
| } |
| Log.d(TAG, "setResultForX509Certificates() End"); |
| } |
| |
| static byte[] hexStringToBytes(String s) { |
| if (s == null || s.length() == 0) return null; |
| int len = s.length(); |
| if (len % 2 != 0) { |
| s = '0' + s; |
| len++; |
| } |
| byte[] data = new byte[len / 2]; |
| for (int i = 0; i < len; i += 2) { |
| data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) |
| + Character.digit(s.charAt(i + 1), 16)); |
| } |
| return data; |
| } |
| |
| final class NxpNfcControllerInterface extends INxpNfcController.Stub { |
| |
| @Override |
| public boolean deleteOffHostService(int userId, String packageName, NQApduServiceInfo service) { |
| return mServiceCache.deleteApduService(userId, Binder.getCallingUid(), packageName, service); |
| } |
| |
| @Override |
| public ArrayList<NQApduServiceInfo> getOffHostServices(int userId, String packageName) { |
| return mServiceCache.getApduServices(userId, Binder.getCallingUid(), packageName); |
| } |
| |
| @Override |
| public NQApduServiceInfo getDefaultOffHostService(int userId, String packageName) { |
| HashMap<ComponentName, NQApduServiceInfo> mapServices = mServiceCache.getApduservicesMaps(); |
| ComponentName preferredPaymentService = mRegisteredAidCache.getPreferredPaymentService(); |
| if(preferredPaymentService != null) { |
| String defaultservice = preferredPaymentService.getClassName(); |
| |
| //If Default is Dynamic Service |
| for (Map.Entry<ComponentName, NQApduServiceInfo> entry : mapServices.entrySet()) |
| { |
| if(defaultservice.equals(entry.getKey().getClassName())) { |
| Log.d(TAG, "getDefaultOffHostService: Dynamic: "+ entry.getValue().getAids().size()); |
| return entry.getValue(); |
| } |
| } |
| |
| //If Default is Static Service |
| HashMap<ComponentName, NQApduServiceInfo> staticServices = mServiceCache.getInstalledStaticServices(); |
| for (Map.Entry<ComponentName, NQApduServiceInfo> entry : staticServices.entrySet()) { |
| if(defaultservice.equals(entry.getKey().getClassName())) { |
| Log.d(TAG, "getDefaultOffHostService: Static: "+ entry.getValue().getAids().size()); |
| return entry.getValue(); |
| } |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| public boolean commitOffHostService(int userId, String packageName, String serviceName, NQApduServiceInfo service) { |
| int aidLength = 0; |
| boolean is_table_size_required = true; |
| List<String> newAidList = new ArrayList<String>(); |
| List<String> oldAidList = new ArrayList<String>(); |
| |
| for (int i=0; i<service.getAids().size(); i++){ // Convering String AIDs to Aids Length |
| aidLength = aidLength + hexStringToBytes(service.getAids().get(i)).length; |
| } |
| Log.d(TAG, "Total commiting aids Length: "+ aidLength); |
| |
| ArrayList<NQApduServiceInfo> serviceList = mServiceCache.getApduServices(userId, Binder.getCallingUid(), packageName); |
| for(int i=0; i< serviceList.size(); i++) { |
| Log.d(TAG, "All Service Names["+i +"] "+ serviceList.get(i).getComponent().getClassName()); |
| if(serviceName.equalsIgnoreCase(serviceList.get(i).getComponent().getClassName())) { |
| oldAidList = serviceList.get(i).getAids(); |
| newAidList = service.getAids(); |
| Log.d(TAG, "Commiting Existing Service: "+ serviceName); |
| break; |
| } |
| } |
| |
| int newAidListSize; |
| for(newAidListSize = 0; newAidListSize < newAidList.size(); newAidListSize++) { |
| if(!oldAidList.contains(newAidList.get(newAidListSize))) { |
| is_table_size_required = true; // Need to calculate Roting table Size, if New Aids Added |
| Log.d(TAG, "New Aids Added "); |
| break; |
| } |
| } |
| |
| if((newAidList.size() != 0) && (newAidListSize == newAidList.size())) { |
| is_table_size_required = false; // No Need to calculate Routing size |
| } |
| |
| Log.d(TAG, "is routing Table size calcution required : "+ is_table_size_required); |
| if((is_table_size_required == true) && NfcService.getInstance().getRemainingAidTableSize() < aidLength) { |
| return false; |
| } |
| |
| Log.d(TAG, "Commiting : "); |
| return mServiceCache.registerApduService(userId, Binder.getCallingUid(), packageName, serviceName, service); |
| } |
| |
| @Override |
| public boolean enableMultiEvt_NxptransactionReception(String packageName, String seName) { |
| boolean result = false; |
| if(checkCertificatesFromUICC(packageName, seName) == true) { |
| mEnabledMultiEvts.add(packageName); |
| result = true; |
| } else { |
| result = false; |
| } |
| |
| return result; |
| } |
| |
| @Override |
| public void enableMultiReception(String pkg, String seName) { |
| if (DBG) Log.d(TAG, "enableMultiReception() " + pkg + " " + seName); |
| |
| if (seName.startsWith("SIM")) { |
| if (checkX509CertificatesFromSim (pkg, seName) == false) { |
| throw new SecurityException("No cerficates from " + seName); |
| } |
| } else { |
| NfcService.getInstance().enforceNfceeAdminPerm(pkg); |
| //NfcPermissions.enforceAdminPermissions(mContext); |
| } |
| |
| mMultiReceptionMap.remove(seName); |
| mMultiReceptionMap.put(seName, Boolean.TRUE); |
| } |
| } |
| } |