1 package com.android.anqp;
2 
3 import android.os.Parcel;
4 
5 import com.android.hotspot2.Utils;
6 
7 import java.io.IOException;
8 import java.net.ProtocolException;
9 import java.nio.ByteBuffer;
10 import java.nio.ByteOrder;
11 import java.nio.charset.StandardCharsets;
12 import java.util.ArrayList;
13 import java.util.List;
14 
15 import static com.android.anqp.Constants.BYTE_MASK;
16 import static com.android.anqp.Constants.SHORT_MASK;
17 
18 /**
19  * An OSU Provider, as specified in
20  * Wi-Fi Alliance Hotspot 2.0 (Release 2) Technical Specification - Version 5.00,
21  * section 4.8.1
22  */
23 public class OSUProvider {
24 
25     public enum OSUMethod {OmaDm, SoapXml}
26 
27     private final String mSSID;
28     private final List<I18Name> mNames;
29     private final String mOSUServer;
30     private final List<OSUMethod> mOSUMethods;
31     private final List<IconInfo> mIcons;
32     private final String mOsuNai;
33     private final List<I18Name> mServiceDescriptions;
34     private final int mHashCode;
35 
OSUProvider(String ssid, ByteBuffer payload)36     public OSUProvider(String ssid, ByteBuffer payload) throws ProtocolException {
37         if (payload.remaining() < 11) {
38             throw new ProtocolException("Truncated OSU provider: " + payload.remaining());
39         }
40 
41         mSSID = ssid;
42 
43         int length = payload.getShort() & SHORT_MASK;
44         int namesLength = payload.getShort() & SHORT_MASK;
45 
46         ByteBuffer namesBuffer = payload.duplicate().order(ByteOrder.LITTLE_ENDIAN);
47         namesBuffer.limit(namesBuffer.position() + namesLength);
48         payload.position(payload.position() + namesLength);
49 
50         mNames = new ArrayList<>();
51 
52         while (namesBuffer.hasRemaining()) {
53             mNames.add(new I18Name(namesBuffer));
54         }
55 
56         mOSUServer = Constants.getPrefixedString(payload, 1, StandardCharsets.UTF_8);
57         int methodLength = payload.get() & BYTE_MASK;
58         mOSUMethods = new ArrayList<>(methodLength);
59         while (methodLength > 0) {
60             int methodID = payload.get() & BYTE_MASK;
61             mOSUMethods.add(methodID < OSUMethod.values().length ?
62                     OSUMethod.values()[methodID] :
63                     null);
64             methodLength--;
65         }
66 
67         int iconsLength = payload.getShort() & SHORT_MASK;
68         ByteBuffer iconsBuffer = payload.duplicate().order(ByteOrder.LITTLE_ENDIAN);
69         iconsBuffer.limit(iconsBuffer.position() + iconsLength);
70         payload.position(payload.position() + iconsLength);
71 
72         mIcons = new ArrayList<>();
73 
74         while (iconsBuffer.hasRemaining()) {
75             mIcons.add(new IconInfo(iconsBuffer));
76         }
77 
78         mOsuNai = Constants.getPrefixedString(payload, 1, StandardCharsets.UTF_8, true);
79 
80         int descriptionsLength = payload.getShort() & SHORT_MASK;
81         ByteBuffer descriptionsBuffer = payload.duplicate().order(ByteOrder.LITTLE_ENDIAN);
82         descriptionsBuffer.limit(descriptionsBuffer.position() + descriptionsLength);
83         payload.position(payload.position() + descriptionsLength);
84 
85         mServiceDescriptions = new ArrayList<>();
86 
87         while (descriptionsBuffer.hasRemaining()) {
88             mServiceDescriptions.add(new I18Name(descriptionsBuffer));
89         }
90 
91         int result = mNames.hashCode();
92         result = 31 * result + mSSID.hashCode();
93         result = 31 * result + mOSUServer.hashCode();
94         result = 31 * result + mOSUMethods.hashCode();
95         result = 31 * result + mIcons.hashCode();
96         result = 31 * result + (mOsuNai != null ? mOsuNai.hashCode() : 0);
97         result = 31 * result + mServiceDescriptions.hashCode();
98         mHashCode = result;
99     }
100 
101     public String getSSID() {
102         return mSSID;
103     }
104 
105     public List<I18Name> getNames() {
106         return mNames;
107     }
108 
109     public String getOSUServer() {
110         return mOSUServer;
111     }
112 
113     public List<OSUMethod> getOSUMethods() {
114         return mOSUMethods;
115     }
116 
117     public List<IconInfo> getIcons() {
118         return mIcons;
119     }
120 
121     public String getOsuNai() {
122         return mOsuNai;
123     }
124 
125     public List<I18Name> getServiceDescriptions() {
126         return mServiceDescriptions;
127     }
128 
129     @Override
130     public boolean equals(Object o) {
131         if (this == o) return true;
132         if (o == null || getClass() != o.getClass()) return false;
133 
134         OSUProvider that = (OSUProvider) o;
135 
136         if (!mSSID.equals(that.mSSID)) return false;
137         if (!mOSUServer.equals(that.mOSUServer)) return false;
138         if (!mNames.equals(that.mNames)) return false;
139         if (!mServiceDescriptions.equals(that.mServiceDescriptions)) return false;
140         if (!mIcons.equals(that.mIcons)) return false;
141         if (!mOSUMethods.equals(that.mOSUMethods)) return false;
142         if (mOsuNai != null ? !mOsuNai.equals(that.mOsuNai) : that.mOsuNai != null) return false;
143 
144         return true;
145     }
146 
147     @Override
148     public int hashCode() {
149         return mHashCode;
150     }
151 
152     @Override
153     public String toString() {
154         return "OSUProvider{" +
155                 "names=" + mNames +
156                 ", OSUServer='" + mOSUServer + '\'' +
157                 ", OSUMethods=" + mOSUMethods +
158                 ", icons=" + mIcons +
159                 ", NAI='" + mOsuNai + '\'' +
160                 ", serviceDescriptions=" + mServiceDescriptions +
161                 '}';
162     }
163 
164     public OSUProvider(Parcel in) throws IOException {
165         mSSID = in.readString();
166         int nameCount = in.readInt();
167         mNames = new ArrayList<>(nameCount);
168         for (int n = 0; n < nameCount; n++) {
169             mNames.add(new I18Name(in));
170         }
171         mOSUServer = in.readString();
172         int methodCount = in.readInt();
173         mOSUMethods = new ArrayList<>(methodCount);
174         for (int n = 0; n < methodCount; n++) {
175             mOSUMethods.add(Utils.mapEnum(in.readInt(), OSUMethod.class));
176         }
177         int iconCount = in.readInt();
178         mIcons = new ArrayList<>(iconCount);
179         for (int n = 0; n < iconCount; n++) {
180             mIcons.add(new IconInfo(in));
181         }
182         mOsuNai = in.readString();
183         int serviceCount = in.readInt();
184         mServiceDescriptions = new ArrayList<>(serviceCount);
185         for (int n = 0; n < serviceCount; n++) {
186             mServiceDescriptions.add(new I18Name(in));
187         }
188         mHashCode = in.readInt();
189     }
190 
191     public void writeParcel(Parcel out) {
192         out.writeString(mSSID);
193         out.writeInt(mNames.size());
194         for (I18Name name : mNames) {
195             name.writeParcel(out);
196         }
197         out.writeString(mOSUServer);
198         out.writeInt(mOSUMethods.size());
199         for (OSUMethod method : mOSUMethods) {
200             out.writeInt(method.ordinal());
201         }
202         out.writeInt(mIcons.size());
203         for (IconInfo iconInfo : mIcons) {
204             iconInfo.writeParcel(out);
205         }
206         out.writeString(mOsuNai);
207         out.writeInt(mServiceDescriptions.size());
208         for (I18Name serviceDescription : mServiceDescriptions) {
209             serviceDescription.writeParcel(out);
210         }
211         out.writeInt(mHashCode);
212     }
213 }
214