19f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen/* 29f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen * Copyright (C) 2013 The Android Open Source Project 39f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen * 49f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen * Licensed under the Apache License, Version 2.0 (the "License"); 59f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen * you may not use this file except in compliance with the License. 69f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen * You may obtain a copy of the License at 79f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen * 89f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen * http://www.apache.org/licenses/LICENSE-2.0 99f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen * 109f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen * Unless required by applicable law or agreed to in writing, software 119f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen * distributed under the License is distributed on an "AS IS" BASIS, 129f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 139f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen * See the License for the specific language governing permissions and 149f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen * limitations under the License. 159f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen */ 169f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 179f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenpackage com.android.nfc.cardemulation; 189f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 19a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenenimport android.app.ActivityManager; 20451ba48faa87d78bfbec0597ff06af1747cf6acbMartijn Coenenimport android.app.KeyguardManager; 219f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport android.content.ComponentName; 229f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport android.content.Context; 239f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport android.content.Intent; 249f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport android.content.ServiceConnection; 2589c893312d524f50c47b0d32071f3de8631197f3Martijn Coenenimport android.database.ContentObserver; 2689c893312d524f50c47b0d32071f3de8631197f3Martijn Coenenimport android.net.Uri; 27a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenenimport android.nfc.cardemulation.ApduServiceInfo; 28f5cd84c3a7ffb66196ab3c0745569da937d7533bMartijn Coenenimport android.nfc.cardemulation.CardEmulation; 296493859b424f65af79e3e13835f7dfed38495c00Martijn Coenenimport android.nfc.cardemulation.HostApduService; 309f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport android.os.Bundle; 319f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport android.os.Handler; 329f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport android.os.IBinder; 3389c893312d524f50c47b0d32071f3de8631197f3Martijn Coenenimport android.os.Looper; 349f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport android.os.Message; 359f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport android.os.Messenger; 369f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport android.os.RemoteException; 37a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenenimport android.os.UserHandle; 3889c893312d524f50c47b0d32071f3de8631197f3Martijn Coenenimport android.provider.Settings; 399f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport android.util.Log; 409f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport com.android.nfc.NfcService; 4178976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenenimport com.android.nfc.cardemulation.RegisteredAidCache.AidResolveInfo; 429f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 439f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport java.util.ArrayList; 449f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 459f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenpublic class HostEmulationManager { 469f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen static final String TAG = "HostEmulationManager"; 47f9366de7b3ad1ce90859d4194dacf13c0066926bMartijn Coenen static final boolean DBG = false; 489f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 499f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen static final int STATE_IDLE = 0; 509f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen static final int STATE_W4_SELECT = 1; 519f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen static final int STATE_W4_SERVICE = 2; 5289c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen static final int STATE_W4_DEACTIVATE = 3; 5389c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen static final int STATE_XFER = 4; 5489c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen 55f9366de7b3ad1ce90859d4194dacf13c0066926bMartijn Coenen /** Minimum AID lenth as per ISO7816 */ 56f9366de7b3ad1ce90859d4194dacf13c0066926bMartijn Coenen static final int MINIMUM_AID_LENGTH = 5; 57f9366de7b3ad1ce90859d4194dacf13c0066926bMartijn Coenen 58f9366de7b3ad1ce90859d4194dacf13c0066926bMartijn Coenen /** Length of Select APDU header including length byte */ 59f9366de7b3ad1ce90859d4194dacf13c0066926bMartijn Coenen static final int SELECT_APDU_HDR_LENGTH = 5; 60f9366de7b3ad1ce90859d4194dacf13c0066926bMartijn Coenen 61f9366de7b3ad1ce90859d4194dacf13c0066926bMartijn Coenen static final byte INSTR_SELECT = (byte)0xA4; 62f9366de7b3ad1ce90859d4194dacf13c0066926bMartijn Coenen 63dba891595bc0bbcb8f0810b4d014e935aa1955c5Martijn Coenen static final String ANDROID_HCE_AID = "A000000476416E64726F6964484345"; 64dba891595bc0bbcb8f0810b4d014e935aa1955c5Martijn Coenen static final byte[] ANDROID_HCE_RESPONSE = {0x14, (byte)0x81, 0x00, 0x00, (byte)0x90, 0x00}; 65dba891595bc0bbcb8f0810b4d014e935aa1955c5Martijn Coenen 6689c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen static final byte[] AID_NOT_FOUND = {0x6A, (byte)0x82}; 6778976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen static final byte[] UNKNOWN_ERROR = {0x6F, 0x00}; 689f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 699f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen final Context mContext; 7078976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen final RegisteredAidCache mAidCache; 719f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen final Messenger mMessenger = new Messenger (new MessageHandler()); 72451ba48faa87d78bfbec0597ff06af1747cf6acbMartijn Coenen final KeyguardManager mKeyguard; 739f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen final Object mLock; 749f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 7589c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen // All variables below protected by mLock 7689c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen 7789c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen /** Whether we need to clear the "next tap" service at the end 7889c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen * of this transaction. 7989c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen */ 8075f63db568f953e935e62cb3046d167b881979c8Martijn Coenen boolean mClearNextTapDefault; 8189c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen 8289c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen // Variables below are for a non-payment service, 8389c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen // that is typically only bound in the STATE_XFER state. 849f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen Messenger mService; 8589c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen boolean mServiceBound; 8689c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen ComponentName mServiceName; 8789c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen 8889c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen // Variables below are for a payment service, 8989c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen // which is typically bound persistently to improve on 9089c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen // latency. 9189c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen Messenger mPaymentService; 9289c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen boolean mPaymentServiceBound; 9389c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen ComponentName mPaymentServiceName; 9489c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen 9589c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen // mActiveService denotes the service interface 9689c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen // that is the current active one, until a new SELECT AID 9789c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen // comes in that may be resolved to a different service. 9889c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen // On deactivation, mActiveService stops being valid. 9989c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen Messenger mActiveService; 10078976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen ComponentName mActiveServiceName; 10189c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen 10278976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen String mLastSelectedAid; 1039f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen int mState; 1049f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen byte[] mSelectApdu; 1059f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 10678976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen public HostEmulationManager(Context context, RegisteredAidCache aidCache) { 1079f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen mContext = context; 1089f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen mLock = new Object(); 1099f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen mAidCache = aidCache; 1109f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen mState = STATE_IDLE; 111451ba48faa87d78bfbec0597ff06af1747cf6acbMartijn Coenen mKeyguard = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE); 11289c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen SettingsObserver observer = new SettingsObserver(mHandler); 11389c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen context.getContentResolver().registerContentObserver( 11489c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen Settings.Secure.getUriFor(Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT), 11589c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen true, observer, UserHandle.USER_ALL); 11689c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen 11789c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen // Bind to payment default if existing 11889c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen int userId = ActivityManager.getCurrentUser(); 11989c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen String name = Settings.Secure.getStringForUser( 12089c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen mContext.getContentResolver(), Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT, 12189c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen userId); 12289c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen if (name != null) { 12389c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen ComponentName serviceComponent = ComponentName.unflattenFromString(name); 12489c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen bindPaymentServiceLocked(userId, serviceComponent); 12589c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen } 12689c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen } 12789c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen 12889c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen public void setDefaultForNextTap(ComponentName service) { 12989c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen synchronized (mLock) { 13089c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen if (service != null) { 13189c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen bindServiceIfNeededLocked(service); 13289c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen } else { 13389c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen unbindServiceIfNeededLocked(); 13489c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen } 13589c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen } 1369f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 1379f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 1389f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen public void notifyHostEmulationActivated() { 1399f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen Log.d(TAG, "notifyHostEmulationActivated"); 1409f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen synchronized (mLock) { 14175f63db568f953e935e62cb3046d167b881979c8Martijn Coenen mClearNextTapDefault = mAidCache.isNextTapOverriden(); 14289c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen // Regardless of what happens, if we're having a tap again 14389c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen // activity up, close it 14489c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen Intent intent = new Intent(TapAgainDialog.ACTION_CLOSE); 14589c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen intent.setPackage("com.android.nfc"); 14689c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen mContext.sendBroadcastAsUser(intent, UserHandle.ALL); 1479f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen if (mState != STATE_IDLE) { 1489f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen Log.e(TAG, "Got activation event in non-idle state"); 1499f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 1509f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen mState = STATE_W4_SELECT; 1519f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 1529f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 1539f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 1549f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen public void notifyHostEmulationData(byte[] data) { 1559f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen Log.d(TAG, "notifyHostEmulationData"); 1569f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen String selectAid = findSelectAid(data); 15789c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen ComponentName resolvedService = null; 1589f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen synchronized (mLock) { 15989c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen if (mState == STATE_IDLE) { 16089c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen Log.e(TAG, "Got data in idle state."); 16189c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen return; 16289c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen } else if (mState == STATE_W4_DEACTIVATE) { 16389c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen Log.e(TAG, "Dropping APDU in STATE_W4_DECTIVATE"); 16489c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen return; 16589c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen } 16689c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen if (selectAid != null) { 167dba891595bc0bbcb8f0810b4d014e935aa1955c5Martijn Coenen if (selectAid.equals(ANDROID_HCE_AID)) { 168dba891595bc0bbcb8f0810b4d014e935aa1955c5Martijn Coenen NfcService.getInstance().sendData(ANDROID_HCE_RESPONSE); 169dba891595bc0bbcb8f0810b4d014e935aa1955c5Martijn Coenen return; 170dba891595bc0bbcb8f0810b4d014e935aa1955c5Martijn Coenen } 17178976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen AidResolveInfo resolveInfo = mAidCache.resolveAidPrefix(selectAid); 17278976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen if (resolveInfo == null || resolveInfo.services.size() == 0) { 17389c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen // Tell the remote we don't handle this AID 17489c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen NfcService.getInstance().sendData(AID_NOT_FOUND); 17589c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen return; 17678976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen } 17778976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen mLastSelectedAid = resolveInfo.aid; 17878976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen if (resolveInfo.defaultService != null) { 17978976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen // Resolve to default 180451ba48faa87d78bfbec0597ff06af1747cf6acbMartijn Coenen // Check if resolvedService requires unlock 181451ba48faa87d78bfbec0597ff06af1747cf6acbMartijn Coenen if (resolveInfo.defaultService.requiresUnlock() && 182451ba48faa87d78bfbec0597ff06af1747cf6acbMartijn Coenen mKeyguard.isKeyguardLocked() && mKeyguard.isKeyguardSecure()) { 183451ba48faa87d78bfbec0597ff06af1747cf6acbMartijn Coenen String category = mAidCache.getCategoryForAid(resolveInfo.aid); 184451ba48faa87d78bfbec0597ff06af1747cf6acbMartijn Coenen // Just ignore all future APDUs until next tap 185451ba48faa87d78bfbec0597ff06af1747cf6acbMartijn Coenen mState = STATE_W4_DEACTIVATE; 186a3b2c7944266cbd02afc08ce48ce8259d8a65019Martijn Coenen launchTapAgain(resolveInfo.defaultService, category); 187451ba48faa87d78bfbec0597ff06af1747cf6acbMartijn Coenen return; 188451ba48faa87d78bfbec0597ff06af1747cf6acbMartijn Coenen } 189dba891595bc0bbcb8f0810b4d014e935aa1955c5Martijn Coenen // In no circumstance should this be an OffHostService - 190dba891595bc0bbcb8f0810b4d014e935aa1955c5Martijn Coenen // we should never get this AID on the host in the first place 191dba891595bc0bbcb8f0810b4d014e935aa1955c5Martijn Coenen if (!resolveInfo.defaultService.isOnHost()) { 192dba891595bc0bbcb8f0810b4d014e935aa1955c5Martijn Coenen Log.e(TAG, "AID that was meant to go off-host was routed to host." + 193dba891595bc0bbcb8f0810b4d014e935aa1955c5Martijn Coenen " Check routing table configuration."); 194dba891595bc0bbcb8f0810b4d014e935aa1955c5Martijn Coenen NfcService.getInstance().sendData(AID_NOT_FOUND); 195dba891595bc0bbcb8f0810b4d014e935aa1955c5Martijn Coenen return; 196dba891595bc0bbcb8f0810b4d014e935aa1955c5Martijn Coenen } 197a3b2c7944266cbd02afc08ce48ce8259d8a65019Martijn Coenen resolvedService = resolveInfo.defaultService.getComponent(); 198a3b2c7944266cbd02afc08ce48ce8259d8a65019Martijn Coenen } else if (mActiveServiceName != null) { 199a3b2c7944266cbd02afc08ce48ce8259d8a65019Martijn Coenen for (ApduServiceInfo service : resolveInfo.services) { 200a3b2c7944266cbd02afc08ce48ce8259d8a65019Martijn Coenen if (mActiveServiceName.equals(service.getComponent())) { 201a3b2c7944266cbd02afc08ce48ce8259d8a65019Martijn Coenen resolvedService = mActiveServiceName; 202a3b2c7944266cbd02afc08ce48ce8259d8a65019Martijn Coenen break; 203a3b2c7944266cbd02afc08ce48ce8259d8a65019Martijn Coenen } 204a3b2c7944266cbd02afc08ce48ce8259d8a65019Martijn Coenen } 205a3b2c7944266cbd02afc08ce48ce8259d8a65019Martijn Coenen } 206a3b2c7944266cbd02afc08ce48ce8259d8a65019Martijn Coenen if (resolvedService == null) { 20778976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen // We have no default, and either one or more services. 20878976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen // Ask the user to confirm. 20989c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen // Get corresponding category 21078976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen String category = mAidCache.getCategoryForAid(resolveInfo.aid); 21189c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen // Just ignore all future APDUs until we resolve to only one 21289c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen mState = STATE_W4_DEACTIVATE; 213dc304dadda013e383fdd1348b2a24bc6b3c9acacMartijn Coenen launchResolver((ArrayList<ApduServiceInfo>)resolveInfo.services, null, category); 21489c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen return; 21589c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen } 21689c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen } 2179f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen switch (mState) { 2189f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen case STATE_W4_SELECT: 2199f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen if (selectAid != null) { 22089c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen Messenger existingService = bindServiceIfNeededLocked(resolvedService); 22189c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen if (existingService != null) { 22289c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen Log.d(TAG, "Binding to existing service"); 22389c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen mState = STATE_XFER; 22489c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen sendDataToServiceLocked(existingService, data); 22589c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen } else { 22689c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen // Waiting for service to be bound 22789c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen Log.d(TAG, "Waiting for new service."); 22889c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen // Queue SELECT APDU to be used 22989c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen mSelectApdu = data; 2309f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen mState = STATE_W4_SERVICE; 23189c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen } 2329f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } else { 2339f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen Log.d(TAG, "Dropping non-select APDU in STATE_W4_SELECT"); 23478976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen NfcService.getInstance().sendData(UNKNOWN_ERROR); 2359f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 2369f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen break; 2379f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen case STATE_W4_SERVICE: 23889c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen Log.d(TAG, "Unexpected APDU in STATE_W4_SERVICE"); 2399f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen break; 2409f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen case STATE_XFER: 24189c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen if (selectAid != null) { 24289c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen Messenger existingService = bindServiceIfNeededLocked(resolvedService); 24389c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen if (existingService != null) { 24489c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen sendDataToServiceLocked(existingService, data); 24589c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen mState = STATE_XFER; 24689c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen } else { 24789c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen // Waiting for service to be bound 248ac91fe83c263fea3a3e38cf7fee54697259999f8Martijn Coenen mSelectApdu = data; 24989c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen mState = STATE_W4_SERVICE; 2509f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 25189c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen } else if (mActiveService != null) { 25289c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen // Regular APDU data 25389c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen sendDataToServiceLocked(mActiveService, data); 2549f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } else { 25589c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen // No SELECT AID and no active service. 2569f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen Log.d(TAG, "Service no longer bound, dropping APDU"); 2579f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 2589f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen break; 2599f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 2609f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 2619f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 2629f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 2639f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen public void notifyNostEmulationDeactivated() { 2649f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen Log.d(TAG, "notifyHostEmulationDeactivated"); 2659f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen synchronized (mLock) { 26689c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen if (mState == STATE_IDLE) { 26789c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen Log.e(TAG, "Got deactivation event while in idle state"); 26889c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen } 26975f63db568f953e935e62cb3046d167b881979c8Martijn Coenen if (mClearNextTapDefault) { 27075f63db568f953e935e62cb3046d167b881979c8Martijn Coenen mAidCache.setDefaultForNextTap(ActivityManager.getCurrentUser(), null); 27175f63db568f953e935e62cb3046d167b881979c8Martijn Coenen } 27289c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen sendDeactivateToActiveServiceLocked(HostApduService.DEACTIVATION_LINK_LOSS); 27389c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen mActiveService = null; 27478976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen mActiveServiceName = null; 27589c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen unbindServiceIfNeededLocked(); 27689c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen mState = STATE_IDLE; 27789c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen } 27889c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen } 27989c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen 280f9366de7b3ad1ce90859d4194dacf13c0066926bMartijn Coenen public void notifyOffHostAidSelected() { 281f9366de7b3ad1ce90859d4194dacf13c0066926bMartijn Coenen Log.d(TAG, "notifyOffHostAidSelected"); 282f9366de7b3ad1ce90859d4194dacf13c0066926bMartijn Coenen synchronized (mLock) { 283f9366de7b3ad1ce90859d4194dacf13c0066926bMartijn Coenen if (mState != STATE_XFER || mActiveService == null) { 284f9366de7b3ad1ce90859d4194dacf13c0066926bMartijn Coenen // Don't bother telling, we're not bound to any service yet 285f9366de7b3ad1ce90859d4194dacf13c0066926bMartijn Coenen } else { 286f9366de7b3ad1ce90859d4194dacf13c0066926bMartijn Coenen sendDeactivateToActiveServiceLocked(HostApduService.DEACTIVATION_DESELECTED); 287f9366de7b3ad1ce90859d4194dacf13c0066926bMartijn Coenen } 288f9366de7b3ad1ce90859d4194dacf13c0066926bMartijn Coenen mActiveService = null; 289f9366de7b3ad1ce90859d4194dacf13c0066926bMartijn Coenen mActiveServiceName = null; 290f9366de7b3ad1ce90859d4194dacf13c0066926bMartijn Coenen unbindServiceIfNeededLocked(); 291f9366de7b3ad1ce90859d4194dacf13c0066926bMartijn Coenen mState = STATE_W4_SELECT; 292f9366de7b3ad1ce90859d4194dacf13c0066926bMartijn Coenen } 293f9366de7b3ad1ce90859d4194dacf13c0066926bMartijn Coenen } 294f9366de7b3ad1ce90859d4194dacf13c0066926bMartijn Coenen 29589c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen Messenger bindServiceIfNeededLocked(ComponentName service) { 29689c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen if (mPaymentServiceBound && mPaymentServiceName.equals(service)) { 29789c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen Log.d(TAG, "Service already bound as payment service."); 29889c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen return mPaymentService; 29989c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen } else if (mServiceBound && mServiceName.equals(service)) { 30089c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen Log.d(TAG, "Service already bound as regular service."); 30189c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen return mService; 30289c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen } else { 30389c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen Log.d(TAG, "Binding to service " + service); 30489c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen unbindServiceIfNeededLocked(); 30589c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen Intent aidIntent = new Intent(HostApduService.SERVICE_INTERFACE); 30689c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen aidIntent.setComponent(service); 30789c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen if (mContext.bindServiceAsUser(aidIntent, mConnection, 30889c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen Context.BIND_AUTO_CREATE, UserHandle.CURRENT)) { 30989c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen } else { 3108d57f1f5bd4ab7fba3c401d7e02fb46a54d3b931Martijn Coenen Log.e(TAG, "Could not bind service."); 31189c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen } 31289c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen return null; 31389c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen } 31489c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen } 31589c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen 31689c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen void sendDataToServiceLocked(Messenger service, byte[] data) { 31789c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen if (service != mActiveService) { 31889c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen sendDeactivateToActiveServiceLocked(HostApduService.DEACTIVATION_DESELECTED); 31989c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen mActiveService = service; 32078976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen if (service.equals(mPaymentService)) { 32178976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen mActiveServiceName = mPaymentServiceName; 32278976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen } else { 32378976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen mActiveServiceName = mServiceName; 32478976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen } 32589c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen } 32689c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen Message msg = Message.obtain(null, HostApduService.MSG_COMMAND_APDU); 32789c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen Bundle dataBundle = new Bundle(); 32889c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen dataBundle.putByteArray("data", data); 32989c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen msg.setData(dataBundle); 33089c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen msg.replyTo = mMessenger; 33189c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen try { 33289c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen mActiveService.send(msg); 33389c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen } catch (RemoteException e) { 33489c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen Log.e(TAG, "Remote service has died, dropping APDU"); 33589c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen } 33689c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen } 33789c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen 33889c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen void sendDeactivateToActiveServiceLocked(int reason) { 33989c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen if (mActiveService == null) return; 34089c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen Message msg = Message.obtain(null, HostApduService.MSG_DEACTIVATED); 34189c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen msg.arg1 = reason; 34289c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen try { 34389c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen mActiveService.send(msg); 34489c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen } catch (RemoteException e) { 34589c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen // Don't care 34689c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen } 34789c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen } 34889c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen 34989c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen final Handler mHandler = new Handler(Looper.getMainLooper()); 35089c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen 35189c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen private final class SettingsObserver extends ContentObserver { 35289c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen public SettingsObserver(Handler handler) { 35389c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen super(handler); 35489c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen } 35589c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen 35689c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen @Override 35789c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen public void onChange(boolean selfChange, Uri uri) { 35889c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen super.onChange(selfChange, uri); 35989c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen synchronized (mLock) { 36089c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen // Do it just for the current user. If it was in fact 36189c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen // a change made for another user, we'll sync it down 36289c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen // on user switch. 36389c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen int userId = ActivityManager.getCurrentUser(); 36478976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen ComponentName paymentApp = mAidCache.getDefaultServiceForCategory(userId, 365f5cd84c3a7ffb66196ab3c0745569da937d7533bMartijn Coenen CardEmulation.CATEGORY_PAYMENT, true); 36689c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen if (paymentApp != null) { 36778976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen bindPaymentServiceLocked(userId, paymentApp); 36889c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen } else { 36989c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen unbindPaymentServiceLocked(userId); 3709f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 3719f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 37289c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen } 37389c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen }; 37489c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen 37589c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen void unbindPaymentServiceLocked(int userId) { 37689c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen if (mPaymentServiceBound) { 37789c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen mContext.unbindService(mPaymentConnection); 37889c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen mPaymentServiceBound = false; 37989c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen mPaymentService = null; 38089c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen mPaymentServiceName = null; 3819f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 3829f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 3839f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 38489c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen void bindPaymentServiceLocked(int userId, ComponentName service) { 38589c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen unbindPaymentServiceLocked(userId); 38689c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen 38789c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen Intent intent = new Intent(HostApduService.SERVICE_INTERFACE); 38889c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen intent.setComponent(service); 389f5cd84c3a7ffb66196ab3c0745569da937d7533bMartijn Coenen if (!mContext.bindServiceAsUser(intent, mPaymentConnection, 390f5cd84c3a7ffb66196ab3c0745569da937d7533bMartijn Coenen Context.BIND_AUTO_CREATE, new UserHandle(userId))) { 391f9366de7b3ad1ce90859d4194dacf13c0066926bMartijn Coenen Log.e(TAG, "Could not bind (persistent) payment service."); 392f5cd84c3a7ffb66196ab3c0745569da937d7533bMartijn Coenen } 39389c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen } 39489c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen 39589c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen void unbindServiceIfNeededLocked() { 39689c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen if (mServiceBound) { 39789c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen Log.d(TAG, "Unbinding from service " + mServiceName); 39889c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen mContext.unbindService(mConnection); 39989c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen mServiceBound = false; 4009f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen mService = null; 40189c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen mServiceName = null; 4029f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 4039f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 4049f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 405a3b2c7944266cbd02afc08ce48ce8259d8a65019Martijn Coenen void launchTapAgain(ApduServiceInfo service, String category) { 406451ba48faa87d78bfbec0597ff06af1747cf6acbMartijn Coenen Intent dialogIntent = new Intent(mContext, TapAgainDialog.class); 407451ba48faa87d78bfbec0597ff06af1747cf6acbMartijn Coenen dialogIntent.putExtra(TapAgainDialog.EXTRA_CATEGORY, category); 408a3b2c7944266cbd02afc08ce48ce8259d8a65019Martijn Coenen dialogIntent.putExtra(TapAgainDialog.EXTRA_APDU_SERVICE, service); 409451ba48faa87d78bfbec0597ff06af1747cf6acbMartijn Coenen dialogIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); 410451ba48faa87d78bfbec0597ff06af1747cf6acbMartijn Coenen mContext.startActivityAsUser(dialogIntent, UserHandle.CURRENT); 411451ba48faa87d78bfbec0597ff06af1747cf6acbMartijn Coenen } 412451ba48faa87d78bfbec0597ff06af1747cf6acbMartijn Coenen 413dc304dadda013e383fdd1348b2a24bc6b3c9acacMartijn Coenen void launchResolver(ArrayList<ApduServiceInfo> services, ComponentName failedComponent, 41478976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen String category) { 41575f63db568f953e935e62cb3046d167b881979c8Martijn Coenen Intent intent = new Intent(mContext, AppChooserActivity.class); 41678976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); 417dc304dadda013e383fdd1348b2a24bc6b3c9acacMartijn Coenen intent.putParcelableArrayListExtra(AppChooserActivity.EXTRA_APDU_SERVICES, services); 41875f63db568f953e935e62cb3046d167b881979c8Martijn Coenen intent.putExtra(AppChooserActivity.EXTRA_CATEGORY, category); 41978976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen if (failedComponent != null) { 42078976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen intent.putExtra(AppChooserActivity.EXTRA_FAILED_COMPONENT, failedComponent); 42178976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen } 42275f63db568f953e935e62cb3046d167b881979c8Martijn Coenen mContext.startActivityAsUser(intent, UserHandle.CURRENT); 42375f63db568f953e935e62cb3046d167b881979c8Martijn Coenen } 42475f63db568f953e935e62cb3046d167b881979c8Martijn Coenen 4259f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen String findSelectAid(byte[] data) { 426f9366de7b3ad1ce90859d4194dacf13c0066926bMartijn Coenen if (data == null || data.length < SELECT_APDU_HDR_LENGTH + MINIMUM_AID_LENGTH) { 427f9366de7b3ad1ce90859d4194dacf13c0066926bMartijn Coenen if (DBG) Log.d(TAG, "Data size too small for SELECT APDU"); 42878976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen return null; 4299f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 430f9366de7b3ad1ce90859d4194dacf13c0066926bMartijn Coenen // To accept a SELECT AID for dispatch, we require the following: 431f9366de7b3ad1ce90859d4194dacf13c0066926bMartijn Coenen // Class byte must be 0x00: logical channel set to zero, no secure messaging, no chaining 432f9366de7b3ad1ce90859d4194dacf13c0066926bMartijn Coenen // Instruction byte must be 0xA4: SELECT instruction 433f9366de7b3ad1ce90859d4194dacf13c0066926bMartijn Coenen // P1: must be 0x04: select by application identifier 434f9366de7b3ad1ce90859d4194dacf13c0066926bMartijn Coenen // P2: File control information is only relevant for higher-level application, 435f9366de7b3ad1ce90859d4194dacf13c0066926bMartijn Coenen // and we only support "first or only occurrence". 436f9366de7b3ad1ce90859d4194dacf13c0066926bMartijn Coenen if (data[0] == 0x00 && data[1] == INSTR_SELECT && data[2] == 0x04) { 437f9366de7b3ad1ce90859d4194dacf13c0066926bMartijn Coenen if (data[3] != 0x00) { 438f9366de7b3ad1ce90859d4194dacf13c0066926bMartijn Coenen Log.d(TAG, "Selecting next, last or previous AID occurrence is not supported"); 439f9366de7b3ad1ce90859d4194dacf13c0066926bMartijn Coenen } 4409f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen int aidLength = data[4]; 441f9366de7b3ad1ce90859d4194dacf13c0066926bMartijn Coenen if (data.length < SELECT_APDU_HDR_LENGTH + aidLength) { 4429f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen return null; 4439f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 444f9366de7b3ad1ce90859d4194dacf13c0066926bMartijn Coenen return bytesToString(data, SELECT_APDU_HDR_LENGTH, aidLength); 4459f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 4469f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen return null; 4479f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 4489f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 44989c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen private ServiceConnection mPaymentConnection = new ServiceConnection() { 45089c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen @Override 45189c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen public void onServiceConnected(ComponentName name, IBinder service) { 45289c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen synchronized (mLock) { 45389c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen mPaymentServiceName = name; 45489c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen mPaymentService = new Messenger(service); 45589c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen mPaymentServiceBound = true; 4569f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 45789c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen } 45889c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen 45989c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen @Override 46089c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen public void onServiceDisconnected(ComponentName name) { 46189c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen synchronized (mLock) { 46289c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen mPaymentService = null; 46389c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen mPaymentServiceBound = false; 46489c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen mPaymentServiceName = null; 465d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 4669f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 46789c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen }; 4689f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 4699f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen private ServiceConnection mConnection = new ServiceConnection() { 4709f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen @Override 4719f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen public void onServiceConnected(ComponentName name, IBinder service) { 4729f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen synchronized (mLock) { 4739f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen mService = new Messenger(service); 47489c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen mServiceBound = true; 47589c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen mServiceName = name; 4769f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen Log.d(TAG, "Service bound"); 4779f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen mState = STATE_XFER; 4789f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen // Send pending select APDU 4799f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen if (mSelectApdu != null) { 48089c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen sendDataToServiceLocked(mService, mSelectApdu); 4819f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen mSelectApdu = null; 4829f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 4839f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 4849f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 4859f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 4869f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen @Override 4879f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen public void onServiceDisconnected(ComponentName name) { 4889f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen synchronized (mLock) { 4899f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen Log.d(TAG, "Service unbound"); 4909f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen mService = null; 49189c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen mServiceBound = false; 4929f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 4939f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 4949f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen }; 4959f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 4969f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen class MessageHandler extends Handler { 4979f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen @Override 4989f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen public void handleMessage(Message msg) { 49978976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen synchronized(mLock) { 50078976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen if (mActiveService == null) { 50178976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen Log.d(TAG, "Dropping service response message; service no longer active."); 50278976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen return; 50378976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen } else if (!msg.replyTo.getBinder().equals(mActiveService.getBinder())) { 50478976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen Log.d(TAG, "Dropping service response message; service no longer bound."); 50578976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen return; 50678976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen } 50778976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen } 5086493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen if (msg.what == HostApduService.MSG_RESPONSE_APDU) { 5096493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen Bundle dataBundle = msg.getData(); 5106493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen if (dataBundle == null) { 5116493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen return; 5126493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen } 5136493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen byte[] data = dataBundle.getByteArray("data"); 5146493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen if (data == null || data.length == 0) { 5156493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen Log.e(TAG, "Dropping empty R-APDU"); 5166493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen return; 5176493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen } 5186493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen int state; 5196493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen synchronized(mLock) { 5206493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen state = mState; 5216493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen } 5226493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen if (state == STATE_XFER) { 5236493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen Log.d(TAG, "Sending data"); 5246493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen NfcService.getInstance().sendData(data); 5256493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen } else { 5266493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen Log.d(TAG, "Dropping data, wrong state " + Integer.toString(state)); 5276493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen } 52878976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen } else if (msg.what == HostApduService.MSG_UNHANDLED) { 52978976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen synchronized (mLock) { 53078976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen AidResolveInfo resolveInfo = mAidCache.resolveAidPrefix(mLastSelectedAid); 53178976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen String category = mAidCache.getCategoryForAid(mLastSelectedAid); 53278976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen if (resolveInfo.services.size() > 0) { 533dc304dadda013e383fdd1348b2a24bc6b3c9acacMartijn Coenen final ArrayList<ApduServiceInfo> services = new ArrayList<ApduServiceInfo>(); 53478976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen for (ApduServiceInfo service : resolveInfo.services) { 53578976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen if (!service.getComponent().equals(mActiveServiceName)) { 536dc304dadda013e383fdd1348b2a24bc6b3c9acacMartijn Coenen services.add(service); 53778976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen } 53878976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen } 539dc304dadda013e383fdd1348b2a24bc6b3c9acacMartijn Coenen launchResolver(services, mActiveServiceName, category); 54078976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen } 54178976de08ad5d5f9d5fcba28f3ea82350907a782Martijn Coenen } 5429f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 5439f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 5449f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 5459f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 5469f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen static String bytesToString(byte[] bytes, int offset, int length) { 5479f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen final char[] hexChars = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; 5489f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen char[] chars = new char[length * 2]; 5499f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen int byteValue; 5509f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen for (int j = 0; j < length; j++) { 5519f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen byteValue = bytes[offset + j] & 0xFF; 5529f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen chars[j * 2] = hexChars[byteValue >>> 4]; 5539f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen chars[j * 2 + 1] = hexChars[byteValue & 0x0F]; 5549f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 5559f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen return new String(chars); 5569f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 5579f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen} 558