blob: b2583d324420c169c4c346d69068fb1bde675455 [file] [log] [blame]
/**
* Copyright (c) 2017, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net.wifi.hotspot2.pps;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.Log;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Class representing Policy subtree in PerProviderSubscription (PPS)
* Management Object (MO) tree.
*
* The Policy specifies additional criteria for Passpoint network selections, such as preferred
* roaming partner, minimum backhaul bandwidth, and etc. It also provides the meta data for
* updating the policy.
*
* For more info, refer to Hotspot 2.0 PPS MO defined in section 9.1 of the Hotspot 2.0
* Release 2 Technical Specification.
*
* @hide
*/
public final class Policy implements Parcelable {
private static final String TAG = "Policy";
/**
* Default priority for preferred roaming partner.
*/
public static final int PREFERRED_ROAMING_PARTNER_DEFAULT_PRIORITY = 128;
/**
* Maximum number of SSIDs in the exclusion list.
*/
private static final int MAX_EXCLUSION_SSIDS = 128;
/**
* Maximum byte for SSID.
*/
private static final int MAX_SSID_BYTES = 32;
/**
* Maximum bytes for port string in {@link #requiredProtoPortMap}.
*/
private static final int MAX_PORT_STRING_BYTES = 64;
/**
* Integer value used for indicating null value in the Parcel.
*/
private static final int NULL_VALUE = -1;
/**
* Minimum available downlink/uplink bandwidth (in kilobits per second) required when
* selecting a network from home providers.
*
* The bandwidth is calculated as the LinkSpeed * (1 – LinkLoad/255), where LinkSpeed
* and LinkLoad parameters are drawn from the WAN Metrics ANQP element at that hotspot.
*
* Using Long.MIN_VALUE to indicate unset value.
*/
public long minHomeDownlinkBandwidth = Long.MIN_VALUE;
public long minHomeUplinkBandwidth = Long.MIN_VALUE;
/**
* Minimum available downlink/uplink bandwidth (in kilobits per second) required when
* selecting a network from roaming providers.
*
* The bandwidth is calculated as the LinkSpeed * (1 – LinkLoad/255), where LinkSpeed
* and LinkLoad parameters are drawn from the WAN Metrics ANQP element at that hotspot.
*
* Using Long.MIN_VALUE to indicate unset value.
*/
public long minRoamingDownlinkBandwidth = Long.MIN_VALUE;
public long minRoamingUplinkBandwidth = Long.MIN_VALUE;
/**
* List of SSIDs that are not preferred by the Home SP.
*/
public String[] excludedSsidList = null;
/**
* List of IP protocol and port number required by one or more operator supported application.
* The port string contained one or more port numbers delimited by ",".
*/
public Map<Integer, String> requiredProtoPortMap = null;
/**
* This specifies the maximum acceptable BSS load policy. This is used to prevent device
* from joining an AP whose channel is overly congested with traffic.
* Using Integer.MIN_VALUE to indicate unset value.
*/
public int maximumBssLoadValue = Integer.MIN_VALUE;
/**
* Policy associated with a roaming provider. This specifies a priority associated
* with a roaming provider for given list of countries.
*
* Contains field under PerProviderSubscription/Policy/PreferredRoamingPartnerList.
*/
public static final class RoamingPartner implements Parcelable {
/**
* FQDN of the roaming partner.
*/
public String fqdn = null;
/**
* Flag indicating the exact match of FQDN is required for FQDN matching.
*
* When this flag is set to false, sub-domain matching is used. For example, when
* {@link #fqdn} s set to "example.com", "host.example.com" would be a match.
*/
public boolean fqdnExactMatch = false;
/**
* Priority associated with this roaming partner policy.
*/
public int priority = PREFERRED_ROAMING_PARTNER_DEFAULT_PRIORITY;
/**
* A string contained One or more, comma delimited (i.e., ",") ISO/IEC 3166-1 two
* character country strings or the country-independent value, "*".
*/
public String countries = null;
public RoamingPartner() {}
public RoamingPartner(RoamingPartner source) {
if (source != null) {
fqdn = source.fqdn;
fqdnExactMatch = source.fqdnExactMatch;
priority = source.priority;
countries = source.countries;
}
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(fqdn);
dest.writeInt(fqdnExactMatch ? 1 : 0);
dest.writeInt(priority);
dest.writeString(countries);
}
@Override
public boolean equals(Object thatObject) {
if (this == thatObject) {
return true;
}
if (!(thatObject instanceof RoamingPartner)) {
return false;
}
RoamingPartner that = (RoamingPartner) thatObject;
return TextUtils.equals(fqdn, that.fqdn)
&& fqdnExactMatch == that.fqdnExactMatch
&& priority == that.priority
&& TextUtils.equals(countries, that.countries);
}
/**
* Validate RoamingParnter data.
*
* @return true on success
*/
public boolean validate() {
if (TextUtils.isEmpty(fqdn)) {
Log.d(TAG, "Missing FQDN");
return false;
}
if (TextUtils.isEmpty(countries)) {
Log.d(TAG, "Missing countries");
return false;
}
return true;
}
public static final Creator<RoamingPartner> CREATOR =
new Creator<RoamingPartner>() {
@Override
public RoamingPartner createFromParcel(Parcel in) {
RoamingPartner roamingPartner = new RoamingPartner();
roamingPartner.fqdn = in.readString();
roamingPartner.fqdnExactMatch = in.readInt() != 0;
roamingPartner.priority = in.readInt();
roamingPartner.countries = in.readString();
return roamingPartner;
}
@Override
public RoamingPartner[] newArray(int size) {
return new RoamingPartner[size];
}
};
}
public List<RoamingPartner> preferredRoamingPartnerList = null;
/**
* Meta data used for policy update.
*/
public UpdateParameter policyUpdate = null;
/**
* Constructor for creating Policy with default values.
*/
public Policy() {}
/**
* Copy constructor.
*
* @param source The source to copy from
*/
public Policy(Policy source) {
if (source == null) {
return;
}
minHomeDownlinkBandwidth = source.minHomeDownlinkBandwidth;
minHomeUplinkBandwidth = source.minHomeUplinkBandwidth;
minRoamingDownlinkBandwidth = source.minRoamingDownlinkBandwidth;
minRoamingUplinkBandwidth = source.minRoamingUplinkBandwidth;
maximumBssLoadValue = source.maximumBssLoadValue;
if (source.excludedSsidList != null) {
excludedSsidList = Arrays.copyOf(source.excludedSsidList,
source.excludedSsidList.length);
}
if (source.requiredProtoPortMap != null) {
requiredProtoPortMap = Collections.unmodifiableMap(source.requiredProtoPortMap);
}
if (source.preferredRoamingPartnerList != null) {
preferredRoamingPartnerList = Collections.unmodifiableList(
source.preferredRoamingPartnerList);
}
if (source.policyUpdate != null) {
policyUpdate = new UpdateParameter(source.policyUpdate);
}
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(minHomeDownlinkBandwidth);
dest.writeLong(minHomeUplinkBandwidth);
dest.writeLong(minRoamingDownlinkBandwidth);
dest.writeLong(minRoamingUplinkBandwidth);
dest.writeStringArray(excludedSsidList);
writeProtoPortMap(dest, requiredProtoPortMap);
dest.writeInt(maximumBssLoadValue);
writeRoamingPartnerList(dest, flags, preferredRoamingPartnerList);
dest.writeParcelable(policyUpdate, flags);
}
@Override
public boolean equals(Object thatObject) {
if (this == thatObject) {
return true;
}
if (!(thatObject instanceof Policy)) {
return false;
}
Policy that = (Policy) thatObject;
return minHomeDownlinkBandwidth == that.minHomeDownlinkBandwidth
&& minHomeUplinkBandwidth == that.minHomeUplinkBandwidth
&& minRoamingDownlinkBandwidth == that.minRoamingDownlinkBandwidth
&& minRoamingUplinkBandwidth == that.minRoamingUplinkBandwidth
&& Arrays.equals(excludedSsidList, that.excludedSsidList)
&& (requiredProtoPortMap == null) ? that.requiredProtoPortMap == null
: requiredProtoPortMap.equals(that.requiredProtoPortMap)
&& maximumBssLoadValue == that.maximumBssLoadValue
&& (preferredRoamingPartnerList == null) ? that.preferredRoamingPartnerList == null
: preferredRoamingPartnerList.equals(that.preferredRoamingPartnerList)
&& (policyUpdate == null) ? that.policyUpdate == null
: policyUpdate.equals(that.policyUpdate);
}
/**
* Validate Policy data.
*
* @return true on success
*/
public boolean validate() {
if (policyUpdate == null) {
Log.d(TAG, "PolicyUpdate not specified");
return false;
}
if (!policyUpdate.validate()) {
return false;
}
// Validate SSID exclusion list.
if (excludedSsidList != null) {
if (excludedSsidList.length > MAX_EXCLUSION_SSIDS) {
Log.d(TAG, "SSID exclusion list size exceeded the max: "
+ excludedSsidList.length);
return false;
}
for (String ssid : excludedSsidList) {
if (ssid.getBytes(StandardCharsets.UTF_8).length > MAX_SSID_BYTES) {
Log.d(TAG, "Invalid SSID: " + ssid);
return false;
}
}
}
// Validate required protocol to port map.
if (requiredProtoPortMap != null) {
for (Map.Entry<Integer, String> entry : requiredProtoPortMap.entrySet()) {
String portNumber = entry.getValue();
if (portNumber.getBytes(StandardCharsets.UTF_8).length > MAX_PORT_STRING_BYTES) {
Log.d(TAG, "PortNumber string bytes exceeded the max: " + portNumber);
return false;
}
}
}
// Validate preferred roaming partner list.
if (preferredRoamingPartnerList != null) {
for (RoamingPartner partner : preferredRoamingPartnerList) {
if (!partner.validate()) {
return false;
}
}
}
return true;
}
public static final Creator<Policy> CREATOR =
new Creator<Policy>() {
@Override
public Policy createFromParcel(Parcel in) {
Policy policy = new Policy();
policy.minHomeDownlinkBandwidth = in.readLong();
policy.minHomeUplinkBandwidth = in.readLong();
policy.minRoamingDownlinkBandwidth = in.readLong();
policy.minRoamingUplinkBandwidth = in.readLong();
policy.excludedSsidList = in.createStringArray();
policy.requiredProtoPortMap = readProtoPortMap(in);
policy.maximumBssLoadValue = in.readInt();
policy.preferredRoamingPartnerList = readRoamingPartnerList(in);
policy.policyUpdate = in.readParcelable(null);
return policy;
}
@Override
public Policy[] newArray(int size) {
return new Policy[size];
}
/**
* Helper function for reading IP Protocol to Port Number map from a Parcel.
*
* @param in The Parcel to read from
* @return Map of IP protocol to port number
*/
private Map<Integer, String> readProtoPortMap(Parcel in) {
int size = in.readInt();
if (size == NULL_VALUE) {
return null;
}
Map<Integer, String> protoPortMap = new HashMap<>(size);
for (int i = 0; i < size; i++) {
int key = in.readInt();
String value = in.readString();
protoPortMap.put(key, value);
}
return protoPortMap;
}
/**
* Helper function for reading roaming partner list from a Parcel.
*
* @param in The Parcel to read from
* @return List of roaming partners
*/
private List<RoamingPartner> readRoamingPartnerList(Parcel in) {
int size = in.readInt();
if (size == NULL_VALUE) {
return null;
}
List<RoamingPartner> partnerList = new ArrayList<>();
for (int i = 0; i < size; i++) {
partnerList.add(in.readParcelable(null));
}
return partnerList;
}
};
/**
* Helper function for writing IP Protocol to Port Number map to a Parcel.
*
* @param dest The Parcel to write to
* @param protoPortMap The map to write
*/
private static void writeProtoPortMap(Parcel dest, Map<Integer, String> protoPortMap) {
if (protoPortMap == null) {
dest.writeInt(NULL_VALUE);
return;
}
dest.writeInt(protoPortMap.size());
for (Map.Entry<Integer, String> entry : protoPortMap.entrySet()) {
dest.writeInt(entry.getKey());
dest.writeString(entry.getValue());
}
}
/**
* Helper function for writing roaming partner list to a Parcel.
*
* @param dest The Parcel to write to
* @param flags The flag about how the object should be written
* @param partnerList The partner list to write
*/
private static void writeRoamingPartnerList(Parcel dest, int flags,
List<RoamingPartner> partnerList) {
if (partnerList == null) {
dest.writeInt(NULL_VALUE);
return;
}
dest.writeInt(partnerList.size());
for (RoamingPartner partner : partnerList) {
dest.writeParcelable(partner, flags);
}
}
}