RegisteredServicesCache.java revision 717a8bdc43aa9328899df86cb523430466cf5baa
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.XmlPullParser; 209f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport org.xmlpull.v1.XmlPullParserException; 219f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport org.xmlpull.v1.XmlSerializer; 229f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 239f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport android.app.ActivityManager; 249f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport android.content.BroadcastReceiver; 259f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport android.content.ComponentName; 269f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport android.content.Context; 279f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport android.content.Intent; 289f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport android.content.IntentFilter; 299f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport android.content.pm.PackageManager; 309f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport android.content.pm.ResolveInfo; 319f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport android.content.pm.PackageManager.NameNotFoundException; 329f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport android.content.pm.ServiceInfo; 339f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport android.content.res.Resources; 346493859b424f65af79e3e13835f7dfed38495c00Martijn Coenenimport android.content.res.TypedArray; 359f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport android.content.res.XmlResourceParser; 366493859b424f65af79e3e13835f7dfed38495c00Martijn Coenenimport android.nfc.cardemulation.HostApduService; 379f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport android.os.Environment; 389f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport android.os.UserHandle; 399f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport android.util.AtomicFile; 406493859b424f65af79e3e13835f7dfed38495c00Martijn Coenenimport android.util.AttributeSet; 419f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport android.util.Log; 429f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport android.util.SparseArray; 439f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport android.util.Xml; 449f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 459f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport com.android.internal.util.FastXmlSerializer; 469f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport com.google.android.collect.Maps; 479f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 489f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport java.io.File; 499f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport java.io.FileInputStream; 509f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport java.io.FileOutputStream; 519f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport java.io.IOException; 529f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport java.util.ArrayList; 539f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport java.util.HashMap; 549f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport java.util.HashSet; 55d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenenimport java.util.Iterator; 569f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport java.util.List; 579f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport java.util.Map; 589f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport java.util.Set; 599f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport java.util.SortedMap; 609f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport java.util.TreeMap; 619f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenenimport java.util.concurrent.atomic.AtomicReference; 629f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 636493859b424f65af79e3e13835f7dfed38495c00Martijn Coenenpublic class RegisteredServicesCache { 649f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen static final String TAG = "RegisteredAidCache"; 659f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen static final boolean DEBUG = true; 669f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 679f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen final Context mContext; 689f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen final AtomicReference<BroadcastReceiver> mReceiver; 699f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen final AtomicFile mAidDefaultsFile; 70d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen final AidRoutingManager mRoutingManager; 719f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 729f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen final Object mLock = new Object(); 739f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen // All variables below synchronized on mLock 749f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 759f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen // mUserServices holds the card emulation services that are running for each user 769f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen final SparseArray<UserServices> mUserServices = new SparseArray<UserServices>(); 779f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 789f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen // mAidServices is a tree that maps an AID to a list of handling services 799f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen // running on Android. It does *not* include apps that registered AIDs 809f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen // for an eSE/UICC 819f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen final TreeMap<String, ArrayList<CardEmulationService>> mAidToServices = 829f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen new TreeMap<String, ArrayList<CardEmulationService>>(); 839f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 849f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen public static class CardEmulationService { 859f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen public final ComponentName serviceName; 869f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen public final ArrayList<String> aids; 87d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen public final int route; 88d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen public final boolean isPaymentService; 899f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 906493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen CardEmulationService(ComponentName serviceName, ArrayList<String> aids, 916493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen boolean isPaymentService) { 929f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen this.serviceName = serviceName; 939f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen this.aids = aids; 946493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen this.isPaymentService = isPaymentService; 95d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen this.route = 0; // TODO 969f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 979f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 989f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen @Override 999f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen public String toString() { 1009f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen StringBuilder out = new StringBuilder("ComponentInfo: "); 1019f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen out.append(serviceName); 1029f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen out.append(", AIDs: "); 1039f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen for (String aid : aids) { 1049f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen out.append(aid); 1059f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen out.append(", "); 1069f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 1079f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen return out.toString(); 1089f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 109d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen 110d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen @Override 111d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen public boolean equals(Object o) { 112d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen if (this == o) return true; 113d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen if (!(o instanceof CardEmulationService)) return false; 114d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen CardEmulationService thatService = (CardEmulationService) o; 115d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen 116d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen return thatService.serviceName.equals(this.serviceName); 117d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 118d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen 119d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen @Override 120d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen public int hashCode() { 121d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen return serviceName.hashCode(); 122d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 1239f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 1249f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 1259f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen private static class UserServices { 1269f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen public final HashMap<ComponentName, CardEmulationService> services = 1279f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen Maps.newHashMap(); 1289f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen public final HashMap<String, ComponentName> defaults = 1299f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen Maps.newHashMap(); // Persisted on file-system 1309f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen }; 1319f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 1329f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen private UserServices findOrCreateUserLocked(int userId) { 1339f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen UserServices services = mUserServices.get(userId); 1349f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen if (services == null) { 1359f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen services = new UserServices(); 1369f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen mUserServices.put(userId, services); 1379f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 1389f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen return services; 1399f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 1409f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 1416493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen public RegisteredServicesCache(Context context, AidRoutingManager routingManager) { 1429f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen mContext = context; 143d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen mRoutingManager = routingManager; 1449f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 1459f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen File dataDir = Environment.getDataDirectory(); 1469f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen File nfcDir = new File(dataDir, "nfc"); 1479f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen mAidDefaultsFile = new AtomicFile(new File(nfcDir, "aid_defaults")); 1489f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 1499f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen // Load defaults from file 1509f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen readDefaultsFromPersistentFile(); 1519f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 1529f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen final BroadcastReceiver receiver = new BroadcastReceiver() { 1539f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen @Override 154d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen public void onReceive(Context context, Intent intent) { 155d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); 156d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen String action = intent.getAction(); 157d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen if (uid != -1) { 158d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen if (DEBUG) Log.d(TAG, "Intent action: " + action); 159d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen boolean replaced = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false) && 160d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen (Intent.ACTION_PACKAGE_ADDED.equals(action) || 161d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen Intent.ACTION_PACKAGE_REMOVED.equals(action)); 162d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen if (!replaced) { 163d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen generateServicesForUser(UserHandle.getUserId(uid)); 164d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } else { 165d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen if (DEBUG) Log.d(TAG, "Ignoring package intent due to package being replaced."); 166d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 167d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 1689f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 1699f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen }; 1709f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 1719f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen mReceiver = new AtomicReference<BroadcastReceiver>(receiver); 1729f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 1739f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen IntentFilter intentFilter = new IntentFilter(); 1749f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED); 1759f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); 1769f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); 177d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen intentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED); 1789f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen intentFilter.addDataScheme("package"); 1799f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen mContext.registerReceiverAsUser(receiver, UserHandle.ALL, intentFilter, null, null); 1809f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 1819f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen // Register for events related to sdcard operations 1829f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen IntentFilter sdFilter = new IntentFilter(); 1839f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); 1849f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); 1859f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen mContext.registerReceiverAsUser(receiver, UserHandle.ALL, sdFilter, null, null); 1869f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 1879f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 188717a8bdc43aa9328899df86cb523430466cf5baaMartijn Coenen public ArrayList<ComponentName> resolveAidPrefix(String aid) { 1899f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen if (aid == null || aid.length() == 0) return null; 1909f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 191717a8bdc43aa9328899df86cb523430466cf5baaMartijn Coenen ArrayList<ComponentName> matchedServices = new ArrayList<ComponentName>(); 1929f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen synchronized (mLock) { 1939f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen char nextAidChar = (char) (aid.charAt(aid.length() - 1) + 1); 1949f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen String nextAid = aid.substring(0, aid.length() - 1) + nextAidChar; 1959f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen SortedMap<String, ArrayList<CardEmulationService>> matches = mAidToServices.subMap(aid, nextAid); 196717a8bdc43aa9328899df86cb523430466cf5baaMartijn Coenen // The first match is lexicographically closest to what the reader asked; 197717a8bdc43aa9328899df86cb523430466cf5baaMartijn Coenen if (matches.isEmpty()) { 198717a8bdc43aa9328899df86cb523430466cf5baaMartijn Coenen return null; 199717a8bdc43aa9328899df86cb523430466cf5baaMartijn Coenen } else { 200717a8bdc43aa9328899df86cb523430466cf5baaMartijn Coenen Log.d(TAG, "Requested AID: " + aid + " resolved to " + matches.firstKey()); 201717a8bdc43aa9328899df86cb523430466cf5baaMartijn Coenen // Return all component-names registering for this AID 202717a8bdc43aa9328899df86cb523430466cf5baaMartijn Coenen ArrayList<CardEmulationService> serviceMatches = matches.get(matches.firstKey()); 203717a8bdc43aa9328899df86cb523430466cf5baaMartijn Coenen for (CardEmulationService service : serviceMatches) { 204717a8bdc43aa9328899df86cb523430466cf5baaMartijn Coenen matchedServices.add(service.serviceName); 205717a8bdc43aa9328899df86cb523430466cf5baaMartijn Coenen } 2069f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 207717a8bdc43aa9328899df86cb523430466cf5baaMartijn Coenen 208d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 2099f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen return matchedServices; 2109f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 2119f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 212d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen void dump(ArrayList<CardEmulationService> services) { 213d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen for (CardEmulationService service : services) { 214d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen if (DEBUG) Log.d(TAG, service.toString()); 2159f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 2169f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 2179f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 218d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen void generateAidTreeForUserLocked(UserServices userServices) { 219d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen Log.d(TAG, "generateAidTree"); 220d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen // Easiest is to just build the entire tree again 221d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen mAidToServices.clear(); 222d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen for (CardEmulationService service : userServices.services.values()) { 223d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen Log.d(TAG, "generateAidTree component: " + service.serviceName); 224d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen for (String aid : service.aids) { 225d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen Log.d(TAG, "generateAidTree AID: " + aid); 226d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen // Check if a mapping exists for this AID 227d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen if (mAidToServices.containsKey(aid)) { 228d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen final ArrayList<CardEmulationService> aidServices = mAidToServices.get(aid); 229d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen aidServices.add(service); 230d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } else { 231d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen final ArrayList<CardEmulationService> aidServices = 232d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen new ArrayList<CardEmulationService>(); 233d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen aidServices.add(service); 234d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen mAidToServices.put(aid, aidServices); 235d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 236d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 237d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 238d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 239d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen 240d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen void addAidsForServiceLocked(CardEmulationService service, ArrayList<String> aids) { 241d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen service.aids.addAll(aids); 242d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 243d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen 244d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen void removeAidsForServiceLocked(UserServices services, CardEmulationService service, 245d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen ArrayList<String> aids) { 246d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen for (String aid : aids) { 247d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen if (services.defaults.containsKey(aid) && 248d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen services.defaults.get(aid).equals(service.serviceName)) { 249d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen // Remove default 250d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen if (DEBUG) Log.d(TAG, "Removing default service for AID: " + aid); 251d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen services.defaults.remove(aid); 252d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 253d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen service.aids.remove(aid); 254d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 255d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 256d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen 257d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen boolean containsServiceLocked(ArrayList<CardEmulationService> services, ComponentName serviceName) { 2589f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen for (CardEmulationService service : services) { 259d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen if (service.serviceName.equals(serviceName)) return true; 2609f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 261d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen return false; 2629f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 2639f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 264d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen public void generateServicesForUser(int userId) { 265d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen // This code consists of 3 main phases: 266d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen // - Finding all HCE services and making sure mUserServices is correct 267d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen // - Rebuilding the mAidToServices lookup table from userServices 268d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen // - Rebuilding the AID routing table from mAidToServices lookup table 269d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen 270d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen // 1. Finding all HCE services and making sure mUserServices is correct 2719f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen PackageManager pm; 2729f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen try { 2739f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen pm = mContext.createPackageContextAsUser("android", 0, 274717a8bdc43aa9328899df86cb523430466cf5baaMartijn Coenen new UserHandle(userId)).getPackageManager(); 2759f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } catch (NameNotFoundException e) { 2769f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen Log.e(TAG, "Could not create user package context"); 2779f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen return; 2789f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 2799f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 2809f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen ArrayList<CardEmulationService> validServices = new ArrayList<CardEmulationService>(); 2819f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 2826493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen List<ResolveInfo> resolvedServices = pm.queryIntentServicesAsUser( 2836493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen new Intent(HostApduService.SERVICE_INTERFACE), 2849f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen PackageManager.GET_META_DATA, ActivityManager.getCurrentUser()); 2859f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 2869f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen for (ResolveInfo resolvedService : resolvedServices) { 2879f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen try { 2889f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen CardEmulationService service = parseServiceInfo(pm, resolvedService); 2899f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen if (service != null) { 2909f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen validServices.add(service); 2919f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 2929f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } catch (XmlPullParserException e) { 2939f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen Log.w(TAG, "Unable to load component info " + resolvedService.toString(), e); 2949f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } catch (IOException e) { 2959f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen Log.w(TAG, "Unable to load component info " + resolvedService.toString(), e); 2969f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 2979f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 2989f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 299d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen 3009f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen synchronized (mLock) { 3019f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen UserServices userServices = findOrCreateUserLocked(userId); 302d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen 303d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen // Find removed services 304d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen Iterator<Map.Entry<ComponentName, CardEmulationService>> it = 305d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen userServices.services.entrySet().iterator(); 306d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen while (it.hasNext()) { 307d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen Map.Entry<ComponentName, CardEmulationService> entry = 308d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen (Map.Entry<ComponentName, CardEmulationService>) it.next(); 309d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen if (!containsServiceLocked(validServices, entry.getKey())) { 310d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen Log.d(TAG, "Service removed: " + entry.getKey()); 311d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen it.remove(); 312d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 313d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 3149f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen for (CardEmulationService service : validServices) { 315d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen if (DEBUG) Log.d(TAG, "Processing service: " + service.serviceName + 316d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen "AIDs: " + service.aids); 317d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen // TODO this is O(N^2) and not very nice 318d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen if (userServices.services.containsKey(service.serviceName)) { 319d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen ArrayList<String> addedAids = new ArrayList<String>(); 320d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen ArrayList<String> removedAids = new ArrayList<String>(); 321d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen CardEmulationService existingService = userServices.services.get(service.serviceName); 322d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen for (String aid : existingService.aids) { 323d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen if (!service.aids.contains(aid)) removedAids.add(aid); 324d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 325d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen for (String aid : service.aids) { 326d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen if (!existingService.aids.contains(aid)) addedAids.add(aid); 327d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 328d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen addAidsForServiceLocked(existingService, addedAids); 329d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen removeAidsForServiceLocked(userServices, existingService, removedAids); 330d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } else { 331d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen // New service, just store it 332d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen userServices.services.put(service.serviceName, service); 333d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 3349f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 335d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen // 2. Rebuild mAidToServices 336d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen generateAidTreeForUserLocked(userServices); 3379f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 338d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen // 3. Recompute routing table 339d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen updateRoutingLocked(userServices); 340d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 3419f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen dump(validServices); 3429f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 3439f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 344d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen void updateRoutingLocked(UserServices userServices) { 345d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen final Set<String> handledAids = new HashSet<String>(); 346d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen // For each AID, find interested services 347d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen for (Map.Entry<String, ArrayList<CardEmulationService>> aidEntry : 348d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen mAidToServices.entrySet()) { 349d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen String aid = aidEntry.getKey(); 350d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen ArrayList<CardEmulationService> aidServices = aidEntry.getValue(); 351d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen // Find the current routing for this AID 352d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen int currentRoute = mRoutingManager.getRouteForAid(aid); 353d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen if (DEBUG) Log.d(TAG, "updateRouting: AID " + aid + " registered service count: " + 354d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen Integer.toString(aidServices.size()) + " current route: " + 355d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen Integer.toString(currentRoute)); 356d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen if (aidServices.size() == 0) { 357d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen // No interested services, if there is a current routing remove it 358d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen mRoutingManager.removeAid(aid); 359d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } else if (aidServices.size() == 1) { 360d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen // Only one service, make sure that is the current default route 361d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen CardEmulationService service = aidServices.get(0); 362d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen mRoutingManager.setRouteForAid(aid, service.route); 363d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } else if (aidServices.size() > 1) { 364d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen // Multiple services, check for default, if no default, 365d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen // route to host to ask for default. 366d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen if (userServices.defaults.get(aid) == null) { 367d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen if (currentRoute != 0) { // TODO constant for host 368d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen mRoutingManager.setRouteForAid(aid, 0); 369d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 370d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } else { 371d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen // Default is set, make sure the current route points to it 372d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen ComponentName defaultServiceName = userServices.defaults.get(aid); 373d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen CardEmulationService defaultService = 374d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen userServices.services.get(defaultServiceName); 375d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen if (defaultService != null && currentRoute != defaultService.route) { 376d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen mRoutingManager.setRouteForAid(aid, defaultService.route); 3779f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 3789f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 3799f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 380d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen handledAids.add(aid); 3819f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 382d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen // Now, find AIDs in the routing table that are no longer routed to 383d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen // and remove them. 384d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen Set<String> routedAids = mRoutingManager.getRoutedAids(); 385d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen for (String aid : routedAids) { 386d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen if (!handledAids.contains(aid)) { 387d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen if (DEBUG) Log.d(TAG, "Removing routing for AID " + aid + ", because " + 388d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen "there are no no interested services."); 389d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen mRoutingManager.removeAid(aid); 390d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 391d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 392d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen 393d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen // And commit the routing 394d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen mRoutingManager.commitRouting(); 3959f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 3969f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 3979f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen CardEmulationService parseServiceInfo(PackageManager pm, ResolveInfo info) throws XmlPullParserException, IOException { 3989f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen ServiceInfo si = info.serviceInfo; 3999f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 4009f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen XmlResourceParser parser = null; 4019f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen try { 4026493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen parser = si.loadXmlMetaData(pm, HostApduService.SERVICE_META_DATA); 4039f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen if (parser == null) { 4046493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen throw new XmlPullParserException("No " + HostApduService.SERVICE_META_DATA + 4056493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen " meta-data"); 4069f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 4079f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 4089f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen return parseAidList(pm.getResourcesForApplication(si.applicationInfo), si.packageName, 4099f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen parser, info); 4109f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } catch (NameNotFoundException e) { 4119f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen throw new XmlPullParserException("Unable to load resources for " + si.packageName); 4129f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } finally { 4139f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen if (parser != null) parser.close(); 4149f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 4159f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 4169f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 4179f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen CardEmulationService parseAidList(Resources res, String packageName, XmlPullParser parser, 4189f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen ResolveInfo resolveInfo) 4199f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen throws XmlPullParserException, IOException { 4209f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen int eventType = parser.getEventType(); 4216493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen while (eventType != XmlPullParser.START_TAG && eventType != XmlPullParser.END_DOCUMENT) { 4229f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen eventType = parser.next(); 4239f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 4249f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 4256493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen String tagName = parser.getName(); 4266493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen if (!"apdu-service".equals(tagName)) { 4276493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen throw new XmlPullParserException( 4286493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen "Meta-data does not start with <apdu-service> tag"); 4296493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen } 4306493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen 4316493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen AttributeSet attrs = Xml.asAttributeSet(parser); 4326493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen TypedArray sa = res.obtainAttributes(attrs, 433717a8bdc43aa9328899df86cb523430466cf5baaMartijn Coenen com.android.internal.R.styleable.HostApduService); 4346493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen String description = sa.getString( 435717a8bdc43aa9328899df86cb523430466cf5baaMartijn Coenen com.android.internal.R.styleable.HostApduService_description); 4369f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen ArrayList<String> items = new ArrayList<String>(); 4376493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen final int depth = parser.getDepth(); 4386493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen while (((eventType = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) 4396493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen && eventType != XmlPullParser.END_DOCUMENT) { 4409f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen tagName = parser.getName(); 4416493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen if (eventType == XmlPullParser.START_TAG && "aid-filter".equals(tagName)) { 4426493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen final TypedArray a = res.obtainAttributes(attrs, 4436493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen com.android.internal.R.styleable.AidFilter); 444a56a84a384db51809101e149d83bf41b5e198ca0Martijn Coenen String aid = a.getString(com.android.internal.R.styleable.AidFilter_name); 4456493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen String aidDescription = a.getString( 4466493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen com.android.internal.R.styleable.AidFilter_description); 447717a8bdc43aa9328899df86cb523430466cf5baaMartijn Coenen String category = a.getString( 448717a8bdc43aa9328899df86cb523430466cf5baaMartijn Coenen com.android.internal.R.styleable.AidFilter_category); 4496493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen items.add(aid); 4509f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 4516493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen } 4529f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 4536493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen int size = items.size(); 4546493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen if (size > 0) { 4556493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen final ArrayList<String> aids = new ArrayList<String>(); 4566493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen for (String aid : items) { 4576493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen Log.e(TAG, "Checking AID: " + aid); 4586493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen if (isValidAid(aid)) aids.add(aid); 4596493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen } 4606493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen if (aids.size() > 0) { 4616493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen return new CardEmulationService( 4626493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen new ComponentName(resolveInfo.serviceInfo.packageName, 463717a8bdc43aa9328899df86cb523430466cf5baaMartijn Coenen resolveInfo.serviceInfo.name), aids, false); 4646493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen } else { 4656493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen Log.e(TAG, "Could not find any valid aid-filer tags"); 4666493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen } 4676493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen } 4689f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen return null; 4699f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 4709f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 4719f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen /** 4729f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen * Parses whether a String contains a valid AID. Rules: 4739f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen * - AID may not be null or empty 4749f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen * - Number of characters is even (ie 0123 is valid, 123 is not) 4759f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen * - TODO min prefix length? 4769f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen */ 4779f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen boolean isValidAid(String aid) { 4789f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen if (aid == null) 4799f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen return false; 4809f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 4819f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen int aidLength = aid.length(); 4829f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen if (aidLength == 0 || (aidLength % 2) != 0) { 4839f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen Log.e(TAG, "AID " + aid + " is not correctly formatted."); 4849f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen return false; 4859f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 4869f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen return true; 4879f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 4889f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 4899f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen void readDefaultsFromPersistentFile() { 4909f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen FileInputStream fis = null; 4919f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen try { 4929f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen if (!mAidDefaultsFile.getBaseFile().exists()) { 493d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen if (DEBUG) Log.d(TAG, "defaults file did not exist"); 4949f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen return; 4959f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 4969f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen fis = mAidDefaultsFile.openRead(); 4979f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen XmlPullParser parser = Xml.newPullParser(); 4989f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen parser.setInput(fis, null); 4999f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen int eventType = parser.getEventType(); 5009f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen while (eventType != XmlPullParser.START_TAG && 5019f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen eventType != XmlPullParser.END_DOCUMENT) { 5029f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen eventType = parser.next(); 5039f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 5049f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen String tagName = parser.getName(); 5059f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen UserServices userServices = null; 5069f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen synchronized (mLock) { 5079f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen do { 5089f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen if (eventType == XmlPullParser.START_TAG && "services".equals(tagName)) { 5099f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen int currentUserId = Integer.parseInt(parser.getAttributeValue(0)); 5109f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen userServices = findOrCreateUserLocked(currentUserId); 5119f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 5129f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 5139f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen if (eventType == XmlPullParser.START_TAG && "aid".equals(tagName) && 5149f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen userServices != null) { 5159f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen String aid = parser.getAttributeValue(0); 5169f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen String component = parser.getAttributeValue(1); 5179f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 5189f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen ComponentName componentName = ComponentName.unflattenFromString(component); 5199f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen if (componentName != null) { 5209f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen userServices.defaults.put(aid, componentName); 5219f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 5229f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen else { 5239f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen Log.e(TAG, "Could not unflatten component string " + component); 5249f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 5259f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 5269f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen if (eventType == XmlPullParser.END_TAG && "services".equals(tagName)) { 5279f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen // Done with current map 5289f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen userServices = null; 5299f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 5309f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen eventType = parser.next(); 5319f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen tagName = parser.getName(); 5329f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } while (eventType != XmlPullParser.END_DOCUMENT); 5339f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 5349f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } catch (Exception e) { 5359f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen Log.e(TAG, "Error reading AID defaults", e); 5369f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } finally { 5379f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen if (fis != null) { 5389f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen try { 5399f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen fis.close(); 5409f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } catch (java.io.IOException e1) { 5419f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 5429f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 5439f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 5449f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 5459f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen 5469f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen void writeDefaultsToPersistentFile() { 5479f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen FileOutputStream fos = null; 5489f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen try { 5499f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen fos = mAidDefaultsFile.startWrite(); 5509f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen XmlSerializer out = new FastXmlSerializer(); 5519f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen out.setOutput(fos, "utf-8"); 5529f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen out.startDocument(null, true); 5539f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); 5549f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen synchronized (mLock) { 5559f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen for (int i = 0; i < mUserServices.size(); i++) { 5569f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen final UserServices userServices = mUserServices.valueAt(i); 5579f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen out.startTag(null, "services"); 5589f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen out.attribute(null, "userId", Integer.toString(mUserServices.keyAt(i))); 5599f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen for (Map.Entry<String, ComponentName> aidDefault : userServices.defaults.entrySet()) { 5609f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen out.startTag(null, "aid"); 5619f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen out.attribute(null, "value", aidDefault.getKey()); 5629f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen out.attribute(null, "defaultComponent", 5639f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen aidDefault.getValue().flattenToShortString()); 5649f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen out.endTag(null, "aid"); 5659f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 5669f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen out.endTag(null, "services"); 5679f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 5689f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 5699f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen out.endDocument(); 5709f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen mAidDefaultsFile.finishWrite(fos); 5719f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } catch (java.io.IOException e) { 5729f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen if (fos != null) { 5739f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen mAidDefaultsFile.failWrite(fos); 5749f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 5759f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 5769f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen } 5779f8f6cf9c58405ecafe2d425801e6c14088db8c7Martijn Coenen} 578