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 
17 package com.android.server.wifi.hotspot2.anqp;
18 
19 import com.android.internal.annotations.VisibleForTesting;
20 import com.android.server.wifi.ByteBufferReader;
21 
22 import java.net.ProtocolException;
23 import java.nio.BufferUnderflowException;
24 import java.nio.ByteBuffer;
25 import java.nio.ByteOrder;
26 
27 /**
28  * Factory to build a collection of 802.11u ANQP elements from a byte buffer.
29  */
30 public class ANQPParser {
31     /**
32      * The OI value for Hotspot 2.0 ANQP-element.
33      */
34     @VisibleForTesting
35     public static final int VENDOR_SPECIFIC_HS20_OI = 0x506F9A;
36 
37     /**
38      * The Type value for Hotspot 2.0 ANQP-element.
39      */
40     @VisibleForTesting
41     public static final int VENDOR_SPECIFIC_HS20_TYPE = 0x11;
42 
43     /**
44      * Parse an ANQP element from the pass-in byte buffer.
45      *
46      * Note: Each Hotspot 2.0 Release 2 element will be wrapped inside a Vendor Specific element
47      * in the ANQP response from the AP.  However, the lower layer (e.g. wpa_supplicant) should
48      * already take care of parsing those elements out of Vendor Specific elements.  To be safe,
49      * we will parse the Vendor Specific elements for non-Hotspot 2.0 Release elements or in
50      * the case they're not parsed by the lower layer.
51      *
52      * @param infoID The ANQP element type
53      * @param payload The buffer to read from
54      * @return {@link com.android.server.wifi.hotspot2.anqp.ANQPElement}
55      * @throws BufferUnderflowException
56      * @throws ProtocolException
57      */
parseElement(Constants.ANQPElementType infoID, ByteBuffer payload)58     public static ANQPElement parseElement(Constants.ANQPElementType infoID, ByteBuffer payload)
59             throws ProtocolException {
60         switch (infoID) {
61             case ANQPVenueName:
62                 return VenueNameElement.parse(payload);
63             case ANQPRoamingConsortium:
64                 return RoamingConsortiumElement.parse(payload);
65             case ANQPIPAddrAvailability:
66                 return IPAddressTypeAvailabilityElement.parse(payload);
67             case ANQPNAIRealm:
68                 return NAIRealmElement.parse(payload);
69             case ANQP3GPPNetwork:
70                 return ThreeGPPNetworkElement.parse(payload);
71             case ANQPDomName:
72                 return DomainNameElement.parse(payload);
73             case ANQPVendorSpec:
74                 return parseVendorSpecificElement(payload);
75             case ANQPVenueUrl:
76                 return VenueUrlElement.parse(payload);
77             default:
78                 throw new ProtocolException("Unknown element ID: " + infoID);
79         }
80     }
81 
82     /**
83      * Parse a Hotspot 2.0 Release 2 ANQP element from the pass-in byte buffer.
84      *
85      * @param infoID The ANQP element ID
86      * @param payload The buffer to read from
87      * @return {@link com.android.server.wifi.hotspot2.anqp.ANQPElement}
88      * @throws BufferUnderflowException
89      * @throws ProtocolException
90      */
parseHS20Element(Constants.ANQPElementType infoID, ByteBuffer payload)91     public static ANQPElement parseHS20Element(Constants.ANQPElementType infoID,
92             ByteBuffer payload) throws ProtocolException {
93         switch (infoID) {
94             case HSFriendlyName:
95                 return HSFriendlyNameElement.parse(payload);
96             case HSWANMetrics:
97                 return HSWanMetricsElement.parse(payload);
98             case HSConnCapability:
99                 return HSConnectionCapabilityElement.parse(payload);
100             case HSOSUProviders:
101                 return HSOsuProvidersElement.parse(payload);
102             default:
103                 throw new ProtocolException("Unknown element ID: " + infoID);
104         }
105     }
106 
107     /**
108      * Parse the ANQP vendor specific element.  Currently only supports the vendor specific
109      * element that contained Hotspot 2.0 ANQP-element.
110      *
111      * Format of a ANQP Vendor Specific element:
112      * | OI | Type | Subtype | Reserved | Payload |
113      *   3     1        1         1       variable
114      *
115      * @param payload The buffer to read from
116      * @return {@link ANQPElement}
117      * @throws BufferUnderflowException
118      * @throws ProtocolException
119      */
parseVendorSpecificElement(ByteBuffer payload)120     private static ANQPElement parseVendorSpecificElement(ByteBuffer payload)
121             throws ProtocolException {
122         int oi = (int) ByteBufferReader.readInteger(payload, ByteOrder.BIG_ENDIAN, 3);
123         int type = payload.get() & 0xFF;
124 
125         if (oi != VENDOR_SPECIFIC_HS20_OI || type != VENDOR_SPECIFIC_HS20_TYPE) {
126             throw new ProtocolException("Unsupported vendor specific OI=" + oi + " type=" + type);
127         }
128 
129         int subType = payload.get() & 0xFF;
130         Constants.ANQPElementType hs20ID = Constants.mapHS20Element(subType);
131         if (hs20ID == null) {
132             throw new ProtocolException("Unsupported subtype: " + subType);
133         }
134         payload.get();     // Skip the reserved byte
135         return parseHS20Element(hs20ID, payload);
136     }
137 }
138