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