177f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvistpackage com.android.server.wifi.hotspot2;
277f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
377f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvistimport android.util.Log;
477f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
577f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvistimport com.android.server.wifi.ScanDetail;
6d3fb9cbb12d013dd70e672ace5c41ab18a3679a0Vinit Deshpandeimport com.android.server.wifi.WifiConfigStore;
777f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvistimport com.android.server.wifi.WifiNative;
877f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvistimport com.android.server.wifi.anqp.ANQPElement;
977f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvistimport com.android.server.wifi.anqp.ANQPFactory;
1077f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvistimport com.android.server.wifi.anqp.Constants;
1177f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvistimport com.android.server.wifi.anqp.eap.AuthParam;
1277f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvistimport com.android.server.wifi.anqp.eap.EAP;
1377f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvistimport com.android.server.wifi.anqp.eap.EAPMethod;
1477f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvistimport com.android.server.wifi.hotspot2.pps.Credential;
1577f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
1677f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvistimport java.io.BufferedReader;
1777f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvistimport java.io.IOException;
1877f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvistimport java.io.StringReader;
1977f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvistimport java.net.ProtocolException;
207ec7de9f415179e38a39e7b1be917ba77e3b68ebJan Nordqvistimport java.nio.BufferUnderflowException;
2177f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvistimport java.nio.ByteBuffer;
2277f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvistimport java.nio.ByteOrder;
2377f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvistimport java.nio.CharBuffer;
2477f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvistimport java.nio.charset.CharacterCodingException;
2577f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvistimport java.nio.charset.StandardCharsets;
2677f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvistimport java.util.ArrayList;
2777f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvistimport java.util.HashMap;
2877f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvistimport java.util.List;
2977f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvistimport java.util.Map;
3077f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
31ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvistpublic class SupplicantBridge {
3277f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist    private final WifiNative mSupplicantHook;
33d3fb9cbb12d013dd70e672ace5c41ab18a3679a0Vinit Deshpande    private final WifiConfigStore mConfigStore;
34ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist    private final Map<Long, ScanDetail> mRequestMap = new HashMap<>();
3577f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
36ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist    private static final Map<String, Constants.ANQPElementType> sWpsNames = new HashMap<>();
3777f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
3877f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist    static {
3977f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        sWpsNames.put("anqp_venue_name", Constants.ANQPElementType.ANQPVenueName);
4077f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        sWpsNames.put("anqp_network_auth_type", Constants.ANQPElementType.ANQPNwkAuthType);
4177f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        sWpsNames.put("anqp_roaming_consortium", Constants.ANQPElementType.ANQPRoamingConsortium);
4277f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        sWpsNames.put("anqp_ip_addr_type_availability",
4377f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                Constants.ANQPElementType.ANQPIPAddrAvailability);
4477f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        sWpsNames.put("anqp_nai_realm", Constants.ANQPElementType.ANQPNAIRealm);
4577f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        sWpsNames.put("anqp_3gpp", Constants.ANQPElementType.ANQP3GPPNetwork);
4677f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        sWpsNames.put("anqp_domain_name", Constants.ANQPElementType.ANQPDomName);
4777f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        sWpsNames.put("hs20_operator_friendly_name", Constants.ANQPElementType.HSFriendlyName);
4877f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        sWpsNames.put("hs20_wan_metrics", Constants.ANQPElementType.HSWANMetrics);
4977f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        sWpsNames.put("hs20_connection_capability", Constants.ANQPElementType.HSConnCapability);
5077f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        sWpsNames.put("hs20_operating_class", Constants.ANQPElementType.HSOperatingclass);
5177f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        sWpsNames.put("hs20_osu_providers_list", Constants.ANQPElementType.HSOSUProviders);
5277f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist    }
5377f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
5477f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist    public static boolean isAnqpAttribute(String line) {
5577f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        int split = line.indexOf('=');
5677f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        return split >= 0 && sWpsNames.containsKey(line.substring(0, split));
5777f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist    }
5877f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
59d3fb9cbb12d013dd70e672ace5c41ab18a3679a0Vinit Deshpande    public SupplicantBridge(WifiNative supplicantHook, WifiConfigStore configStore) {
6077f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        mSupplicantHook = supplicantHook;
61d3fb9cbb12d013dd70e672ace5c41ab18a3679a0Vinit Deshpande        mConfigStore = configStore;
6277f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist    }
6377f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
6477f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist    public static Map<Constants.ANQPElementType, ANQPElement> parseANQPLines(List<String> lines) {
6577f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        if (lines == null) {
6677f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            return null;
6777f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        }
6831891afce40b903ada9b24ec12e3648ae6aa27b2Jan Nordqvist        Map<Constants.ANQPElementType, ANQPElement> elements = new HashMap<>(lines.size());
6977f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        for (String line : lines) {
7077f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            try {
7177f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                ANQPElement element = buildElement(line);
7277f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                if (element != null) {
7377f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                    elements.put(element.getID(), element);
7477f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                }
7577f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            }
7677f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            catch (ProtocolException pe) {
771c03d75c73b9f5fa24a795a0d546f4f56b82ab9bJan Nordqvist                Log.e(Utils.hs2LogTag(SupplicantBridge.class), "Failed to parse ANQP: " + pe);
7877f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            }
7977f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        }
8077f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        return elements;
8177f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist    }
8277f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
8377f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist    public void startANQP(ScanDetail scanDetail) {
8477f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        String anqpGet = buildWPSQueryRequest(scanDetail.getNetworkDetail());
85ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist        synchronized (mRequestMap) {
86ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist            mRequestMap.put(scanDetail.getNetworkDetail().getBSSID(), scanDetail);
8777f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        }
88ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist        String result = mSupplicantHook.doCustomCommand(anqpGet);
895ef40f88bab7e645a5a9e42aeae768d3b916c02cJan Nordqvist        if (result != null && result.startsWith("OK")) {
905bee0e4616e2f8025d60cbfe3eec3e274a68a452Jan Nordqvist            Log.d(Utils.hs2LogTag(getClass()), "ANQP initiated on " + scanDetail);
9131891afce40b903ada9b24ec12e3648ae6aa27b2Jan Nordqvist        }
9231891afce40b903ada9b24ec12e3648ae6aa27b2Jan Nordqvist        else {
9331891afce40b903ada9b24ec12e3648ae6aa27b2Jan Nordqvist            Log.d(Utils.hs2LogTag(getClass()), "ANQP failed on " +
945bee0e4616e2f8025d60cbfe3eec3e274a68a452Jan Nordqvist                    scanDetail + ": " + result);
9577f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        }
9677f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist    }
9777f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
98ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist    public void notifyANQPDone(Long bssid, boolean success) {
99ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist        ScanDetail scanDetail;
100ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist        synchronized (mRequestMap) {
101ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist            scanDetail = mRequestMap.remove(bssid);
102ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist        }
103ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist        if (scanDetail == null) {
1045bee0e4616e2f8025d60cbfe3eec3e274a68a452Jan Nordqvist            Log.d(Utils.hs2LogTag(getClass()), String.format("Spurious %s ANQP response for %012x",
1055bee0e4616e2f8025d60cbfe3eec3e274a68a452Jan Nordqvist                            success ? "successful" : "failed", bssid));
106ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist            return;
107ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist        }
10877f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
109ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist        String bssData = mSupplicantHook.scanResult(scanDetail.getBSSIDString());
11077f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        try {
11177f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            Map<Constants.ANQPElementType, ANQPElement> elements = parseWPSData(bssData);
1125bee0e4616e2f8025d60cbfe3eec3e274a68a452Jan Nordqvist            Log.d(Utils.hs2LogTag(getClass()), String.format("%s ANQP response for %012x: %s",
1135bee0e4616e2f8025d60cbfe3eec3e274a68a452Jan Nordqvist                    success ? "successful" : "failed", bssid, elements));
11442a1bf5e9b21288524d9a5fe90556abefc035879Jan Nordqvist            mConfigStore.notifyANQPResponse(scanDetail, success ? elements : null);
11577f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        }
11677f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        catch (IOException ioe) {
1177ec7de9f415179e38a39e7b1be917ba77e3b68ebJan Nordqvist            Log.e(Utils.hs2LogTag(getClass()), "Failed to parse ANQP: " +
1187ec7de9f415179e38a39e7b1be917ba77e3b68ebJan Nordqvist                    ioe.toString() + ": " + bssData);
1197ec7de9f415179e38a39e7b1be917ba77e3b68ebJan Nordqvist        }
1207ec7de9f415179e38a39e7b1be917ba77e3b68ebJan Nordqvist        catch (RuntimeException rte) {
1217ec7de9f415179e38a39e7b1be917ba77e3b68ebJan Nordqvist            Log.e(Utils.hs2LogTag(getClass()), "Failed to parse ANQP: " +
1227ec7de9f415179e38a39e7b1be917ba77e3b68ebJan Nordqvist                    rte.toString() + ": " + bssData, rte);
12377f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        }
124d3fb9cbb12d013dd70e672ace5c41ab18a3679a0Vinit Deshpande        mConfigStore.notifyANQPResponse(scanDetail, null);
12577f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist    }
12677f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
127ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist    /*
12877f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist    public boolean addCredential(HomeSP homeSP, NetworkDetail networkDetail) {
12977f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        Credential credential = homeSP.getCredential();
13077f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        if (credential == null)
13177f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            return false;
13277f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
13377f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        String nwkID = null;
13477f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        if (mLastSSID != null) {
13577f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            String nwkList = mSupplicantHook.doCustomCommand("LIST_NETWORKS");
13677f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
13777f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            BufferedReader reader = new BufferedReader(new StringReader(nwkList));
13877f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            String line;
13977f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            try {
14077f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                while ((line = reader.readLine()) != null) {
14177f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                    String[] tokens = line.split("\\t");
14277f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                    if (tokens.length < 2 || ! Utils.isDecimal(tokens[0])) {
14377f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                        continue;
14477f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                    }
14577f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                    if (unescapeSSID(tokens[1]).equals(mLastSSID)) {
14677f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                        nwkID = tokens[0];
14777f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                        Log.d("HS2J", "Network " + tokens[0] +
14877f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                                " matches last SSID '" + mLastSSID + "'");
14977f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                        break;
15077f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                    }
15177f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                }
15277f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            }
15377f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            catch (IOException ioe) {
154ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist                //
15577f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            }
15677f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        }
15777f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
15877f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        if (nwkID == null) {
15977f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            nwkID = mSupplicantHook.doCustomCommand("ADD_NETWORK");
16077f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            Log.d("HS2J", "add_network: '" + nwkID + "'");
16177f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            if (! Utils.isDecimal(nwkID)) {
16277f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                return false;
16377f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            }
16477f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        }
16577f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
16677f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        List<String> credCommand = getWPSNetCommands(nwkID, networkDetail, credential);
16777f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        for (String command : credCommand) {
16877f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            String status = mSupplicantHook.doCustomCommand(command);
16977f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            Log.d("HS2J", "Status of '" + command + "': '" + status + "'");
17077f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        }
17177f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
17277f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        if (! networkDetail.getSSID().equals(mLastSSID)) {
17377f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            mLastSSID = networkDetail.getSSID();
17477f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            PrintWriter out = null;
17577f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            try {
17677f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                out = new PrintWriter(new OutputStreamWriter(
17777f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                        new FileOutputStream(mLastSSIDFile, false), StandardCharsets.UTF_8));
17877f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                out.println(mLastSSID);
17977f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            } catch (IOException ioe) {
180ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist            //
18177f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            } finally {
18277f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                if (out != null) {
18377f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                    out.close();
18477f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                }
18577f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            }
18677f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        }
18777f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
18877f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        return true;
18977f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist    }
190ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist    */
19177f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
19277f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist    private static String escapeSSID(NetworkDetail networkDetail) {
19377f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        return escapeString(networkDetail.getSSID(), networkDetail.isSSID_UTF8());
19477f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist    }
19577f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
19677f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist    private static String escapeString(String s, boolean utf8) {
19777f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        boolean asciiOnly = true;
19877f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        for (int n = 0; n < s.length(); n++) {
19977f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            char ch = s.charAt(n);
20077f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            if (ch > 127) {
20177f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                asciiOnly = false;
20277f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                break;
20377f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            }
20477f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        }
20577f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
20677f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        if (asciiOnly) {
207ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist            return '"' + s + '"';
20877f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        }
20977f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        else {
21077f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            byte[] octets = s.getBytes(utf8 ? StandardCharsets.UTF_8 : StandardCharsets.ISO_8859_1);
21177f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
21277f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            StringBuilder sb = new StringBuilder();
21377f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            for (byte octet : octets) {
21477f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                sb.append(String.format("%02x", octet & Constants.BYTE_MASK));
21577f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            }
21677f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            return sb.toString();
21777f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        }
21877f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist    }
21977f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
22077f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist    private static String buildWPSQueryRequest(NetworkDetail networkDetail) {
22177f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        StringBuilder sb = new StringBuilder();
22277f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        sb.append("ANQP_GET ").append(networkDetail.getBSSIDString()).append(' ');
22377f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
22477f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        boolean first = true;
22577f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        for (Constants.ANQPElementType elementType : ANQPFactory.getBaseANQPSet()) {
22677f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            if (networkDetail.getAnqpOICount() == 0 &&
22777f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                    elementType == Constants.ANQPElementType.ANQPRoamingConsortium) {
22877f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                continue;
22977f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            }
23077f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            if (first) {
23177f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                first = false;
23277f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            }
23377f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            else {
23477f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                sb.append(',');
23577f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            }
23677f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            sb.append(Constants.getANQPElementID(elementType));
23777f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        }
23877f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        if (networkDetail.getHSRelease() != null) {
23977f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            for (Constants.ANQPElementType elementType : ANQPFactory.getHS20ANQPSet()) {
24077f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                sb.append(",hs20:").append(Constants.getHS20ElementID(elementType));
24177f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            }
24277f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        }
24377f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        return sb.toString();
24477f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist    }
24577f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
24677f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist    private static List<String> getWPSNetCommands(String netID, NetworkDetail networkDetail,
24777f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                                                 Credential credential) {
24877f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
24977f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        List<String> commands = new ArrayList<String>();
25077f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
25177f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        EAPMethod eapMethod = credential.getEAPMethod();
25277f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        commands.add(String.format("SET_NETWORK %s key_mgmt WPA-EAP", netID));
25377f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        commands.add(String.format("SET_NETWORK %s ssid %s", netID, escapeSSID(networkDetail)));
25477f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        commands.add(String.format("SET_NETWORK %s bssid %s",
25577f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                netID, networkDetail.getBSSIDString()));
25677f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        commands.add(String.format("SET_NETWORK %s eap %s",
25777f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                netID, mapEAPMethodName(eapMethod.getEAPMethodID())));
25877f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
25977f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        AuthParam authParam = credential.getEAPMethod().getAuthParam();
26077f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        if (authParam == null) {
26177f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            return null;            // TLS or SIM/AKA
26277f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        }
26377f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        switch (authParam.getAuthInfoID()) {
26477f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            case NonEAPInnerAuthType:
26577f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            case InnerAuthEAPMethodType:
26677f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                commands.add(String.format("SET_NETWORK %s identity %s",
26777f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                        netID, escapeString(credential.getUserName(), true)));
26877f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                commands.add(String.format("SET_NETWORK %s password %s",
26977f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                        netID, escapeString(credential.getPassword(), true)));
27077f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                commands.add(String.format("SET_NETWORK %s anonymous_identity \"anonymous\"",
27177f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                        netID));
27277f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                break;
27377f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            default:                // !!! Needs work.
27477f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                return null;
27577f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        }
27677f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        commands.add(String.format("SET_NETWORK %s priority 0", netID));
27777f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        commands.add(String.format("ENABLE_NETWORK %s", netID));
27877f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        commands.add(String.format("SAVE_CONFIG"));
27977f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        return commands;
28077f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist    }
28177f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
28277f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist    private static Map<Constants.ANQPElementType, ANQPElement> parseWPSData(String bssInfo)
28377f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            throws IOException {
284ef1567e413c9ed5f5c4fdb9e354861632f7b2f87Jan Nordqvist        Map<Constants.ANQPElementType, ANQPElement> elements = new HashMap<>();
28577f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        if (bssInfo == null) {
28677f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            return elements;
28777f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        }
28877f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        BufferedReader lineReader = new BufferedReader(new StringReader(bssInfo));
28977f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        String line;
29077f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        while ((line=lineReader.readLine()) != null) {
29177f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            ANQPElement element = buildElement(line);
29277f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            if (element != null) {
29377f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                elements.put(element.getID(), element);
29477f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            }
29577f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        }
29677f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        return elements;
29777f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist    }
29877f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
29977f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist    private static ANQPElement buildElement(String text) throws ProtocolException {
30077f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        int separator = text.indexOf('=');
30177f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        if (separator < 0) {
30277f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            return null;
30377f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        }
30477f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
30577f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        String elementName = text.substring(0, separator);
30677f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        Constants.ANQPElementType elementType = sWpsNames.get(elementName);
30777f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        if (elementType == null) {
30877f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            return null;
30977f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        }
31077f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
31177f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        byte[] payload;
31277f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        try {
31377f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            payload = Utils.hexToBytes(text.substring(separator + 1));
31477f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        }
31577f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        catch (NumberFormatException nfe) {
3161c03d75c73b9f5fa24a795a0d546f4f56b82ab9bJan Nordqvist            Log.e(Utils.hs2LogTag(SupplicantBridge.class), "Failed to parse hex string");
31777f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            return null;
31877f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        }
31977f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        return Constants.getANQPElementID(elementType) != null ?
32077f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                ANQPFactory.buildElement(ByteBuffer.wrap(payload), elementType, payload.length) :
32177f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                ANQPFactory.buildHS20Element(elementType,
32277f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                        ByteBuffer.wrap(payload).order(ByteOrder.LITTLE_ENDIAN));
32377f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist    }
32477f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
32577f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist    private static String mapEAPMethodName(EAP.EAPMethodID eapMethodID) {
32677f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        switch (eapMethodID) {
32777f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            case EAP_AKA:
32877f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                return "AKA";
32977f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            case EAP_AKAPrim:
33077f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                return "AKA'";  // eap.c:1514
33177f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            case EAP_SIM:
33277f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                return "SIM";
33377f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            case EAP_TLS:
33477f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                return "TLS";
33577f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            case EAP_TTLS:
33677f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                return "TTLS";
33777f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            default:
33877f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                throw new IllegalArgumentException("No mapping for " + eapMethodID);
33977f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        }
34077f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist    }
34177f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
34277f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist    private static final Map<Character,Integer> sMappings = new HashMap<Character, Integer>();
34377f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
34477f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist    static {
34577f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        sMappings.put('\\', (int)'\\');
34677f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        sMappings.put('"', (int)'"');
34777f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        sMappings.put('e', 0x1b);
34877f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        sMappings.put('n', (int)'\n');
34977f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        sMappings.put('r', (int)'\n');
35077f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        sMappings.put('t', (int)'\t');
35177f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist    }
35277f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
35377f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist    public static String unescapeSSID(String ssid) {
35477f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
35577f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        CharIterator chars = new CharIterator(ssid);
35677f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        byte[] octets = new byte[ssid.length()];
35777f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        int bo = 0;
35877f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
35977f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        while (chars.hasNext()) {
36077f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            char ch = chars.next();
36177f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            if (ch != '\\' || ! chars.hasNext()) {
36277f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                octets[bo++] = (byte)ch;
36377f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            }
36477f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            else {
36577f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                char suffix = chars.next();
36677f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                Integer mapped = sMappings.get(suffix);
36777f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                if (mapped != null) {
36877f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                    octets[bo++] = mapped.byteValue();
36977f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                }
37077f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                else if (suffix == 'x' && chars.hasDoubleHex()) {
37177f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                    octets[bo++] = (byte)chars.nextDoubleHex();
37277f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                }
37377f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                else {
37477f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                    octets[bo++] = '\\';
37577f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                    octets[bo++] = (byte)suffix;
37677f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                }
37777f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            }
37877f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        }
37977f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
38077f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        boolean asciiOnly = true;
38177f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        for (byte b : octets) {
38277f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            if ((b&0x80) != 0) {
38377f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                asciiOnly = false;
38477f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                break;
38577f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            }
38677f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        }
38777f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        if (asciiOnly) {
38877f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            return new String(octets, 0, bo, StandardCharsets.UTF_8);
38977f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        } else {
39077f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            try {
39177f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                // If UTF-8 decoding is successful it is almost certainly UTF-8
39277f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                CharBuffer cb = StandardCharsets.UTF_8.newDecoder().decode(
39377f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                        ByteBuffer.wrap(octets, 0, bo));
39477f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                return cb.toString();
39577f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            } catch (CharacterCodingException cce) {
39677f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                return new String(octets, 0, bo, StandardCharsets.ISO_8859_1);
39777f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            }
39877f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        }
39977f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist    }
40077f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
40177f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist    private static class CharIterator {
40277f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        private final String mString;
40377f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        private int mPosition;
40477f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        private int mHex;
40577f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
40677f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        private CharIterator(String s) {
40777f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            mString = s;
40877f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        }
40977f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
41077f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        private boolean hasNext() {
41177f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            return mPosition < mString.length();
41277f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        }
41377f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
41477f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        private char next() {
41577f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            return mString.charAt(mPosition++);
41677f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        }
41777f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
41877f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        private boolean hasDoubleHex() {
41977f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            if (mString.length() - mPosition < 2) {
42077f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                return false;
42177f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            }
42277f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            int nh = Utils.fromHex(mString.charAt(mPosition), true);
42377f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            if (nh < 0) {
42477f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                return false;
42577f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            }
42677f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            int nl = Utils.fromHex(mString.charAt(mPosition + 1), true);
42777f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            if (nl < 0) {
42877f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist                return false;
42977f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            }
43077f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            mPosition += 2;
43177f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            mHex = (nh << 4) | nl;
43277f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            return true;
43377f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        }
43477f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
43577f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        private int nextDoubleHex() {
43677f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            return mHex;
43777f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        }
43877f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist    }
43977f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
44077f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist    private static final String[] TestStrings = {
44177f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            "test-ssid",
44277f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            "test\\nss\\tid",
44377f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            "test\\x2d\\x5f\\nss\\tid",
44477f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            "test\\x2d\\x5f\\nss\\tid\\\\",
44577f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            "test\\x2d\\x5f\\nss\\tid\\n",
44677f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            "test\\x2d\\x5f\\nss\\tid\\x4a",
44777f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            "another\\",
44877f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            "an\\other",
44977f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            "another\\x2"
45077f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist    };
45177f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
45277f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist    public static void main(String[] args) {
45377f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        for (String string : TestStrings) {
45477f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist            System.out.println(unescapeSSID(string));
45577f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        }
45677f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist    }
45777f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist}
458