RegisteredServicesCache.java revision 4358172f18871d58d5bc2050e4d9d0bf9bc2d5e5
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 199f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport org.xmlpull.v1.XmlPullParserException; 209f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport android.app.ActivityManager; 219f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport android.content.BroadcastReceiver; 229f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport android.content.ComponentName; 239f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport android.content.Context; 249f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport android.content.Intent; 259f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport android.content.IntentFilter; 269f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport android.content.pm.PackageManager; 279f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport android.content.pm.ResolveInfo; 289f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport android.content.pm.ServiceInfo; 29a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenenimport android.content.pm.PackageManager.NameNotFoundException; 30a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenenimport android.database.ContentObserver; 31a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenenimport android.net.Uri; 32a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenenimport android.nfc.cardemulation.ApduServiceInfo; 33a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenenimport android.nfc.cardemulation.ApduServiceInfo.AidGroup; 34a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenenimport android.nfc.cardemulation.CardEmulationManager; 356493859b424f65af79e3e13835f7dfed38495c00Martijn Coenenimport android.nfc.cardemulation.HostApduService; 3689c893312d524f50c47b0d32071f3de8631197f3Martijn Coenenimport android.nfc.cardemulation.OffHostApduService; 37a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenenimport android.os.Handler; 38a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenenimport android.os.Looper; 399f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport android.os.UserHandle; 40a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenenimport android.provider.Settings; 419f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport android.util.Log; 429f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport android.util.SparseArray; 434358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen 449f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport com.google.android.collect.Maps; 459f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 469f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport java.io.IOException; 479f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport java.util.ArrayList; 489f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport java.util.HashMap; 499f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport java.util.HashSet; 50d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenenimport java.util.Iterator; 519f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport java.util.List; 529f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport java.util.Map; 539f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport java.util.Set; 549f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport java.util.SortedMap; 559f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport java.util.TreeMap; 569f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport java.util.concurrent.atomic.AtomicReference; 579f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 586493859b424f65af79e3e13835f7dfed38495c00Martijn Coenenpublic class RegisteredServicesCache { 599f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen static final String TAG = "RegisteredAidCache"; 609f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen static final boolean DEBUG = true; 619f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 62a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen static final String AID_CATEGORY_PAYMENT = CardEmulationManager.CATEGORY_PAYMENT; 63a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen 649f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen final Context mContext; 659f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen final AtomicReference<BroadcastReceiver> mReceiver; 66d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen final AidRoutingManager mRoutingManager; 679f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 689f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen final Object mLock = new Object(); 699f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen // All variables below synchronized on mLock 709f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 719f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen // mUserServices holds the card emulation services that are running for each user 729f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen final SparseArray<UserServices> mUserServices = new SparseArray<UserServices>(); 739f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 749f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen // mAidServices is a tree that maps an AID to a list of handling services 75a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen // on Android. It is only valid for the current user. 76a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen final TreeMap<String, ArrayList<ApduServiceInfo>> mAidToServices = 77a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen new TreeMap<String, ArrayList<ApduServiceInfo>>(); 78a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen 79a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen // mAidCache is a lookup table for quickly mapping an AID to one or 80a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen // more services. It differs from mAidServices in the sense that it 81a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen // has already accounted for defaults, and hence its return value 82a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen // is authoritative for the current set of services and defaults. 83a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen // It is only valid for the current user. 84a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen final HashMap<String, ArrayList<ApduServiceInfo>> mAidCache = 85a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen Maps.newHashMap(); 86a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen 87a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen final Handler mHandler = new Handler(Looper.getMainLooper()); 88a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen 8975f63db568f953e935e62cb3046d167b881979c8Martijn Coenen ComponentName mNextTapComponent = null; 9075f63db568f953e935e62cb3046d167b881979c8Martijn Coenen 91a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen private final class SettingsObserver extends ContentObserver { 92a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen public SettingsObserver(Handler handler) { 93a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen super(handler); 949f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 959f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 969f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen @Override 97a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen public void onChange(boolean selfChange, Uri uri) { 98a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen super.onChange(selfChange, uri); 99a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen synchronized (mLock) { 100a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen // Do it just for the current user. If it was in fact 101a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen // a change made for another user, we'll sync it down 102a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen // on user switch. 103a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen int currentUser = ActivityManager.getCurrentUser(); 104a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen UserServices userServices = findOrCreateUserLocked(currentUser); 10589c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen boolean changed = updateFromSettingsLocked(currentUser); 10689c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen if (changed) { 10789c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen generateAidCacheLocked(userServices); 10889c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen updateRoutingLocked(userServices); 10989c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen } else { 11089c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen Log.d(TAG, "Not updating aid cache + routing: nothing changed."); 11189c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen } 1129f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 1139f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 114a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen }; 1159f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 1169f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen private static class UserServices { 117a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen /** 118a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen * All services that have registered 119a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen */ 120a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen public final HashMap<ComponentName, ApduServiceInfo> services = 121a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen Maps.newHashMap(); // Re-built at run-time 122a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen 123a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen /** 124a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen * AIDs per category 125a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen */ 126a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen public final HashMap<String, Set<String>> categoryAids = 1279f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen Maps.newHashMap(); 128a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen 129a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen /** 130a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen * Default component per category 131a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen */ 1329f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen public final HashMap<String, ComponentName> defaults = 133a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen Maps.newHashMap(); 134a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen 135a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen /** 136a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen * Whether auto-select mode is enabled per category 137a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen */ 138a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen public final HashMap<String, Boolean> mode = 139a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen Maps.newHashMap(); 1409f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen }; 1419f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 1429f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen private UserServices findOrCreateUserLocked(int userId) { 1439f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen UserServices services = mUserServices.get(userId); 1449f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen if (services == null) { 1459f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen services = new UserServices(); 1469f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen mUserServices.put(userId, services); 1479f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 1489f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen return services; 1499f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 1509f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 1516493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen public RegisteredServicesCache(Context context, AidRoutingManager routingManager) { 1529f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen mContext = context; 153d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen mRoutingManager = routingManager; 1549f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 1559f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen final BroadcastReceiver receiver = new BroadcastReceiver() { 1569f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen @Override 157d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen public void onReceive(Context context, Intent intent) { 158d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); 159d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen String action = intent.getAction(); 160d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen if (uid != -1) { 161d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen if (DEBUG) Log.d(TAG, "Intent action: " + action); 162d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen boolean replaced = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false) && 163d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen (Intent.ACTION_PACKAGE_ADDED.equals(action) || 164d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen Intent.ACTION_PACKAGE_REMOVED.equals(action)); 165d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen if (!replaced) { 16689c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen int currentUser = ActivityManager.getCurrentUser(); 16789c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen if (currentUser == UserHandle.getUserId(uid)) { 16889c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen invalidateCache(UserHandle.getUserId(uid)); 16989c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen } else { 17089c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen // Cache will automatically be updated on user switch 17189c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen } 172d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } else { 173d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen if (DEBUG) Log.d(TAG, "Ignoring package intent due to package being replaced."); 174d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 175d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 1769f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 1779f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen }; 1789f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 1799f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen mReceiver = new AtomicReference<BroadcastReceiver>(receiver); 1809f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 1819f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen IntentFilter intentFilter = new IntentFilter(); 1829f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED); 1839f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); 1849f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); 185d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen intentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED); 1869f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen intentFilter.addDataScheme("package"); 1879f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen mContext.registerReceiverAsUser(receiver, UserHandle.ALL, intentFilter, null, null); 1889f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 1899f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen // Register for events related to sdcard operations 1909f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen IntentFilter sdFilter = new IntentFilter(); 1919f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); 1929f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); 1939f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen mContext.registerReceiverAsUser(receiver, UserHandle.ALL, sdFilter, null, null); 194a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen 195a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen SettingsObserver observer = new SettingsObserver(mHandler); 196a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen context.getContentResolver().registerContentObserver( 197a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen Settings.Secure.getUriFor(Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT), 198a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen true, observer, UserHandle.USER_ALL); 199a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen context.getContentResolver().registerContentObserver( 200a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen Settings.Secure.getUriFor(Settings.Secure.NFC_PAYMENT_MODE), 201a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen true, observer, UserHandle.USER_ALL); 2029f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 2039f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 20489c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen public ArrayList<ApduServiceInfo> resolveAidPrefix(String aid) { 205a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen synchronized (mLock) { 206a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen char nextAidChar = (char) (aid.charAt(aid.length() - 1) + 1); 207a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen String nextAid = aid.substring(0, aid.length() - 1) + nextAidChar; 208a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen SortedMap<String, ArrayList<ApduServiceInfo>> matches = 209a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen mAidToServices.subMap(aid, nextAid); 210a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen // The first match is lexicographically closest to what the reader asked; 211a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen if (matches.isEmpty()) { 212a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen return null; 213a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen } else { 21489c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen return mAidCache.get(matches.firstKey()); 2159f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 216a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen } 217a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen } 218717a8bdc43aa9328899df86cb523430466cf5baaMartijn Coenen 219a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen /** 220a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen * Resolves an AID to a set of services that can handle it. 221a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen */ 22275f63db568f953e935e62cb3046d167b881979c8Martijn Coenen ArrayList<ApduServiceInfo> resolveAidLocked(UserServices userServices, String aid) { 223a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen ArrayList<ApduServiceInfo> resolvedServices = mAidToServices.get(aid); 224a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen if (resolvedServices == null || resolvedServices.size() == 0) { 225a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen Log.d(TAG, "Could not resolve AID " + aid + " to any service."); 226a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen return null; 227a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen } 228a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen Log.e(TAG, "resolveAidLocked: resolving AID " + aid); 229a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen ArrayList<ApduServiceInfo> resolved = new ArrayList<ApduServiceInfo>(); 230a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen 23175f63db568f953e935e62cb3046d167b881979c8Martijn Coenen ComponentName defaultComponent = mNextTapComponent; 23275f63db568f953e935e62cb3046d167b881979c8Martijn Coenen Log.d(TAG, "resolveAidLocked: next tap component is " + defaultComponent); 233a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen Set<String> paymentAids = userServices.categoryAids.get(AID_CATEGORY_PAYMENT); 234a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen if (paymentAids != null && paymentAids.contains(aid)) { 235a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen Log.d(TAG, "resolveAidLocked: AID " + aid + " is a payment AID"); 236a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen // This AID has been registered as a payment AID by at least one service. 23775f63db568f953e935e62cb3046d167b881979c8Martijn Coenen // Get default component for payment if no next tap default. 23875f63db568f953e935e62cb3046d167b881979c8Martijn Coenen if (defaultComponent == null) { 23975f63db568f953e935e62cb3046d167b881979c8Martijn Coenen defaultComponent = userServices.defaults.get(AID_CATEGORY_PAYMENT); 24075f63db568f953e935e62cb3046d167b881979c8Martijn Coenen } 241a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen Log.d(TAG, "resolveAidLocked: default payment component is " + defaultComponent); 242a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen if (resolvedServices.size() == 1) { 243a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen ApduServiceInfo resolvedService = resolvedServices.get(0); 244a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen Log.d(TAG, "resolveAidLocked: resolved single service " + 245a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen resolvedService.getComponent()); 246a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen if (resolvedService.equals(defaultComponent)) { 247a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen Log.d(TAG, "resolveAidLocked: DECISION: routing to (default) " + 248a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen resolvedService.getComponent()); 249a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen resolvedServices.clear(); 250a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen resolved.add(resolvedService); 251a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen } else { 252a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen // Route to this one service if uncontended for all AIDs. 253a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen boolean foundConflict = false; 254a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen for (String registeredAid : resolvedService.getAids()) { 255a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen ArrayList<ApduServiceInfo> servicesForAid = 256a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen mAidToServices.get(registeredAid); 257a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen if (servicesForAid != null && servicesForAid.size() > 1) { 258a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen foundConflict = true; 259a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen } 260a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen } 261a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen if (!foundConflict) { 262a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen Log.d(TAG, "resolveAidLocked: DECISION: routing to " + 263a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen resolvedService.getComponent()); 264a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen resolved.add(resolvedService); 265a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen } else { 266a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen // will drop out below with empty list 267a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen Log.d(TAG, "resolveAidLocked: DECISION: not routing AID " + aid + 268a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen " because only part of it's <aid-group> is uncontended"); 269a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen } 270a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen } 271a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen } else if (resolvedServices.size() > 1) { 272a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen // More services have registered. If there's a default and it 273a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen // registered this AID, go with the default. Otherwise, add all. 27475f63db568f953e935e62cb3046d167b881979c8Martijn Coenen Log.d(TAG, "resolveAidLocked: multiple services matched."); 275a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen if (defaultComponent != null) { 276a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen for (ApduServiceInfo service : resolvedServices) { 277a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen if (service.getComponent().equals(defaultComponent)) { 278a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen Log.d(TAG, "resolveAidLocked: DECISION: routing to (default) " + 279a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen service.getComponent()); 280a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen resolved.add(service); 281a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen break; 282a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen } 283a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen } 284a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen if (resolved.size() == 0) { 285a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen // If we didn't find the default, just add all 28675f63db568f953e935e62cb3046d167b881979c8Martijn Coenen // This happens when multiple payment apps have registered 28775f63db568f953e935e62cb3046d167b881979c8Martijn Coenen // for an AID, but the default does not handle it. In this 28875f63db568f953e935e62cb3046d167b881979c8Martijn Coenen // case, let the user pick, and that app will be the default 28975f63db568f953e935e62cb3046d167b881979c8Martijn Coenen // for it's aid groups for the next tap. 29075f63db568f953e935e62cb3046d167b881979c8Martijn Coenen // TODO do we want to show different UI in this case? 29175f63db568f953e935e62cb3046d167b881979c8Martijn Coenen Log.d(TAG, "resolveAidLocked: DECISION: routing all"); 292a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen resolved.addAll(resolvedServices); 293a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen } 29475f63db568f953e935e62cb3046d167b881979c8Martijn Coenen } else { 29575f63db568f953e935e62cb3046d167b881979c8Martijn Coenen Log.d(TAG, "resolveAidLocked: DECISION: no default, routing all"); 29675f63db568f953e935e62cb3046d167b881979c8Martijn Coenen resolved.addAll(resolvedServices); 297a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen } 298a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen } // else -> should not hit, we checked for 0 before. 299a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen } else { 300a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen // This AID is not a payment AID, just return all components 301a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen // that can handle it. 302a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen Log.d(TAG, "resolveAidLocked: DECISION: cat OTHER AID, routing all"); 303a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen for (ApduServiceInfo service : resolvedServices) { 304a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen resolved.add(service); 305a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen } 306d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 307a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen return resolved; 3089f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 3099f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 310a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen void dump(ArrayList<ApduServiceInfo> services) { 311a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen for (ApduServiceInfo service : services) { 312d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen if (DEBUG) Log.d(TAG, service.toString()); 3139f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 3149f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 3159f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 316a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen void generateAidCategoriesLocked(UserServices userServices) { 317a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen // Trash existing mapping 318a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen userServices.categoryAids.clear(); 319a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen 320a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen for (ApduServiceInfo service : userServices.services.values()) { 321a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen ArrayList<AidGroup> aidGroups = service.getAidGroups(); 322a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen if (aidGroups == null) continue; 323a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen for (AidGroup aidGroup : aidGroups) { 324a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen String groupCategory = aidGroup.getCategory(); 325a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen Set<String> categoryAids = userServices.categoryAids.get(groupCategory); 326a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen if (categoryAids == null) { 327a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen categoryAids = new HashSet<String>(); 328a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen } 329a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen categoryAids.addAll(aidGroup.getAids()); 330a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen userServices.categoryAids.put(groupCategory, categoryAids); 331a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen } 332a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen } 333a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen } 334a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen 335d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen void generateAidTreeForUserLocked(UserServices userServices) { 336d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen // Easiest is to just build the entire tree again 337d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen mAidToServices.clear(); 338a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen for (ApduServiceInfo service : userServices.services.values()) { 339a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen Log.d(TAG, "generateAidTree component: " + service.getComponent()); 340a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen for (String aid : service.getAids()) { 341d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen Log.d(TAG, "generateAidTree AID: " + aid); 342d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen // Check if a mapping exists for this AID 343d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen if (mAidToServices.containsKey(aid)) { 344a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen final ArrayList<ApduServiceInfo> aidServices = mAidToServices.get(aid); 345d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen aidServices.add(service); 346d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } else { 347a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen final ArrayList<ApduServiceInfo> aidServices = 348a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen new ArrayList<ApduServiceInfo>(); 349d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen aidServices.add(service); 350d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen mAidToServices.put(aid, aidServices); 351d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 352d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 353d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 354d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 355d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen 356a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen void generateAidCacheLocked(UserServices userServices) { 357a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen mAidCache.clear(); 358a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen for (Map.Entry<String, ArrayList<ApduServiceInfo>> aidEntry: 359a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen mAidToServices.entrySet()) { 360a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen String aid = aidEntry.getKey(); 3614358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen Log.e(TAG, "Mapping aid " + aid + "to: " + resolveAidLocked(userServices, aid)); 362a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen mAidCache.put(aid, resolveAidLocked(userServices, aid)); 363a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen } 364a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen } 365a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen 366a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen boolean containsServiceLocked(ArrayList<ApduServiceInfo> services, ComponentName serviceName) { 367a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen for (ApduServiceInfo service : services) { 368a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen if (service.getComponent().equals(serviceName)) return true; 369a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen } 370a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen return false; 371d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 372d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen 37389c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen boolean updateFromSettingsLocked(int userId) { 374a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen UserServices userServices = findOrCreateUserLocked(userId); 37589c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen boolean changed = false; 376a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen 377a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen // Load current payment default from settings 378a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen String name = Settings.Secure.getStringForUser( 379a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen mContext.getContentResolver(), Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT, 380a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen userId); 38189c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen ComponentName newDefault = name != null ? ComponentName.unflattenFromString(name) : null; 38289c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen ComponentName oldDefault = userServices.defaults.put(AID_CATEGORY_PAYMENT, newDefault); 38389c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen changed |= newDefault != oldDefault; 38489c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen Log.e(TAG, "Set default component to: " + (name != null ? 385a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen ComponentName.unflattenFromString(name) : "null")); 38675f63db568f953e935e62cb3046d167b881979c8Martijn Coenen 387a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen // Load payment mode from settings 38889c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen String newModeString = Settings.Secure.getStringForUser(mContext.getContentResolver(), 389a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen Settings.Secure.NFC_PAYMENT_MODE, userId); 39089c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen boolean newMode = !CardEmulationManager.PAYMENT_MODE_MANUAL.equals(newModeString); 39189c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen Log.e(TAG, "Setting mode to: " + newMode); 39289c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen Boolean oldMode = userServices.mode.put(AID_CATEGORY_PAYMENT, newMode); 39389c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen changed |= (oldMode == null) || (newMode != oldMode.booleanValue()); 39489c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen 39589c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen return changed; 396a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen } 397a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen 39875f63db568f953e935e62cb3046d167b881979c8Martijn Coenen public String getCategoryForAid(int userId, String aid) { 39975f63db568f953e935e62cb3046d167b881979c8Martijn Coenen synchronized (mLock) { 40075f63db568f953e935e62cb3046d167b881979c8Martijn Coenen UserServices userServices = findOrCreateUserLocked(userId); 40175f63db568f953e935e62cb3046d167b881979c8Martijn Coenen // Optimize this later 40275f63db568f953e935e62cb3046d167b881979c8Martijn Coenen Set<String> paymentAids = userServices.categoryAids.get(AID_CATEGORY_PAYMENT); 40375f63db568f953e935e62cb3046d167b881979c8Martijn Coenen if (paymentAids != null && paymentAids.contains(aid)) { 40475f63db568f953e935e62cb3046d167b881979c8Martijn Coenen return CardEmulationManager.CATEGORY_PAYMENT; 40575f63db568f953e935e62cb3046d167b881979c8Martijn Coenen } else { 40675f63db568f953e935e62cb3046d167b881979c8Martijn Coenen return CardEmulationManager.CATEGORY_OTHER; 40775f63db568f953e935e62cb3046d167b881979c8Martijn Coenen } 40875f63db568f953e935e62cb3046d167b881979c8Martijn Coenen } 40975f63db568f953e935e62cb3046d167b881979c8Martijn Coenen } 410a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen public ApduServiceInfo getDefaultServiceForCategory(int userId, String category) { 411a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen if (!CardEmulationManager.CATEGORY_PAYMENT.equals(category)) { 412a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen Log.e(TAG, "Not allowing defaults for category " + category); 413a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen return null; 414a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen } 415a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen synchronized (mLock) { 416a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen UserServices userServices = findOrCreateUserLocked(userId); 417a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen // Load current payment default from settings 418a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen String name = Settings.Secure.getStringForUser( 419a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen mContext.getContentResolver(), Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT, 420a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen userId); 421a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen if (name != null) { 422a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen ComponentName serviceComponent = ComponentName.unflattenFromString(name); 423a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen return serviceComponent != null ? 424a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen userServices.services.get(serviceComponent) : null; 425a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen } else { 426a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen return null; 427d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 428d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 429d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 430d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen 431a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen public boolean isDefaultServiceForAid(int userId, ComponentName service, String aid) { 432a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen ArrayList<ApduServiceInfo> serviceList = mAidCache.get(aid); 433a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen if (serviceList == null || serviceList.size() == 0) return false; 434a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen 435a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen ApduServiceInfo serviceInfo = serviceList.get(0); 436a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen 437a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen return service.equals(serviceInfo); 438a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen } 439a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen 440a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen public boolean setDefaultServiceForCategory(int userId, ComponentName service, 441a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen String category) { 442a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen if (!CardEmulationManager.CATEGORY_PAYMENT.equals(category)) { 443a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen Log.e(TAG, "Not allowing defaults for category " + category); 444a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen return false; 4459f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 446a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen synchronized (mLock) { 447a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen UserServices userServices = findOrCreateUserLocked(userId); 44875f63db568f953e935e62cb3046d167b881979c8Martijn Coenen // TODO Not really nice to be writing to Settings.Secure here... 44975f63db568f953e935e62cb3046d167b881979c8Martijn Coenen // ideally we overlay our local changes over whatever is in 45075f63db568f953e935e62cb3046d167b881979c8Martijn Coenen // Settings.Secure 451a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen if (userServices.services.get(service) != null) { 452a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen Settings.Secure.putStringForUser(mContext.getContentResolver(), 453a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT, service.flattenToString(), 454a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen userId); 45575f63db568f953e935e62cb3046d167b881979c8Martijn Coenen Settings.Secure.putStringForUser(mContext.getContentResolver(), 45675f63db568f953e935e62cb3046d167b881979c8Martijn Coenen Settings.Secure.NFC_PAYMENT_MODE, CardEmulationManager.PAYMENT_MODE_AUTO, 45775f63db568f953e935e62cb3046d167b881979c8Martijn Coenen userId); 45875f63db568f953e935e62cb3046d167b881979c8Martijn Coenen } else { 45975f63db568f953e935e62cb3046d167b881979c8Martijn Coenen Settings.Secure.putStringForUser(mContext.getContentResolver(), 46075f63db568f953e935e62cb3046d167b881979c8Martijn Coenen Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT, null, 46175f63db568f953e935e62cb3046d167b881979c8Martijn Coenen userId); 46275f63db568f953e935e62cb3046d167b881979c8Martijn Coenen Settings.Secure.putStringForUser(mContext.getContentResolver(), 46375f63db568f953e935e62cb3046d167b881979c8Martijn Coenen Settings.Secure.NFC_PAYMENT_MODE, CardEmulationManager.PAYMENT_MODE_MANUAL, 46475f63db568f953e935e62cb3046d167b881979c8Martijn Coenen userId); 46575f63db568f953e935e62cb3046d167b881979c8Martijn Coenen } 46675f63db568f953e935e62cb3046d167b881979c8Martijn Coenen } 46775f63db568f953e935e62cb3046d167b881979c8Martijn Coenen return true; 46875f63db568f953e935e62cb3046d167b881979c8Martijn Coenen } 46975f63db568f953e935e62cb3046d167b881979c8Martijn Coenen 47075f63db568f953e935e62cb3046d167b881979c8Martijn Coenen public boolean isNextTapOverriden() { 47175f63db568f953e935e62cb3046d167b881979c8Martijn Coenen return mNextTapComponent != null; 47275f63db568f953e935e62cb3046d167b881979c8Martijn Coenen } 47375f63db568f953e935e62cb3046d167b881979c8Martijn Coenen 47475f63db568f953e935e62cb3046d167b881979c8Martijn Coenen public boolean setDefaultForNextTap(int userId, ComponentName service) { 47575f63db568f953e935e62cb3046d167b881979c8Martijn Coenen synchronized (mLock) { 47675f63db568f953e935e62cb3046d167b881979c8Martijn Coenen if (service != null) { 47775f63db568f953e935e62cb3046d167b881979c8Martijn Coenen mNextTapComponent = service; 47875f63db568f953e935e62cb3046d167b881979c8Martijn Coenen } else { 47975f63db568f953e935e62cb3046d167b881979c8Martijn Coenen mNextTapComponent = null; 480a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen } 48175f63db568f953e935e62cb3046d167b881979c8Martijn Coenen UserServices userServices = findOrCreateUserLocked(userId); 48275f63db568f953e935e62cb3046d167b881979c8Martijn Coenen // Update state and routing table 48375f63db568f953e935e62cb3046d167b881979c8Martijn Coenen generateAidCacheLocked(userServices); 48475f63db568f953e935e62cb3046d167b881979c8Martijn Coenen updateRoutingLocked(userServices); 485a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen } 486a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen return true; 4879f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 4889f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 489a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen public ArrayList<ApduServiceInfo> getServicesForCategory(int userId, String category) { 490a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen final ArrayList<ApduServiceInfo> enabledServices = new ArrayList<ApduServiceInfo>(); 491a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen synchronized (mLock) { 492a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen UserServices userServices = findOrCreateUserLocked(userId); 493a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen for (ApduServiceInfo service : userServices.services.values()) { 494a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen if (service.hasCategory(category)) enabledServices.add(service); 495a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen } 496a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen } 497a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen return enabledServices; 498a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen } 499a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen 50062372fdecd77cc07db475e79e366e6864b44b956Martijn Coenen public void invalidateCache(int userId) { 501a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen // This code consists of a few phases: 502a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen // - Finding all HCE services and making sure mUserServices.services is correct 503a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen // - Rebuilding the UserServices.categoryAids HashMap from userServices 504d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen // - Rebuilding the mAidToServices lookup table from userServices 505a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen // - Rebuilding the mAidCache lookup table from mAidToServices 506d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen // - Rebuilding the AID routing table from mAidToServices lookup table 507d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen // 1. Finding all HCE services and making sure mUserServices is correct 5089f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen PackageManager pm; 5099f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen try { 5109f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen pm = mContext.createPackageContextAsUser("android", 0, 511717a8bdc43aa9328899df86cb523430466cf5baaMartijn Coenen new UserHandle(userId)).getPackageManager(); 5129f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } catch (NameNotFoundException e) { 5139f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen Log.e(TAG, "Could not create user package context"); 5149f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen return; 5159f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 5169f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 517a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen ArrayList<ApduServiceInfo> validServices = new ArrayList<ApduServiceInfo>(); 5189f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 5196493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen List<ResolveInfo> resolvedServices = pm.queryIntentServicesAsUser( 5206493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen new Intent(HostApduService.SERVICE_INTERFACE), 52189c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen PackageManager.GET_META_DATA, userId); 52289c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen 52389c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen List<ResolveInfo> resolvedOffHostServices = pm.queryIntentServicesAsUser( 52489c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen new Intent(OffHostApduService.SERVICE_INTERFACE), 52589c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen PackageManager.GET_META_DATA, userId); 52689c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen resolvedServices.addAll(resolvedOffHostServices); 5279f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 5289f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen for (ResolveInfo resolvedService : resolvedServices) { 5299f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen try { 53089c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen boolean onHost = !resolvedOffHostServices.contains(resolvedService); 531a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen ServiceInfo si = resolvedService.serviceInfo; 532a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen ComponentName componentName = new ComponentName(si.packageName, si.name); 533a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen if (!android.Manifest.permission.BIND_NFC_SERVICE.equals( 534a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen si.permission)) { 535a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen Log.e(TAG, "Skipping APDU service " + componentName + 536a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen ": it does not require the permission " + 537a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen android.Manifest.permission.BIND_NFC_SERVICE); 538a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen continue; 539a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen } 54089c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen ApduServiceInfo service = new ApduServiceInfo(pm, resolvedService, onHost); 5419f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen if (service != null) { 5429f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen validServices.add(service); 5439f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 5449f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } catch (XmlPullParserException e) { 5459f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen Log.w(TAG, "Unable to load component info " + resolvedService.toString(), e); 5469f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } catch (IOException e) { 5479f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen Log.w(TAG, "Unable to load component info " + resolvedService.toString(), e); 5489f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 5499f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 5509f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 5519f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen synchronized (mLock) { 5529f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen UserServices userServices = findOrCreateUserLocked(userId); 553d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen 554a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen updateFromSettingsLocked(userId); 555a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen 5564358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen // Deal with defaults; if a default app is removed, notify the user. 557a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen ComponentName defaultForPayment = userServices.defaults.get(AID_CATEGORY_PAYMENT); 5584358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen boolean paymentModeAuto = userServices.mode.get(AID_CATEGORY_PAYMENT); 559a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen 560d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen // Find removed services 561a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen Iterator<Map.Entry<ComponentName, ApduServiceInfo>> it = 562d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen userServices.services.entrySet().iterator(); 563d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen while (it.hasNext()) { 564a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen Map.Entry<ComponentName, ApduServiceInfo> entry = 565a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen (Map.Entry<ComponentName, ApduServiceInfo>) it.next(); 566d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen if (!containsServiceLocked(validServices, entry.getKey())) { 567d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen Log.d(TAG, "Service removed: " + entry.getKey()); 5684358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen if (entry.getKey().equals(defaultForPayment) && paymentModeAuto) { 569a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen Log.d(TAG, "Clearing as default for payment"); 5704358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen notifyDefaultServiceRemoved(entry.getValue().loadLabel(pm)); 571a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen } 572d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen it.remove(); 573d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 574d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 575a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen 57675f63db568f953e935e62cb3046d167b881979c8Martijn Coenen int numPaymentApps = 0; 57775f63db568f953e935e62cb3046d167b881979c8Martijn Coenen ApduServiceInfo paymentService = null; 578a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen for (ApduServiceInfo service : validServices) { 579a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen if (DEBUG) Log.d(TAG, "Processing service: " + service.getComponent() + 580a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen "AIDs: " + service.getAids()); 5814358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen ApduServiceInfo existingService = 5824358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen userServices.services.put(service.getComponent(), service); 58375f63db568f953e935e62cb3046d167b881979c8Martijn Coenen if (service.hasCategory(AID_CATEGORY_PAYMENT)) { 58475f63db568f953e935e62cb3046d167b881979c8Martijn Coenen numPaymentApps++; 58575f63db568f953e935e62cb3046d167b881979c8Martijn Coenen paymentService = service; 58675f63db568f953e935e62cb3046d167b881979c8Martijn Coenen } 5874358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen if (existingService != null && existingService.hasCategory(AID_CATEGORY_PAYMENT) && 588a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen !service.hasCategory(AID_CATEGORY_PAYMENT)) { 5894358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen // This is a rather special case but we have to deal with it: what if your default 5904358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen // payment app suddenly stops offering payment AIDs. 5914358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen if (service.getComponent().equals(defaultForPayment) && paymentModeAuto) { 5924358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen notifyDefaultServiceRemoved(existingService.loadLabel(pm)); 593d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 594d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 5959f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 596a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen 5974358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen if (numPaymentApps == 1) { 5984358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen Log.d(TAG, "Only one payment app installed, setting default to: " + paymentService.getComponent()); 59975f63db568f953e935e62cb3046d167b881979c8Martijn Coenen setDefaultServiceForCategory(userId, paymentService.getComponent(), 60075f63db568f953e935e62cb3046d167b881979c8Martijn Coenen AID_CATEGORY_PAYMENT); 60175f63db568f953e935e62cb3046d167b881979c8Martijn Coenen } 60275f63db568f953e935e62cb3046d167b881979c8Martijn Coenen 603a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen // 2. Generate AID category hashmap 604a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen generateAidCategoriesLocked(userServices); 605a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen 606a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen // 3. Rebuild mAidToServices 607d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen generateAidTreeForUserLocked(userServices); 6089f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 609a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen // 4. Generate a quick-lookup AID cache 610a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen generateAidCacheLocked(userServices); 611a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen 612a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen // 5. Recompute routing table from the AID cache 613d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen updateRoutingLocked(userServices); 614d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 6159f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen dump(validServices); 6169f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 6179f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 6184358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen void notifyDefaultServiceRemoved(CharSequence serviceName) { 6194358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen Intent intent = new Intent(mContext, DefaultRemovedActivity.class); 6204358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen intent.putExtra(DefaultRemovedActivity.EXTRA_DEFAULT_NAME, serviceName); 6214358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen mContext.startActivityAsUser(intent, UserHandle.CURRENT); 6224358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen } 6234358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen 624d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen void updateRoutingLocked(UserServices userServices) { 625d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen final Set<String> handledAids = new HashSet<String>(); 626d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen // For each AID, find interested services 627a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen for (Map.Entry<String, ArrayList<ApduServiceInfo>> aidEntry: 628a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen mAidCache.entrySet()) { 629d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen String aid = aidEntry.getKey(); 630a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen ArrayList<ApduServiceInfo> aidServices = aidEntry.getValue(); 631d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen if (aidServices.size() == 0) { 632d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen // No interested services, if there is a current routing remove it 633d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen mRoutingManager.removeAid(aid); 634d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } else if (aidServices.size() == 1) { 635d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen // Only one service, make sure that is the current default route 636a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen ApduServiceInfo service = aidServices.get(0); 63789c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen mRoutingManager.setRouteForAid(aid, service.isOnHost()); 638d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } else if (aidServices.size() > 1) { 639a0b908c58b5ab0d242ccc545d14573901774bd29Martijn Coenen // Multiple services, need to route to host to ask 64089c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen mRoutingManager.setRouteForAid(aid, true); 6419f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 642d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen handledAids.add(aid); 6439f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 644d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen // Now, find AIDs in the routing table that are no longer routed to 645d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen // and remove them. 646d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen Set<String> routedAids = mRoutingManager.getRoutedAids(); 647d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen for (String aid : routedAids) { 648d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen if (!handledAids.contains(aid)) { 649d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen if (DEBUG) Log.d(TAG, "Removing routing for AID " + aid + ", because " + 650d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen "there are no no interested services."); 651d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen mRoutingManager.removeAid(aid); 652d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 653d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 654d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen // And commit the routing 655d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen mRoutingManager.commitRouting(); 6569f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 6579f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen} 658