AnqpEvent.java revision 65d8ba5dd551cd132789e8feb270dfc7998dfbdc
1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.wifi.hotspot2;
18
19import android.util.Log;
20
21import com.android.server.wifi.hotspot2.anqp.ANQPElement;
22import com.android.server.wifi.hotspot2.anqp.ANQPParser;
23import com.android.server.wifi.hotspot2.anqp.Constants;
24
25import java.io.BufferedReader;
26import java.io.IOException;
27import java.io.StringReader;
28import java.net.ProtocolException;
29import java.nio.BufferUnderflowException;
30import java.nio.ByteBuffer;
31import java.util.HashMap;
32import java.util.Map;
33
34/**
35 * This class carries the response of an ANQP request.
36 */
37public class AnqpEvent {
38    private static final String TAG = "AnqpEvent";
39    private static final Map<String, Constants.ANQPElementType> sWpsNames = new HashMap<>();
40
41    static {
42        sWpsNames.put("anqp_venue_name", Constants.ANQPElementType.ANQPVenueName);
43        sWpsNames.put("anqp_roaming_consortium", Constants.ANQPElementType.ANQPRoamingConsortium);
44        sWpsNames.put("anqp_ip_addr_type_availability",
45                Constants.ANQPElementType.ANQPIPAddrAvailability);
46        sWpsNames.put("anqp_nai_realm", Constants.ANQPElementType.ANQPNAIRealm);
47        sWpsNames.put("anqp_3gpp", Constants.ANQPElementType.ANQP3GPPNetwork);
48        sWpsNames.put("anqp_domain_name", Constants.ANQPElementType.ANQPDomName);
49        sWpsNames.put("hs20_operator_friendly_name", Constants.ANQPElementType.HSFriendlyName);
50        sWpsNames.put("hs20_wan_metrics", Constants.ANQPElementType.HSWANMetrics);
51        sWpsNames.put("hs20_connection_capability", Constants.ANQPElementType.HSConnCapability);
52        sWpsNames.put("hs20_osu_providers_list", Constants.ANQPElementType.HSOSUProviders);
53    }
54
55    /**
56     * Bssid of the access point.
57     */
58    private final long mBssid;
59    /**
60     * Map of ANQP element type to the data retrieved from the access point.
61     */
62    private final Map<Constants.ANQPElementType, ANQPElement> mElements;
63
64    public AnqpEvent(long bssid, Map<Constants.ANQPElementType, ANQPElement> elements) {
65        mBssid = bssid;
66        mElements = elements;
67    }
68
69    /**
70     * Creates an ANQP result instance using the passed in scan result data.
71     *
72     * @param bssid   BSSID of the access point.
73     * @param bssData Scan results fetched from wpa_supplicant after ANQP query.
74     */
75    public static AnqpEvent buildAnqpEvent(long bssid, String bssData) {
76        Map<Constants.ANQPElementType, ANQPElement> elements = null;
77        try {
78            elements = parseWPSData(bssData);
79            Log.d(TAG,
80                    String.format("Successful ANQP response for %012x: %s",
81                            bssid, elements));
82        } catch (IOException | BufferUnderflowException e) {
83            Log.e(TAG, "Failed to parse ANQP: " + e.toString() + ": " + bssData);
84            return null;
85        }
86        return new AnqpEvent(bssid, elements);
87    }
88
89    private static Map<Constants.ANQPElementType, ANQPElement> parseWPSData(String bssInfo)
90            throws IOException {
91        Map<Constants.ANQPElementType, ANQPElement> elements = new HashMap<>();
92        if (bssInfo == null) {
93            return elements;
94        }
95        BufferedReader lineReader = new BufferedReader(new StringReader(bssInfo));
96        String line;
97        while ((line = lineReader.readLine()) != null) {
98            ANQPElement element = buildElement(line);
99            if (element != null) {
100                elements.put(element.getID(), element);
101            }
102        }
103        return elements;
104    }
105
106    private static ANQPElement buildElement(String text) throws ProtocolException {
107        int separator = text.indexOf('=');
108        if (separator < 0 || separator + 1 == text.length()) {
109            return null;
110        }
111
112        String elementName = text.substring(0, separator);
113        Constants.ANQPElementType elementType = sWpsNames.get(elementName);
114        if (elementType == null) {
115            return null;
116        }
117
118        byte[] payload;
119        try {
120            payload = Utils.hexToBytes(text.substring(separator + 1));
121        } catch (NumberFormatException nfe) {
122            Log.e(Utils.hs2LogTag(PasspointEventHandler.class),
123                    "Failed to parse hex string");
124            return null;
125        }
126        // Wrap the payload inside a ByteBuffer.
127        ByteBuffer buffer = ByteBuffer.wrap(payload);
128
129        return Constants.getANQPElementID(elementType) != null
130                ? ANQPParser.parseElement(elementType, buffer)
131                : ANQPParser.parseHS20Element(elementType, buffer);
132    }
133
134    /**
135     * Get the bssid of the access point from which this ANQP result was created.
136     */
137    public long getBssid() {
138        return mBssid;
139    }
140
141    /**
142     * Get the map of ANQP elements retrieved from the access point.
143     */
144    public Map<Constants.ANQPElementType, ANQPElement> getElements() {
145        return mElements;
146    }
147
148}
149