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