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 android.net.wifi.hotspot2.pps;
18
19import android.os.Parcelable;
20import android.os.Parcel;
21import android.text.TextUtils;
22import android.util.Log;
23
24import java.nio.charset.StandardCharsets;
25import java.util.Arrays;
26import java.util.Collections;
27import java.util.HashMap;
28import java.util.Map;
29import 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 */
38public 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     */
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     */
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     */
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     */
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     */
100    public void setIconUrl(String iconUrl) {
101        mIconUrl = iconUrl;
102    }
103    /**
104     * @hide
105     */
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     */
121    public void setHomeNetworkIds(Map<String, Long> homeNetworkIds) {
122        mHomeNetworkIds = homeNetworkIds;
123    }
124    /**
125     * @hide
126     */
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     */
145    public void setMatchAllOis(long[] matchAllOis) {
146        mMatchAllOis = matchAllOis;
147    }
148    /**
149     * @hide
150     */
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     */
172    public void setMatchAnyOis(long[] matchAnyOis) {
173        mMatchAnyOis = matchAnyOis;
174    }
175    /**
176     * @hide
177     */
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     */
192    public void setOtherHomePartners(String[] otherHomePartners) {
193        mOtherHomePartners = otherHomePartners;
194    }
195    /**
196     * @hide
197     */
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     */
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     */
222    public long[] getRoamingConsortiumOis() {
223        return mRoamingConsortiumOis;
224    }
225
226    /**
227     * Constructor for creating HomeSp with default values.
228     */
229    public HomeSp() {}
230
231    /**
232     * Copy constructor.
233     *
234     * @param source The source to copy from
235     */
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
263    public int describeContents() {
264        return 0;
265    }
266
267    @Override
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
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
301    public int hashCode() {
302        return Objects.hash(mFqdn, mFriendlyName, mIconUrl, mHomeNetworkIds, mMatchAllOis,
303                mMatchAnyOis, mOtherHomePartners, mRoamingConsortiumOis);
304    }
305
306    @Override
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     */
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 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     */
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