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 android.net.wifi.hotspot2.pps;
18 
19 import android.os.Parcelable;
20 import android.os.Parcel;
21 import android.text.TextUtils;
22 import android.util.Log;
23 
24 import java.nio.charset.StandardCharsets;
25 import java.util.Arrays;
26 import java.util.Collections;
27 import java.util.HashMap;
28 import java.util.Map;
29 import java.util.Objects;
30 
31 /**
32  * Class representing HomeSP subtree in PerProviderSubscription (PPS)
33  * Management Object (MO) tree.
34  *
35  * For more info, refer to Hotspot 2.0 PPS MO defined in section 9.1 of the Hotspot 2.0
36  * Release 2 Technical Specification.
37  */
38 public final class HomeSp implements Parcelable {
39     private static final String TAG = "HomeSp";
40 
41     /**
42      * Maximum number of bytes allowed for a SSID.
43      */
44     private static final int MAX_SSID_BYTES = 32;
45 
46     /**
47      * Integer value used for indicating null value in the Parcel.
48      */
49     private static final int NULL_VALUE = -1;
50 
51     /**
52      * FQDN (Fully Qualified Domain Name) of this home service provider.
53      */
54     private String mFqdn = null;
55     /**
56      * Set the FQDN (Fully Qualified Domain Name) associated with this home service provider.
57      *
58      * @param fqdn The FQDN to set to
59      */
setFqdn(String fqdn)60     public void setFqdn(String fqdn) {
61         mFqdn = fqdn;
62     }
63     /**
64      * Get the FQDN (Fully Qualified Domain Name) associated with this home service provider.
65      *
66      * @return the FQDN associated with this home service provider
67      */
getFqdn()68     public String getFqdn() {
69         return mFqdn;
70     }
71 
72     /**
73      * Friendly name of this home service provider.
74      */
75     private String mFriendlyName = null;
76     /**
77      * Set the friendly name associated with this home service provider.
78      *
79      * @param friendlyName The friendly name to set to
80      */
setFriendlyName(String friendlyName)81     public void setFriendlyName(String friendlyName) {
82         mFriendlyName = friendlyName;
83     }
84     /**
85      * Get the friendly name associated with this home service provider.
86      *
87      * @return the friendly name associated with this home service provider
88      */
getFriendlyName()89     public String getFriendlyName() {
90         return mFriendlyName;
91     }
92 
93     /**
94      * Icon URL of this home service provider.
95      */
96     private String mIconUrl = null;
97     /**
98      * @hide
99      */
setIconUrl(String iconUrl)100     public void setIconUrl(String iconUrl) {
101         mIconUrl = iconUrl;
102     }
103     /**
104      * @hide
105      */
getIconUrl()106     public String getIconUrl() {
107         return mIconUrl;
108     }
109 
110     /**
111      * <SSID, HESSID> duple of the networks that are consider home networks.
112      *
113      * According to the Section 9.1.2 of the Hotspot 2.0 Release 2 Technical Specification,
114      * all nodes in the PSS MO are encoded using UTF-8 unless stated otherwise.  Thus, the SSID
115      * string is assumed to be encoded using UTF-8.
116      */
117     private Map<String, Long> mHomeNetworkIds = null;
118     /**
119      * @hide
120      */
setHomeNetworkIds(Map<String, Long> homeNetworkIds)121     public void setHomeNetworkIds(Map<String, Long> homeNetworkIds) {
122         mHomeNetworkIds = homeNetworkIds;
123     }
124     /**
125      * @hide
126      */
getHomeNetworkIds()127     public Map<String, Long> getHomeNetworkIds() {
128         return mHomeNetworkIds;
129     }
130 
131     /**
132      * Used for determining if this provider is a member of a given Hotspot provider.
133      * Every Organization Identifiers (OIs) in this list are required to match an OI in the
134      * the Roaming Consortium advertised by a Hotspot, in order to consider this provider
135      * as a member of that Hotspot provider (e.g. successful authentication with such Hotspot
136      * is possible).
137      *
138      * Refer to HomeSP/HomeOIList subtree in PerProviderSubscription (PPS) Management Object
139      * (MO) tree for more detail.
140      */
141     private long[] mMatchAllOis = null;
142     /**
143      * @hide
144      */
setMatchAllOis(long[] matchAllOis)145     public void setMatchAllOis(long[] matchAllOis) {
146         mMatchAllOis = matchAllOis;
147     }
148     /**
149      * @hide
150      */
getMatchAllOis()151     public long[] getMatchAllOis() {
152         return mMatchAllOis;
153     }
154 
155     /**
156      * Used for determining if this provider is a member of a given Hotspot provider.
157      * Matching of any Organization Identifiers (OIs) in this list with an OI in the
158      * Roaming Consortium advertised by a Hotspot, will consider this provider as a member
159      * of that Hotspot provider (e.g. successful authentication with such Hotspot
160      * is possible).
161      *
162      * {@link #mMatchAllOIs} will have precedence over this one, meaning this list will
163      * only be used for matching if {@link #mMatchAllOIs} is null or empty.
164      *
165      * Refer to HomeSP/HomeOIList subtree in PerProviderSubscription (PPS) Management Object
166      * (MO) tree for more detail.
167      */
168     private long[] mMatchAnyOis = null;
169     /**
170      * @hide
171      */
setMatchAnyOis(long[] matchAnyOis)172     public void setMatchAnyOis(long[] matchAnyOis) {
173         mMatchAnyOis = matchAnyOis;
174     }
175     /**
176      * @hide
177      */
getMatchAnyOis()178     public long[] getMatchAnyOis() {
179         return mMatchAnyOis;
180     }
181 
182     /**
183      * List of FQDN (Fully Qualified Domain Name) of partner providers.
184      * These providers should also be regarded as home Hotspot operators.
185      * This relationship is most likely achieved via a commercial agreement or
186      * operator merges between the providers.
187      */
188     private String[] mOtherHomePartners = null;
189     /**
190      * @hide
191      */
setOtherHomePartners(String[] otherHomePartners)192     public void setOtherHomePartners(String[] otherHomePartners) {
193         mOtherHomePartners = otherHomePartners;
194     }
195     /**
196      * @hide
197      */
getOtherHomePartners()198     public String[] getOtherHomePartners() {
199         return mOtherHomePartners;
200     }
201 
202     /**
203      * List of Organization Identifiers (OIs) identifying a roaming consortium of
204      * which this provider is a member.
205      */
206     private long[] mRoamingConsortiumOis = null;
207     /**
208      * Set the Organization Identifiers (OIs) identifying a roaming consortium of which this
209      * provider is a member.
210      *
211      * @param roamingConsortiumOis Array of roaming consortium OIs
212      */
setRoamingConsortiumOis(long[] roamingConsortiumOis)213     public void setRoamingConsortiumOis(long[] roamingConsortiumOis) {
214         mRoamingConsortiumOis = roamingConsortiumOis;
215     }
216     /**
217      * Get the Organization Identifiers (OIs) identifying a roaming consortium of which this
218      * provider is a member.
219      *
220      * @return array of roaming consortium OIs
221      */
getRoamingConsortiumOis()222     public long[] getRoamingConsortiumOis() {
223         return mRoamingConsortiumOis;
224     }
225 
226     /**
227      * Constructor for creating HomeSp with default values.
228      */
HomeSp()229     public HomeSp() {}
230 
231     /**
232      * Copy constructor.
233      *
234      * @param source The source to copy from
235      */
HomeSp(HomeSp source)236     public HomeSp(HomeSp source) {
237         if (source == null) {
238             return;
239         }
240         mFqdn = source.mFqdn;
241         mFriendlyName = source.mFriendlyName;
242         mIconUrl = source.mIconUrl;
243         if (source.mHomeNetworkIds != null) {
244             mHomeNetworkIds = Collections.unmodifiableMap(source.mHomeNetworkIds);
245         }
246         if (source.mMatchAllOis != null) {
247             mMatchAllOis = Arrays.copyOf(source.mMatchAllOis, source.mMatchAllOis.length);
248         }
249         if (source.mMatchAnyOis != null) {
250             mMatchAnyOis = Arrays.copyOf(source.mMatchAnyOis, source.mMatchAnyOis.length);
251         }
252         if (source.mOtherHomePartners != null) {
253             mOtherHomePartners = Arrays.copyOf(source.mOtherHomePartners,
254                     source.mOtherHomePartners.length);
255         }
256         if (source.mRoamingConsortiumOis != null) {
257             mRoamingConsortiumOis = Arrays.copyOf(source.mRoamingConsortiumOis,
258                     source.mRoamingConsortiumOis.length);
259         }
260     }
261 
262     @Override
describeContents()263     public int describeContents() {
264         return 0;
265     }
266 
267     @Override
writeToParcel(Parcel dest, int flags)268     public void writeToParcel(Parcel dest, int flags) {
269         dest.writeString(mFqdn);
270         dest.writeString(mFriendlyName);
271         dest.writeString(mIconUrl);
272         writeHomeNetworkIds(dest, mHomeNetworkIds);
273         dest.writeLongArray(mMatchAllOis);
274         dest.writeLongArray(mMatchAnyOis);
275         dest.writeStringArray(mOtherHomePartners);
276         dest.writeLongArray(mRoamingConsortiumOis);
277     }
278 
279     @Override
equals(Object thatObject)280     public boolean equals(Object thatObject) {
281         if (this == thatObject) {
282             return true;
283         }
284         if (!(thatObject instanceof HomeSp)) {
285             return false;
286         }
287         HomeSp that = (HomeSp) thatObject;
288 
289         return TextUtils.equals(mFqdn, that.mFqdn)
290                 && TextUtils.equals(mFriendlyName, that.mFriendlyName)
291                 && TextUtils.equals(mIconUrl, that.mIconUrl)
292                 && (mHomeNetworkIds == null ? that.mHomeNetworkIds == null
293                         : mHomeNetworkIds.equals(that.mHomeNetworkIds))
294                 && Arrays.equals(mMatchAllOis, that.mMatchAllOis)
295                 && Arrays.equals(mMatchAnyOis, that.mMatchAnyOis)
296                 && Arrays.equals(mOtherHomePartners, that.mOtherHomePartners)
297                 && Arrays.equals(mRoamingConsortiumOis, that.mRoamingConsortiumOis);
298     }
299 
300     @Override
hashCode()301     public int hashCode() {
302         return Objects.hash(mFqdn, mFriendlyName, mIconUrl, mHomeNetworkIds, mMatchAllOis,
303                 mMatchAnyOis, mOtherHomePartners, mRoamingConsortiumOis);
304     }
305 
306     @Override
toString()307     public String toString() {
308         StringBuilder builder = new StringBuilder();
309         builder.append("FQDN: ").append(mFqdn).append("\n");
310         builder.append("FriendlyName: ").append(mFriendlyName).append("\n");
311         builder.append("IconURL: ").append(mIconUrl).append("\n");
312         builder.append("HomeNetworkIDs: ").append(mHomeNetworkIds).append("\n");
313         builder.append("MatchAllOIs: ").append(mMatchAllOis).append("\n");
314         builder.append("MatchAnyOIs: ").append(mMatchAnyOis).append("\n");
315         builder.append("OtherHomePartners: ").append(mOtherHomePartners).append("\n");
316         builder.append("RoamingConsortiumOIs: ").append(mRoamingConsortiumOis).append("\n");
317         return builder.toString();
318     }
319 
320     /**
321      * Validate HomeSp data.
322      *
323      * @return true on success or false on failure
324      * @hide
325      */
validate()326     public boolean validate() {
327         if (TextUtils.isEmpty(mFqdn)) {
328             Log.d(TAG, "Missing FQDN");
329             return false;
330         }
331         if (TextUtils.isEmpty(mFriendlyName)) {
332             Log.d(TAG, "Missing friendly name");
333             return false;
334         }
335         // Verify SSIDs specified in the NetworkID
336         if (mHomeNetworkIds != null) {
337             for (Map.Entry<String, Long> entry : mHomeNetworkIds.entrySet()) {
338                 if (entry.getKey() == null ||
339                         entry.getKey().getBytes(StandardCharsets.UTF_8).length > MAX_SSID_BYTES) {
340                     Log.d(TAG, "Invalid SSID in HomeNetworkIDs");
341                     return false;
342                 }
343             }
344         }
345         return true;
346     }
347 
348     public static final @android.annotation.NonNull Creator<HomeSp> CREATOR =
349         new Creator<HomeSp>() {
350             @Override
351             public HomeSp createFromParcel(Parcel in) {
352                 HomeSp homeSp = new HomeSp();
353                 homeSp.setFqdn(in.readString());
354                 homeSp.setFriendlyName(in.readString());
355                 homeSp.setIconUrl(in.readString());
356                 homeSp.setHomeNetworkIds(readHomeNetworkIds(in));
357                 homeSp.setMatchAllOis(in.createLongArray());
358                 homeSp.setMatchAnyOis(in.createLongArray());
359                 homeSp.setOtherHomePartners(in.createStringArray());
360                 homeSp.setRoamingConsortiumOis(in.createLongArray());
361                 return homeSp;
362             }
363 
364             @Override
365             public HomeSp[] newArray(int size) {
366                 return new HomeSp[size];
367             }
368 
369             /**
370              * Helper function for reading a Home Network IDs map from a Parcel.
371              *
372              * @param in The Parcel to read from
373              * @return Map of home network IDs
374              */
375             private Map<String, Long> readHomeNetworkIds(Parcel in) {
376                 int size = in.readInt();
377                 if (size == NULL_VALUE) {
378                     return null;
379                 }
380                 Map<String, Long> networkIds = new HashMap<>(size);
381                 for (int i = 0; i < size; i++) {
382                     String key = in.readString();
383                     Long value = null;
384                     long readValue = in.readLong();
385                     if (readValue != NULL_VALUE) {
386                         value = Long.valueOf(readValue);
387                     }
388                     networkIds.put(key, value);
389                 }
390                 return networkIds;
391             }
392         };
393 
394     /**
395      * Helper function for writing Home Network IDs map to a Parcel.
396      *
397      * @param dest The Parcel to write to
398      * @param networkIds The map of home network IDs
399      */
writeHomeNetworkIds(Parcel dest, Map<String, Long> networkIds)400     private static void writeHomeNetworkIds(Parcel dest, Map<String, Long> networkIds) {
401         if (networkIds == null) {
402             dest.writeInt(NULL_VALUE);
403             return;
404         }
405         dest.writeInt(networkIds.size());
406         for (Map.Entry<String, Long> entry : networkIds.entrySet()) {
407             dest.writeString(entry.getKey());
408             if (entry.getValue() == null) {
409                 dest.writeLong(NULL_VALUE);
410             } else {
411                 dest.writeLong(entry.getValue());
412             }
413         }
414     }
415 }
416