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