16493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen/* 26493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen * Copyright (C) 2013 The Android Open Source Project 36493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen * 46493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen * Licensed under the Apache License, Version 2.0 (the "License"); 56493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen * you may not use this file except in compliance with the License. 66493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen * You may obtain a copy of the License at 76493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen * 86493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen * http://www.apache.org/licenses/LICENSE-2.0 96493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen * 106493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen * Unless required by applicable law or agreed to in writing, software 116493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen * distributed under the License is distributed on an "AS IS" BASIS, 126493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 136493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen * See the License for the specific language governing permissions and 146493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen * limitations under the License. 156493859b424f65af79e3e13835f7dfed38495c00Martijn Coenen */ 16d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenenpackage com.android.nfc.cardemulation; 17d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen 18d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenenimport android.util.Log; 19d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenenimport android.util.SparseArray; 20d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen 21d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenenimport com.android.nfc.NfcService; 22d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen 23af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenenimport java.io.FileDescriptor; 24af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenenimport java.io.PrintWriter; 25d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenenimport java.util.HashMap; 26d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenenimport java.util.HashSet; 27d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenenimport java.util.Map; 28d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenenimport java.util.Set; 29d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen 30d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenenpublic class AidRoutingManager { 31d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen static final String TAG = "AidRoutingManager"; 32d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen 33410f4b955283be0187cd2933bdea07c66e101639Martijn Coenen static final boolean DBG = false; 3431208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen 3531208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen static final int ROUTE_HOST = 0x00; 3631208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen 3731208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen // Every routing table entry is matched exact 3831208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen static final int AID_MATCHING_EXACT_ONLY = 0x00; 3931208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen // Every routing table entry can be matched either exact or prefix 4031208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen static final int AID_MATCHING_EXACT_OR_PREFIX = 0x01; 4131208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen // Every routing table entry is matched as a prefix 4231208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen static final int AID_MATCHING_PREFIX_ONLY = 0x02; 43341b2c02da8b4d2a681f3fbcc5657921ad421e32Martijn Coenen 446a707e75739696dde62201047158913411e8cd81Martijn Coenen // This is the default IsoDep protocol route; it means 456a707e75739696dde62201047158913411e8cd81Martijn Coenen // that for any AID that needs to be routed to this 466a707e75739696dde62201047158913411e8cd81Martijn Coenen // destination, we won't need to add a rule to the routing 476a707e75739696dde62201047158913411e8cd81Martijn Coenen // table, because this destination is already the default route. 486a707e75739696dde62201047158913411e8cd81Martijn Coenen // 496a707e75739696dde62201047158913411e8cd81Martijn Coenen // For Nexus devices, the default route is always 0x00. 50ea8f37ee5376e296c3b67f57946b73e48ad88c5cEvan Chu final int mDefaultRoute; 516a707e75739696dde62201047158913411e8cd81Martijn Coenen 52451ba48faa87d78bfbec0597ff06af1747cf6acbMartijn Coenen // For Nexus devices, just a static route to the eSE 5389c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen // OEMs/Carriers could manually map off-host AIDs 544358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen // to the correct eSE/UICC based on state they keep. 55ea8f37ee5376e296c3b67f57946b73e48ad88c5cEvan Chu final int mDefaultOffHostRoute; 5689c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen 5731208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen // How the NFC controller can match AIDs in the routing table; 5831208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen // see AID_MATCHING constants 5931208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen final int mAidMatchingSupport; 6031208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen 614358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen final Object mLock = new Object(); 624358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen 63d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen // mAidRoutingTable contains the current routing table. The index is the route ID. 64d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen // The route can include routes to a eSE/UICC. 6531208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen SparseArray<Set<String>> mAidRoutingTable = 6631208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen new SparseArray<Set<String>>(); 67d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen 68d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen // Easy look-up what the route is for a certain AID 6931208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen HashMap<String, Integer> mRouteForAid = new HashMap<String, Integer>(); 70d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen 71ea8f37ee5376e296c3b67f57946b73e48ad88c5cEvan Chu private native int doGetDefaultRouteDestination(); 72ea8f37ee5376e296c3b67f57946b73e48ad88c5cEvan Chu private native int doGetDefaultOffHostRouteDestination(); 7331208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen private native int doGetAidMatchingMode(); 7431208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen 75d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen public AidRoutingManager() { 76ea8f37ee5376e296c3b67f57946b73e48ad88c5cEvan Chu mDefaultRoute = doGetDefaultRouteDestination(); 77ea8f37ee5376e296c3b67f57946b73e48ad88c5cEvan Chu if (DBG) Log.d(TAG, "mDefaultRoute=0x" + Integer.toHexString(mDefaultRoute)); 78ea8f37ee5376e296c3b67f57946b73e48ad88c5cEvan Chu mDefaultOffHostRoute = doGetDefaultOffHostRouteDestination(); 79ea8f37ee5376e296c3b67f57946b73e48ad88c5cEvan Chu if (DBG) Log.d(TAG, "mDefaultOffHostRoute=0x" + Integer.toHexString(mDefaultOffHostRoute)); 8031208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen mAidMatchingSupport = doGetAidMatchingMode(); 8131208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen if (DBG) Log.d(TAG, "mAidMatchingSupport=0x" + Integer.toHexString(mAidMatchingSupport)); 82d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 83d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen 8431208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen public boolean supportsAidPrefixRouting() { 8531208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen return mAidMatchingSupport == AID_MATCHING_EXACT_OR_PREFIX || 8631208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen mAidMatchingSupport == AID_MATCHING_PREFIX_ONLY; 87d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 88d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen 8931208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen void clearNfcRoutingTableLocked() { 9031208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen for (Map.Entry<String, Integer> aidEntry : mRouteForAid.entrySet()) { 9131208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen NfcService.getInstance().unrouteAids(aidEntry.getKey()); 92d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 93d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 94d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen 9531208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen public boolean configureRouting(HashMap<String, Boolean> aidMap) { 9631208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen SparseArray<Set<String>> aidRoutingTable = new SparseArray<Set<String>>(aidMap.size()); 9731208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen HashMap<String, Integer> routeForAid = new HashMap<String, Integer>(aidMap.size()); 9831208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen // Then, populate internal data structures first 9931208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen for (Map.Entry<String, Boolean> aidEntry : aidMap.entrySet()) { 10031208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen int route = aidEntry.getValue() ? ROUTE_HOST : mDefaultOffHostRoute; 10131208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen String aid = aidEntry.getKey(); 10231208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen Set<String> entries = aidRoutingTable.get(route, new HashSet<String>()); 10331208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen entries.add(aid); 10431208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen aidRoutingTable.put(route, entries); 10531208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen routeForAid.put(aid, route); 10631208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen } 10731208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen 1084358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen synchronized (mLock) { 10931208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen if (routeForAid.equals(mRouteForAid)) { 11031208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen if (DBG) Log.d(TAG, "Routing table unchanged, not updating"); 11131208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen return false; 1124358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen } 11331208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen 11431208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen // Otherwise, update internal structures and commit new routing 11531208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen clearNfcRoutingTableLocked(); 11631208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen mRouteForAid = routeForAid; 11731208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen mAidRoutingTable = aidRoutingTable; 11831208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen if (mAidMatchingSupport == AID_MATCHING_PREFIX_ONLY) { 11931208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen /* If a non-default route registers an exact AID which is shorter 12031208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen * than this exact AID, this will create a problem with controllers 12131208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen * that treat every AID in the routing table as a prefix. 12231208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen * For example, if App A registers F0000000041010 as an exact AID, 12331208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen * and App B registers F000000004 as an exact AID, and App B is not 12431208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen * the default route, the following would be added to the routing table: 12531208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen * F000000004 -> non-default destination 12631208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen * However, because in this mode, the controller treats every routing table 12731208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen * entry as a prefix, it means F0000000041010 would suddenly go to the non-default 12831208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen * destination too, whereas it should have gone to the default. 12931208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen * 13031208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen * The only way to prevent this is to add the longer AIDs of the 13131208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen * default route at the top of the table, so they will be matched first. 13231208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen */ 13331208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen Set<String> defaultRouteAids = mAidRoutingTable.get(mDefaultRoute); 13431208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen if (defaultRouteAids != null) { 13531208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen for (String defaultRouteAid : defaultRouteAids) { 13631208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen // Check whether there are any shorted AIDs routed to non-default 13731208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen // TODO this is O(N^2) run-time complexity... 13831208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen for (Map.Entry<String, Integer> aidEntry : mRouteForAid.entrySet()) { 13931208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen String aid = aidEntry.getKey(); 14031208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen int route = aidEntry.getValue(); 14131208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen if (defaultRouteAid.startsWith(aid) && route != mDefaultRoute) { 14231208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen if (DBG) 14331208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen Log.d(TAG, "Adding AID " + defaultRouteAid + " for default " + 14431208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen "route, because a conflicting shorter AID will be " + 14531208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen "added to the routing table"); 14631208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen NfcService.getInstance().routeAids(defaultRouteAid, mDefaultRoute); 14731208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen } 14831208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen } 14931208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen } 15031208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen } 1514358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen } 15231208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen 15331208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen // Add AID entries for all non-default routes 15431208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen for (int i = 0; i < mAidRoutingTable.size(); i++) { 15531208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen int route = mAidRoutingTable.keyAt(i); 15631208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen if (route != mDefaultRoute) { 15731208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen Set<String> aidsForRoute = mAidRoutingTable.get(route); 15831208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen for (String aid : aidsForRoute) { 15931208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen if (aid.endsWith("*")) { 16031208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen if (mAidMatchingSupport == AID_MATCHING_EXACT_ONLY) { 16131208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen Log.e(TAG, "This device does not support prefix AIDs."); 16231208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen } else if (mAidMatchingSupport == AID_MATCHING_PREFIX_ONLY) { 16331208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen if (DBG) Log.d(TAG, "Routing prefix AID " + aid + " to route " 16431208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen + Integer.toString(route)); 16531208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen // Cut off '*' since controller anyway treats all AIDs as a prefix 16631208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen NfcService.getInstance().routeAids(aid.substring(0, 16731208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen aid.length() - 1), route); 16831208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen } else if (mAidMatchingSupport == AID_MATCHING_EXACT_OR_PREFIX) { 16931208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen if (DBG) Log.d(TAG, "Routing prefix AID " + aid + " to route " 17031208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen + Integer.toString(route)); 17131208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen NfcService.getInstance().routeAids(aid, route); 17231208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen } 17331208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen } else { 17431208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen if (DBG) Log.d(TAG, "Routing exact AID " + aid + " to route " 17531208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen + Integer.toString(route)); 17631208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen NfcService.getInstance().routeAids(aid, route); 17731208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen } 17831208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen } 17931208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen } 1806a707e75739696dde62201047158913411e8cd81Martijn Coenen } 181d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 18231208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen 18331208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen // And finally commit the routing 18431208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen NfcService.getInstance().commitRouting(); 18531208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen 186d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen return true; 187d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 188d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen 189d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen /** 190d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen * This notifies that the AID routing table in the controller 191d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen * has been cleared (usually due to NFC being turned off). 192d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen */ 193d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen public void onNfccRoutingTableCleared() { 194d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen // The routing table in the controller was cleared 195d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen // To stay in sync, clear our own tables. 1964358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen synchronized (mLock) { 1974358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen mAidRoutingTable.clear(); 1984358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen mRouteForAid.clear(); 1994358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen } 200d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 201d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen 202af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 203af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen pw.println("Routing table:"); 204ea8f37ee5376e296c3b67f57946b73e48ad88c5cEvan Chu pw.println(" Default route: " + ((mDefaultRoute == 0x00) ? "host" : "secure element")); 205af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen synchronized (mLock) { 206af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen for (int i = 0; i < mAidRoutingTable.size(); i++) { 207af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen Set<String> aids = mAidRoutingTable.valueAt(i); 208af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen pw.println(" Routed to 0x" + Integer.toHexString(mAidRoutingTable.keyAt(i)) + ":"); 209af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen for (String aid : aids) { 210af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen pw.println(" \"" + aid + "\""); 211af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen } 212af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen } 213af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen } 214af3e301d7820bc0a2447db8af16ab5335e6bd520Martijn Coenen } 215d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen} 216