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 23d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenenimport java.util.HashMap; 24d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenenimport java.util.HashSet; 25d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenenimport java.util.Map; 26d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenenimport java.util.Set; 27d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen 28d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenenpublic class AidRoutingManager { 29d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen static final String TAG = "AidRoutingManager"; 30d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen 31341b2c02da8b4d2a681f3fbcc5657921ad421e32Martijn Coenen static final boolean DBG = false; 32341b2c02da8b4d2a681f3fbcc5657921ad421e32Martijn Coenen 336a707e75739696dde62201047158913411e8cd81Martijn Coenen // This is the default IsoDep protocol route; it means 346a707e75739696dde62201047158913411e8cd81Martijn Coenen // that for any AID that needs to be routed to this 356a707e75739696dde62201047158913411e8cd81Martijn Coenen // destination, we won't need to add a rule to the routing 366a707e75739696dde62201047158913411e8cd81Martijn Coenen // table, because this destination is already the default route. 376a707e75739696dde62201047158913411e8cd81Martijn Coenen // 386a707e75739696dde62201047158913411e8cd81Martijn Coenen // For Nexus devices, the default route is always 0x00. 396a707e75739696dde62201047158913411e8cd81Martijn Coenen static final int DEFAULT_ROUTE = 0x00; 406a707e75739696dde62201047158913411e8cd81Martijn Coenen 41451ba48faa87d78bfbec0597ff06af1747cf6acbMartijn Coenen // For Nexus devices, just a static route to the eSE 4289c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen // OEMs/Carriers could manually map off-host AIDs 434358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen // to the correct eSE/UICC based on state they keep. 4489c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen static final int DEFAULT_OFFHOST_ROUTE = 0xF4; 4589c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen 464358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen final Object mLock = new Object(); 474358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen 48d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen // mAidRoutingTable contains the current routing table. The index is the route ID. 49d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen // The route can include routes to a eSE/UICC. 50d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen final SparseArray<Set<String>> mAidRoutingTable = new SparseArray<Set<String>>(); 51d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen 52d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen // Easy look-up what the route is for a certain AID 53d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen final HashMap<String, Integer> mRouteForAid = new HashMap<String, Integer>(); 54d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen 55d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen // Whether the routing table is dirty 56d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen boolean mDirty; 57d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen 58d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen public AidRoutingManager() { 59d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen 60d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 61d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen 62d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen public boolean aidsRoutedToHost() { 634358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen synchronized(mLock) { 644358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen Set<String> aidsToHost = mAidRoutingTable.get(0); 654358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen return aidsToHost != null && aidsToHost.size() > 0; 664358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen } 67d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 68d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen 69d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen public Set<String> getRoutedAids() { 70d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen Set<String> routedAids = new HashSet<String>(); 714358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen synchronized (mLock) { 724358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen for (Map.Entry<String, Integer> aidEntry : mRouteForAid.entrySet()) { 734358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen routedAids.add(aidEntry.getKey()); 744358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen } 75d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 76d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen return routedAids; 77d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 78d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen 7989c893312d524f50c47b0d32071f3de8631197f3Martijn Coenen public boolean setRouteForAid(String aid, boolean onHost) { 804358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen int route; 814358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen synchronized (mLock) { 824358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen int currentRoute = getRouteForAidLocked(aid); 83341b2c02da8b4d2a681f3fbcc5657921ad421e32Martijn Coenen if (DBG) Log.d(TAG, "Set route for AID: " + aid + ", host: " + onHost + " , current: 0x" + 84451ba48faa87d78bfbec0597ff06af1747cf6acbMartijn Coenen Integer.toHexString(currentRoute)); 854358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen route = onHost ? 0 : DEFAULT_OFFHOST_ROUTE; 864358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen if (route == currentRoute) return true; 874358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen 884358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen if (currentRoute != -1) { 894358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen // Remove current routing 904358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen removeAid(aid); 914358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen } 924358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen Set<String> aids = mAidRoutingTable.get(route); 934358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen if (aids == null) { 944358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen aids = new HashSet<String>(); 954358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen mAidRoutingTable.put(route, aids); 964358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen } 974358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen aids.add(aid); 984358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen mRouteForAid.put(aid, route); 996a707e75739696dde62201047158913411e8cd81Martijn Coenen if (route != DEFAULT_ROUTE) { 1006a707e75739696dde62201047158913411e8cd81Martijn Coenen NfcService.getInstance().routeAids(aid, route); 1016a707e75739696dde62201047158913411e8cd81Martijn Coenen mDirty = true; 1026a707e75739696dde62201047158913411e8cd81Martijn Coenen } 103d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 104d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen return true; 105d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 106d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen 107d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen /** 108d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen * This notifies that the AID routing table in the controller 109d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen * has been cleared (usually due to NFC being turned off). 110d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen */ 111d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen public void onNfccRoutingTableCleared() { 112d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen // The routing table in the controller was cleared 113d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen // To stay in sync, clear our own tables. 1144358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen synchronized (mLock) { 1154358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen mAidRoutingTable.clear(); 1164358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen mRouteForAid.clear(); 1174358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen } 118d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 119d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen 120d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen public boolean removeAid(String aid) { 1214358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen synchronized (mLock) { 1224358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen Integer route = mRouteForAid.get(aid); 1234358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen if (route == null) { 124341b2c02da8b4d2a681f3fbcc5657921ad421e32Martijn Coenen if (DBG) Log.d(TAG, "removeAid(): No existing route for " + aid); 1254358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen return false; 1264358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen } 1274358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen Set<String> aids = mAidRoutingTable.get(route); 1284358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen if (aids == null) return false; 1294358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen aids.remove(aid); 1304358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen mRouteForAid.remove(aid); 1316a707e75739696dde62201047158913411e8cd81Martijn Coenen if (route.intValue() != DEFAULT_ROUTE) { 1326a707e75739696dde62201047158913411e8cd81Martijn Coenen NfcService.getInstance().unrouteAids(aid); 1336a707e75739696dde62201047158913411e8cd81Martijn Coenen mDirty = true; 1346a707e75739696dde62201047158913411e8cd81Martijn Coenen } 135d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 136d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen return true; 137d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 138d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen 139d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen public void commitRouting() { 1404358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen synchronized (mLock) { 1414358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen if (mDirty) { 1424358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen NfcService.getInstance().commitRouting(); 1434358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen mDirty = false; 1444358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen } else { 145341b2c02da8b4d2a681f3fbcc5657921ad421e32Martijn Coenen if (DBG) Log.d(TAG, "Not committing routing because table not dirty."); 1464358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen } 147d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 148d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 149d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen 1504358172f18871d58d5bc2050e4d9d0bf9bc2d5e5Martijn Coenen int getRouteForAidLocked(String aid) { 151d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen Integer route = mRouteForAid.get(aid); 152d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen return route == null ? -1 : route; 153d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen } 154d53c2b599c73f7404b5a604be4d9a5449cafdd72Martijn Coenen} 155