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