1da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen/*
2da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * Copyright (C) 2014 The Android Open Source Project
3da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen *
4da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * Licensed under the Apache License, Version 2.0 (the "License");
5da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * you may not use this file except in compliance with the License.
6da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * You may obtain a copy of the License at
7da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen *
8da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen *      http://www.apache.org/licenses/LICENSE-2.0
9da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen *
10da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * Unless required by applicable law or agreed to in writing, software
11da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * distributed under the License is distributed on an "AS IS" BASIS,
12da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * See the License for the specific language governing permissions and
14da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * limitations under the License.
15da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen */
16af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenenpackage com.android.nfc.cardemulation;
17af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen
18af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenenimport java.io.FileDescriptor;
19af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenenimport java.io.PrintWriter;
20af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenenimport java.util.List;
21af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen
22af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenenimport android.content.ComponentName;
23af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenenimport android.content.Context;
24af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenenimport android.content.Intent;
25af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenenimport android.nfc.INfcCardEmulation;
26af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenenimport android.nfc.cardemulation.AidGroup;
27af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenenimport android.nfc.cardemulation.ApduServiceInfo;
28af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenenimport android.nfc.cardemulation.CardEmulation;
29af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenenimport android.os.Binder;
30af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenenimport android.os.RemoteException;
31af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenenimport android.os.UserHandle;
32af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenenimport android.provider.Settings;
33af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenenimport android.util.Log;
34af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen
35af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenenimport com.android.nfc.NfcPermissions;
36da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenenimport com.android.nfc.cardemulation.RegisteredServicesCache;
37af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen
38af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen/**
39af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen * CardEmulationManager is the central entity
40af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen * responsible for delegating to individual components
41af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen * implementing card emulation:
42af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen * - RegisteredServicesCache keeping track of HCE and SE services on the device
43af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen * - RegisteredAidCache keeping track of AIDs registered by those services and manages
44af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen *   the routing table in the NFCC.
45af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen * - HostEmulationManager handles incoming APDUs for the host and forwards to HCE
46af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen *   services as necessary.
47af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen */
48da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenenpublic class CardEmulationManager implements RegisteredServicesCache.Callback,
49da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen        PreferredServices.Callback {
50af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen    static final String TAG = "CardEmulationManager";
51410f4b955283be0187cd2933bdea07c66e101639Martijn Coenen    static final boolean DBG = false;
52af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen
53af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen    final RegisteredAidCache mAidCache;
54af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen    final RegisteredServicesCache mServiceCache;
55af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen    final HostEmulationManager mHostEmulationManager;
56da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen    final PreferredServices mPreferredServices;
57af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen    final Context mContext;
58af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen    final CardEmulationInterface mCardEmulationInterface;
59af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen
60af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen    public CardEmulationManager(Context context) {
61af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        mContext = context;
62af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        mCardEmulationInterface = new CardEmulationInterface();
63af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        mAidCache = new RegisteredAidCache(context);
64af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        mHostEmulationManager = new HostEmulationManager(context, mAidCache);
65af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        mServiceCache = new RegisteredServicesCache(context, this);
660ad654d57bf17d73f2a7de5c44c9d69b7b450962Martijn Coenen        mPreferredServices = new PreferredServices(context, mServiceCache, mAidCache, this);
67af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen
68af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        mServiceCache.initialize();
69af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen    }
70af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen
71af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen    public INfcCardEmulation getNfcCardEmulationInterface() {
72af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        return mCardEmulationInterface;
73af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen    }
74af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen
75af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen    public void onHostCardEmulationActivated() {
76af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        mHostEmulationManager.onHostEmulationActivated();
77da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen        mPreferredServices.onHostEmulationActivated();
78af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen    }
79af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen
80af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen    public void onHostCardEmulationData(byte[] data) {
81af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        mHostEmulationManager.onHostEmulationData(data);
82af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen    }
83af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen
84af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen    public void onHostCardEmulationDeactivated() {
85af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        mHostEmulationManager.onHostEmulationDeactivated();
86da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen        mPreferredServices.onHostEmulationDeactivated();
87af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen    }
88af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen
89af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen    public void onOffHostAidSelected() {
90af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        mHostEmulationManager.onOffHostAidSelected();
91af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen    }
92af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen
93af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen    public void onUserSwitched(int userId) {
94af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        mServiceCache.invalidateCache(userId);
95da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen        mPreferredServices.onUserSwitched(userId);
96af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen    }
97af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen
98af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen    public void onNfcEnabled() {
99af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        mAidCache.onNfcEnabled();
100af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen    }
101af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen
102af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen    public void onNfcDisabled() {
103af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        mAidCache.onNfcDisabled();
104af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen    }
105af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen
106af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
107af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        mServiceCache.dump(fd, pw, args);
108da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen        mPreferredServices.dump(fd, pw, args);
109af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        mAidCache.dump(fd, pw, args);
110da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen        mHostEmulationManager.dump(fd, pw, args);
111af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen    }
112af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen
113af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen    @Override
114af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen    public void onServicesUpdated(int userId, List<ApduServiceInfo> services) {
115af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        // Verify defaults are still sane
116af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        verifyDefaults(userId, services);
117af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        // Update the AID cache
118af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        mAidCache.onServicesUpdated(userId, services);
119ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen        // Update the preferred services list
120ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen        mPreferredServices.onServicesUpdated();
121af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen    }
122af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen
123af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen    void verifyDefaults(int userId, List<ApduServiceInfo> services) {
124af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        ComponentName defaultPaymentService =
125af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                getDefaultServiceForCategory(userId, CardEmulation.CATEGORY_PAYMENT, false);
126af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        if (DBG) Log.d(TAG, "Current default: " + defaultPaymentService);
127af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        if (defaultPaymentService != null) {
128af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            // Validate the default is still installed and handling payment
129af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            ApduServiceInfo serviceInfo = mServiceCache.getService(userId, defaultPaymentService);
130af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            if (serviceInfo == null || !serviceInfo.hasCategory(CardEmulation.CATEGORY_PAYMENT)) {
131af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                if (serviceInfo == null) {
132af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                    Log.e(TAG, "Default payment service unexpectedly removed.");
133af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                } else if (!serviceInfo.hasCategory(CardEmulation.CATEGORY_PAYMENT)) {
134af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                    if (DBG) Log.d(TAG, "Default payment service had payment category removed");
135af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                }
136af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                int numPaymentServices = 0;
137af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                ComponentName lastFoundPaymentService = null;
138af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                for (ApduServiceInfo service : services) {
139af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                    if (service.hasCategory(CardEmulation.CATEGORY_PAYMENT))  {
140af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                        numPaymentServices++;
141af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                        lastFoundPaymentService = service.getComponent();
142af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                    }
143af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                }
144af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                if (DBG) Log.d(TAG, "Number of payment services is " +
145af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                        Integer.toString(numPaymentServices));
146af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                if (numPaymentServices == 0) {
147af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                    if (DBG) Log.d(TAG, "Default removed, no services left.");
148af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                    // No payment services left, unset default and don't ask the user
149af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                    setDefaultServiceForCategoryChecked(userId, null, CardEmulation.CATEGORY_PAYMENT);
150af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                } else if (numPaymentServices == 1) {
151af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                    // Only one left, automatically make it the default
152af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                    if (DBG) Log.d(TAG, "Default removed, making remaining service default.");
153af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                    setDefaultServiceForCategoryChecked(userId, lastFoundPaymentService,
154af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                            CardEmulation.CATEGORY_PAYMENT);
155af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                } else if (numPaymentServices > 1) {
156af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                    // More than one left, unset default and ask the user if he wants
157af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                    // to set a new one
158af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                    if (DBG) Log.d(TAG, "Default removed, asking user to pick.");
159af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                    setDefaultServiceForCategoryChecked(userId, null,
160af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                            CardEmulation.CATEGORY_PAYMENT);
161af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                    Intent intent = new Intent(mContext, DefaultRemovedActivity.class);
162f291ad8974438d513757d5da929a86cd3d86fe45Martijn Coenen                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
163af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                    mContext.startActivityAsUser(intent, UserHandle.CURRENT);
164af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                }
165af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            } else {
166af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                // Default still exists and handles the category, nothing do
167af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                if (DBG) Log.d(TAG, "Default payment service still ok.");
168af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            }
169af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        } else {
170af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            // A payment service may have been removed, leaving only one;
171af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            // in that case, automatically set that app as default.
172af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            int numPaymentServices = 0;
173af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            ComponentName lastFoundPaymentService = null;
174af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            for (ApduServiceInfo service : services) {
175af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                if (service.hasCategory(CardEmulation.CATEGORY_PAYMENT))  {
176af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                    numPaymentServices++;
177af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                    lastFoundPaymentService = service.getComponent();
178af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                }
179af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            }
180af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            if (numPaymentServices > 1) {
181af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                // More than one service left, leave default unset
182af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                if (DBG) Log.d(TAG, "No default set, more than one service left.");
183af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            } else if (numPaymentServices == 1) {
184af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                // Make single found payment service the default
185af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                if (DBG) Log.d(TAG, "No default set, making single service default.");
186af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                setDefaultServiceForCategoryChecked(userId, lastFoundPaymentService,
187af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                        CardEmulation.CATEGORY_PAYMENT);
188af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            } else {
189af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                // No payment services left, leave default at null
190af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                if (DBG) Log.d(TAG, "No default set, last payment service removed.");
191af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            }
192af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        }
193af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen    }
194af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen
195af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen    ComponentName getDefaultServiceForCategory(int userId, String category,
196af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen             boolean validateInstalled) {
197af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        if (!CardEmulation.CATEGORY_PAYMENT.equals(category)) {
198af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            Log.e(TAG, "Not allowing defaults for category " + category);
199af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            return null;
200af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        }
201af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        // Load current payment default from settings
202af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        String name = Settings.Secure.getStringForUser(
203af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                mContext.getContentResolver(), Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT,
204af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                userId);
205af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        if (name != null) {
206af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            ComponentName service = ComponentName.unflattenFromString(name);
207af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            if (!validateInstalled || service == null) {
208af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                return service;
209af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            } else {
210af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                return mServiceCache.hasService(userId, service) ? service : null;
211af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen             }
212af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        } else {
213af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            return null;
214af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        }
215af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen    }
216af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen
217af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen    boolean setDefaultServiceForCategoryChecked(int userId, ComponentName service,
218af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            String category) {
219af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        if (!CardEmulation.CATEGORY_PAYMENT.equals(category)) {
220af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            Log.e(TAG, "Not allowing defaults for category " + category);
221af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            return false;
222af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        }
223af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        // TODO Not really nice to be writing to Settings.Secure here...
224af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        // ideally we overlay our local changes over whatever is in
225af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        // Settings.Secure
226af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        if (service == null || mServiceCache.hasService(userId, service)) {
227af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            Settings.Secure.putStringForUser(mContext.getContentResolver(),
228af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                    Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT,
229af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                    service != null ? service.flattenToString() : null, userId);
230af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        } else {
231af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            Log.e(TAG, "Could not find default service to make default: " + service);
232af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        }
233af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        return true;
234af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen    }
235af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen
236af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen    boolean isServiceRegistered(int userId, ComponentName service) {
237af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        boolean serviceFound = mServiceCache.hasService(userId, service);
238af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        if (!serviceFound) {
239af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            // If we don't know about this service yet, it may have just been enabled
240af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            // using PackageManager.setComponentEnabledSetting(). The PackageManager
241af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            // broadcasts are delayed by 10 seconds in that scenario, which causes
242af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            // calls to our APIs referencing that service to fail.
243af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            // Hence, update the cache in case we don't know about the service.
244af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            if (DBG) Log.d(TAG, "Didn't find passed in service, invalidating cache.");
245af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            mServiceCache.invalidateCache(userId);
246af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        }
247af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        return mServiceCache.hasService(userId, service);
248af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen    }
249af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen
250af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen    /**
251d943597d17fb8aa462b119040334478756f9e2f9Martijn Coenen     * Returns whether a service in this package is preferred,
252d943597d17fb8aa462b119040334478756f9e2f9Martijn Coenen     * either because it's the default payment app or it's running
253d943597d17fb8aa462b119040334478756f9e2f9Martijn Coenen     * in the foreground.
254d943597d17fb8aa462b119040334478756f9e2f9Martijn Coenen     */
255d943597d17fb8aa462b119040334478756f9e2f9Martijn Coenen    public boolean packageHasPreferredService(String packageName) {
256d943597d17fb8aa462b119040334478756f9e2f9Martijn Coenen        return mPreferredServices.packageHasPreferredService(packageName);
257d943597d17fb8aa462b119040334478756f9e2f9Martijn Coenen    }
258d943597d17fb8aa462b119040334478756f9e2f9Martijn Coenen
259d943597d17fb8aa462b119040334478756f9e2f9Martijn Coenen    /**
260af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen     * This class implements the application-facing APIs
261af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen     * and are called from binder. All calls must be
262af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen     * permission-checked.
263af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen     *
264af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen     */
265af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen    final class CardEmulationInterface extends INfcCardEmulation.Stub {
266af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        @Override
267af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        public boolean isDefaultServiceForCategory(int userId, ComponentName service,
268af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                String category) {
269af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            NfcPermissions.enforceUserPermissions(mContext);
270af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            NfcPermissions.validateUserId(userId);
271af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            if (!isServiceRegistered(userId, service)) {
272af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                return false;
273af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            }
274af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            ComponentName defaultService =
275af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                    getDefaultServiceForCategory(userId, category, true);
276af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            return (defaultService != null && defaultService.equals(service));
277af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        }
278af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen
279af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        @Override
280af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        public boolean isDefaultServiceForAid(int userId,
281af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                ComponentName service, String aid) throws RemoteException {
282af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            NfcPermissions.validateUserId(userId);
283af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            NfcPermissions.enforceUserPermissions(mContext);
284af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            if (!isServiceRegistered(userId, service)) {
285af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                return false;
286af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            }
287af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            return mAidCache.isDefaultServiceForAid(userId, service, aid);
288af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        }
289af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen
290af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        @Override
291af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        public boolean setDefaultServiceForCategory(int userId,
292af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                ComponentName service, String category) throws RemoteException {
293af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            NfcPermissions.validateUserId(userId);
294af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            NfcPermissions.enforceAdminPermissions(mContext);
295af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            if (!isServiceRegistered(userId, service)) {
296af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                return false;
297af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            }
298af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            return setDefaultServiceForCategoryChecked(userId, service, category);
299af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        }
300af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen
301af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        @Override
302af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        public boolean setDefaultForNextTap(int userId, ComponentName service)
303af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                throws RemoteException {
304af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            NfcPermissions.validateUserId(userId);
305af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            NfcPermissions.enforceAdminPermissions(mContext);
306af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            if (!isServiceRegistered(userId, service)) {
307af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                return false;
308af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            }
309da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen            return mPreferredServices.setDefaultForNextTap(service);
310af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        }
311af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen
312af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        @Override
313af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        public boolean registerAidGroupForService(int userId,
314af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                ComponentName service, AidGroup aidGroup) throws RemoteException {
315af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            NfcPermissions.validateUserId(userId);
316af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            NfcPermissions.enforceUserPermissions(mContext);
317af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            if (!isServiceRegistered(userId, service)) {
318af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                return false;
319af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            }
320af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            return mServiceCache.registerAidGroupForService(userId, Binder.getCallingUid(), service,
321af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                    aidGroup);
322af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        }
323af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen
324af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        @Override
325af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        public AidGroup getAidGroupForService(int userId,
326af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                ComponentName service, String category) throws RemoteException {
327af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            NfcPermissions.validateUserId(userId);
328af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            NfcPermissions.enforceUserPermissions(mContext);
329af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            if (!isServiceRegistered(userId, service)) {
330af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                return null;
331af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            }
332af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            return mServiceCache.getAidGroupForService(userId, Binder.getCallingUid(), service,
333af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                    category);
334af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        }
335af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen
336af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        @Override
337af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        public boolean removeAidGroupForService(int userId,
338af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                ComponentName service, String category) throws RemoteException {
339af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            NfcPermissions.validateUserId(userId);
340af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            NfcPermissions.enforceUserPermissions(mContext);
341af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            if (!isServiceRegistered(userId, service)) {
342af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                return false;
343af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            }
344af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            return mServiceCache.removeAidGroupForService(userId, Binder.getCallingUid(), service,
345af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                    category);
346af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        }
347af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen
348af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        @Override
349af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        public List<ApduServiceInfo> getServices(int userId, String category)
350af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen                throws RemoteException {
351af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            NfcPermissions.validateUserId(userId);
352af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            NfcPermissions.enforceAdminPermissions(mContext);
353af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen            return mServiceCache.getServicesForCategory(userId, category);
354af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen        }
355da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen
356da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen        @Override
357da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen        public boolean setPreferredService(ComponentName service)
358da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen                throws RemoteException {
359da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen            NfcPermissions.enforceUserPermissions(mContext);
360da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen            if (!isServiceRegistered(UserHandle.getCallingUserId(), service)) {
36131208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen                Log.e(TAG, "setPreferredService: unknown component.");
362da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen                return false;
363da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen            }
364da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen            return mPreferredServices.registerPreferredForegroundService(service,
365da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen                    Binder.getCallingUid());
366da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen        }
367da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen
368da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen        @Override
369da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen        public boolean unsetPreferredService() throws RemoteException {
370da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen            NfcPermissions.enforceUserPermissions(mContext);
371da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen            return mPreferredServices.unregisteredPreferredForegroundService(
372da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen                    Binder.getCallingUid());
373da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen
374da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen        }
37531208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen
37631208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen        @Override
37731208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen        public boolean supportsAidPrefixRegistration() throws RemoteException {
37831208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen            return mAidCache.supportsAidPrefixRegistration();
37931208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen        }
380da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen    }
381da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen
382da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen    @Override
383da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen    public void onPreferredPaymentServiceChanged(ComponentName service) {
384da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen        mAidCache.onPreferredPaymentServiceChanged(service);
385da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen        mHostEmulationManager.onPreferredPaymentServiceChanged(service);
386da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen    }
387da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen
388da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen    @Override
389da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen    public void onPreferredForegroundServiceChanged(ComponentName service) {
390da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen        mAidCache.onPreferredForegroundServiceChanged(service);
391da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen        mHostEmulationManager.onPreferredForegroundServiceChanged(service);
392af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen    };
393e6f2dc6731bccc8ea01c8d9d03dd7e90f2c00312Martijn Coenen}
394