Home | History | Annotate | Download | only in pps
      1 /**
      2  * Copyright (c) 2017, 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.Parcel;
     20 import android.os.Parcelable;
     21 import android.text.TextUtils;
     22 import android.util.Log;
     23 
     24 import java.nio.charset.StandardCharsets;
     25 import java.util.ArrayList;
     26 import java.util.Arrays;
     27 import java.util.Collections;
     28 import java.util.HashMap;
     29 import java.util.List;
     30 import java.util.Map;
     31 import java.util.Objects;
     32 
     33 /**
     34  * Class representing Policy subtree in PerProviderSubscription (PPS)
     35  * Management Object (MO) tree.
     36  *
     37  * The Policy specifies additional criteria for Passpoint network selections, such as preferred
     38  * roaming partner, minimum backhaul bandwidth, and etc. It also provides the meta data for
     39  * updating the policy.
     40  *
     41  * For more info, refer to Hotspot 2.0 PPS MO defined in section 9.1 of the Hotspot 2.0
     42  * Release 2 Technical Specification.
     43  *
     44  * @hide
     45  */
     46 public final class Policy implements Parcelable {
     47     private static final String TAG = "Policy";
     48 
     49     /**
     50      * Maximum number of SSIDs in the exclusion list.
     51      */
     52     private static final int MAX_EXCLUSION_SSIDS = 128;
     53 
     54     /**
     55      * Maximum byte for SSID.
     56      */
     57     private static final int MAX_SSID_BYTES = 32;
     58 
     59     /**
     60      * Maximum bytes for port string in {@link #requiredProtoPortMap}.
     61      */
     62     private static final int MAX_PORT_STRING_BYTES = 64;
     63 
     64     /**
     65      * Integer value used for indicating null value in the Parcel.
     66      */
     67     private static final int NULL_VALUE = -1;
     68 
     69     /**
     70      * Minimum available downlink/uplink bandwidth (in kilobits per second) required when
     71      * selecting a network from home providers.
     72      *
     73      * The bandwidth is calculated as the LinkSpeed * (1  LinkLoad/255), where LinkSpeed
     74      * and LinkLoad parameters are drawn from the WAN Metrics ANQP element at that hotspot.
     75      *
     76      * Using Long.MIN_VALUE to indicate unset value.
     77      */
     78     private long mMinHomeDownlinkBandwidth = Long.MIN_VALUE;
     79     public void setMinHomeDownlinkBandwidth(long minHomeDownlinkBandwidth) {
     80         mMinHomeDownlinkBandwidth = minHomeDownlinkBandwidth;
     81     }
     82     public long getMinHomeDownlinkBandwidth() {
     83         return mMinHomeDownlinkBandwidth;
     84     }
     85     private long mMinHomeUplinkBandwidth = Long.MIN_VALUE;
     86     public void setMinHomeUplinkBandwidth(long minHomeUplinkBandwidth) {
     87         mMinHomeUplinkBandwidth = minHomeUplinkBandwidth;
     88     }
     89     public long getMinHomeUplinkBandwidth() {
     90         return mMinHomeUplinkBandwidth;
     91     }
     92 
     93     /**
     94      * Minimum available downlink/uplink bandwidth (in kilobits per second) required when
     95      * selecting a network from roaming providers.
     96      *
     97      * The bandwidth is calculated as the LinkSpeed * (1  LinkLoad/255), where LinkSpeed
     98      * and LinkLoad parameters are drawn from the WAN Metrics ANQP element at that hotspot.
     99      *
    100      * Using Long.MIN_VALUE to indicate unset value.
    101      */
    102     private long mMinRoamingDownlinkBandwidth = Long.MIN_VALUE;
    103     public void setMinRoamingDownlinkBandwidth(long minRoamingDownlinkBandwidth) {
    104         mMinRoamingDownlinkBandwidth = minRoamingDownlinkBandwidth;
    105     }
    106     public long getMinRoamingDownlinkBandwidth() {
    107         return mMinRoamingDownlinkBandwidth;
    108     }
    109     private long mMinRoamingUplinkBandwidth = Long.MIN_VALUE;
    110     public void setMinRoamingUplinkBandwidth(long minRoamingUplinkBandwidth) {
    111         mMinRoamingUplinkBandwidth = minRoamingUplinkBandwidth;
    112     }
    113     public long getMinRoamingUplinkBandwidth() {
    114         return mMinRoamingUplinkBandwidth;
    115     }
    116 
    117     /**
    118      * List of SSIDs that are not preferred by the Home SP.
    119      */
    120     private String[] mExcludedSsidList = null;
    121     public void setExcludedSsidList(String[] excludedSsidList) {
    122         mExcludedSsidList = excludedSsidList;
    123     }
    124     public String[] getExcludedSsidList() {
    125         return mExcludedSsidList;
    126     }
    127 
    128     /**
    129      * List of IP protocol and port number required by one or more operator supported application.
    130      * The port string contained one or more port numbers delimited by ",".
    131      */
    132     private Map<Integer, String> mRequiredProtoPortMap = null;
    133     public void setRequiredProtoPortMap(Map<Integer, String> requiredProtoPortMap) {
    134         mRequiredProtoPortMap = requiredProtoPortMap;
    135     }
    136     public Map<Integer, String> getRequiredProtoPortMap() {
    137         return mRequiredProtoPortMap;
    138     }
    139 
    140     /**
    141      * This specifies the maximum acceptable BSS load policy.  This is used to prevent device
    142      * from joining an AP whose channel is overly congested with traffic.
    143      * Using Integer.MIN_VALUE to indicate unset value.
    144      */
    145     private int mMaximumBssLoadValue = Integer.MIN_VALUE;
    146     public void setMaximumBssLoadValue(int maximumBssLoadValue) {
    147         mMaximumBssLoadValue = maximumBssLoadValue;
    148     }
    149     public int getMaximumBssLoadValue() {
    150         return mMaximumBssLoadValue;
    151     }
    152 
    153     /**
    154      * Policy associated with a roaming provider.  This specifies a priority associated
    155      * with a roaming provider for given list of countries.
    156      *
    157      * Contains field under PerProviderSubscription/Policy/PreferredRoamingPartnerList.
    158      */
    159     public static final class RoamingPartner implements Parcelable {
    160         /**
    161          * FQDN of the roaming partner.
    162          */
    163         private String mFqdn = null;
    164         public void setFqdn(String fqdn) {
    165             mFqdn = fqdn;
    166         }
    167         public String getFqdn() {
    168             return mFqdn;
    169         }
    170 
    171         /**
    172          * Flag indicating the exact match of FQDN is required for FQDN matching.
    173          *
    174          * When this flag is set to false, sub-domain matching is used.  For example, when
    175          * {@link #fqdn} s set to "example.com", "host.example.com" would be a match.
    176          */
    177         private boolean mFqdnExactMatch = false;
    178         public void setFqdnExactMatch(boolean fqdnExactMatch) {
    179             mFqdnExactMatch = fqdnExactMatch;
    180         }
    181         public boolean getFqdnExactMatch() {
    182             return mFqdnExactMatch;
    183         }
    184 
    185         /**
    186          * Priority associated with this roaming partner policy.
    187          * Using Integer.MIN_VALUE to indicate unset value.
    188          */
    189         private int mPriority = Integer.MIN_VALUE;
    190         public void setPriority(int priority) {
    191             mPriority = priority;
    192         }
    193         public int getPriority() {
    194             return mPriority;
    195         }
    196 
    197         /**
    198          * A string contained One or more, comma delimited (i.e., ",") ISO/IEC 3166-1 two
    199          * character country strings or the country-independent value, "*".
    200          */
    201         private String mCountries = null;
    202         public void setCountries(String countries) {
    203             mCountries = countries;
    204         }
    205         public String getCountries() {
    206             return mCountries;
    207         }
    208 
    209         public RoamingPartner() {}
    210 
    211         public RoamingPartner(RoamingPartner source) {
    212             if (source != null) {
    213                 mFqdn = source.mFqdn;
    214                 mFqdnExactMatch = source.mFqdnExactMatch;
    215                 mPriority = source.mPriority;
    216                 mCountries = source.mCountries;
    217             }
    218         }
    219 
    220         @Override
    221         public int describeContents() {
    222             return 0;
    223         }
    224 
    225         @Override
    226         public void writeToParcel(Parcel dest, int flags) {
    227             dest.writeString(mFqdn);
    228             dest.writeInt(mFqdnExactMatch ? 1 : 0);
    229             dest.writeInt(mPriority);
    230             dest.writeString(mCountries);
    231         }
    232 
    233         @Override
    234         public boolean equals(Object thatObject) {
    235             if (this == thatObject) {
    236                 return true;
    237             }
    238             if (!(thatObject instanceof RoamingPartner)) {
    239                 return false;
    240             }
    241 
    242             RoamingPartner that = (RoamingPartner) thatObject;
    243             return TextUtils.equals(mFqdn, that.mFqdn)
    244                     && mFqdnExactMatch == that.mFqdnExactMatch
    245                     && mPriority == that.mPriority
    246                     && TextUtils.equals(mCountries, that.mCountries);
    247         }
    248 
    249         @Override
    250         public int hashCode() {
    251             return Objects.hash(mFqdn, mFqdnExactMatch, mPriority, mCountries);
    252         }
    253 
    254         @Override
    255         public String toString() {
    256             StringBuilder builder = new StringBuilder();
    257             builder.append("FQDN: ").append(mFqdn).append("\n");
    258             builder.append("ExactMatch: ").append("mFqdnExactMatch").append("\n");
    259             builder.append("Priority: ").append(mPriority).append("\n");
    260             builder.append("Countries: ").append(mCountries).append("\n");
    261             return builder.toString();
    262         }
    263 
    264         /**
    265          * Validate RoamingParnter data.
    266          *
    267          * @return true on success
    268          * @hide
    269          */
    270         public boolean validate() {
    271             if (TextUtils.isEmpty(mFqdn)) {
    272                 Log.d(TAG, "Missing FQDN");
    273                 return false;
    274             }
    275             if (TextUtils.isEmpty(mCountries)) {
    276                 Log.d(TAG, "Missing countries");
    277                 return false;
    278             }
    279             return true;
    280         }
    281 
    282         public static final Creator<RoamingPartner> CREATOR =
    283             new Creator<RoamingPartner>() {
    284                 @Override
    285                 public RoamingPartner createFromParcel(Parcel in) {
    286                     RoamingPartner roamingPartner = new RoamingPartner();
    287                     roamingPartner.setFqdn(in.readString());
    288                     roamingPartner.setFqdnExactMatch(in.readInt() != 0);
    289                     roamingPartner.setPriority(in.readInt());
    290                     roamingPartner.setCountries(in.readString());
    291                     return roamingPartner;
    292                 }
    293 
    294                 @Override
    295                 public RoamingPartner[] newArray(int size) {
    296                     return new RoamingPartner[size];
    297                 }
    298             };
    299     }
    300     private List<RoamingPartner> mPreferredRoamingPartnerList = null;
    301     public void setPreferredRoamingPartnerList(List<RoamingPartner> partnerList) {
    302         mPreferredRoamingPartnerList = partnerList;
    303     }
    304     public List<RoamingPartner> getPreferredRoamingPartnerList() {
    305         return mPreferredRoamingPartnerList;
    306     }
    307 
    308     /**
    309      * Meta data used for policy update.
    310      */
    311     private UpdateParameter mPolicyUpdate = null;
    312     public void setPolicyUpdate(UpdateParameter policyUpdate) {
    313         mPolicyUpdate = policyUpdate;
    314     }
    315     public UpdateParameter getPolicyUpdate() {
    316         return mPolicyUpdate;
    317     }
    318 
    319     /**
    320      * Constructor for creating Policy with default values.
    321      */
    322     public Policy() {}
    323 
    324     /**
    325      * Copy constructor.
    326      *
    327      * @param source The source to copy from
    328      */
    329     public Policy(Policy source) {
    330         if (source == null) {
    331             return;
    332         }
    333         mMinHomeDownlinkBandwidth = source.mMinHomeDownlinkBandwidth;
    334         mMinHomeUplinkBandwidth = source.mMinHomeUplinkBandwidth;
    335         mMinRoamingDownlinkBandwidth = source.mMinRoamingDownlinkBandwidth;
    336         mMinRoamingUplinkBandwidth = source.mMinRoamingUplinkBandwidth;
    337         mMaximumBssLoadValue = source.mMaximumBssLoadValue;
    338         if (source.mExcludedSsidList != null) {
    339             mExcludedSsidList = Arrays.copyOf(source.mExcludedSsidList,
    340                     source.mExcludedSsidList.length);
    341         }
    342         if (source.mRequiredProtoPortMap != null) {
    343             mRequiredProtoPortMap = Collections.unmodifiableMap(source.mRequiredProtoPortMap);
    344         }
    345         if (source.mPreferredRoamingPartnerList != null) {
    346             mPreferredRoamingPartnerList = Collections.unmodifiableList(
    347                     source.mPreferredRoamingPartnerList);
    348         }
    349         if (source.mPolicyUpdate != null) {
    350             mPolicyUpdate = new UpdateParameter(source.mPolicyUpdate);
    351         }
    352     }
    353 
    354     @Override
    355     public int describeContents() {
    356         return 0;
    357     }
    358 
    359     @Override
    360     public void writeToParcel(Parcel dest, int flags) {
    361         dest.writeLong(mMinHomeDownlinkBandwidth);
    362         dest.writeLong(mMinHomeUplinkBandwidth);
    363         dest.writeLong(mMinRoamingDownlinkBandwidth);
    364         dest.writeLong(mMinRoamingUplinkBandwidth);
    365         dest.writeStringArray(mExcludedSsidList);
    366         writeProtoPortMap(dest, mRequiredProtoPortMap);
    367         dest.writeInt(mMaximumBssLoadValue);
    368         writeRoamingPartnerList(dest, flags, mPreferredRoamingPartnerList);
    369         dest.writeParcelable(mPolicyUpdate, flags);
    370     }
    371 
    372     @Override
    373     public boolean equals(Object thatObject) {
    374         if (this == thatObject) {
    375             return true;
    376         }
    377         if (!(thatObject instanceof Policy)) {
    378             return false;
    379         }
    380         Policy that = (Policy) thatObject;
    381 
    382         return mMinHomeDownlinkBandwidth == that.mMinHomeDownlinkBandwidth
    383                 && mMinHomeUplinkBandwidth == that.mMinHomeUplinkBandwidth
    384                 && mMinRoamingDownlinkBandwidth == that.mMinRoamingDownlinkBandwidth
    385                 && mMinRoamingUplinkBandwidth == that.mMinRoamingUplinkBandwidth
    386                 && Arrays.equals(mExcludedSsidList, that.mExcludedSsidList)
    387                 && (mRequiredProtoPortMap == null ? that.mRequiredProtoPortMap == null
    388                         : mRequiredProtoPortMap.equals(that.mRequiredProtoPortMap))
    389                 && mMaximumBssLoadValue == that.mMaximumBssLoadValue
    390                 && (mPreferredRoamingPartnerList == null
    391                         ? that.mPreferredRoamingPartnerList == null
    392                         : mPreferredRoamingPartnerList.equals(that.mPreferredRoamingPartnerList))
    393                 && (mPolicyUpdate == null ? that.mPolicyUpdate == null
    394                         : mPolicyUpdate.equals(that.mPolicyUpdate));
    395     }
    396 
    397     @Override
    398     public int hashCode() {
    399         return Objects.hash(mMinHomeDownlinkBandwidth, mMinHomeUplinkBandwidth,
    400                 mMinRoamingDownlinkBandwidth, mMinRoamingUplinkBandwidth, mExcludedSsidList,
    401                 mRequiredProtoPortMap, mMaximumBssLoadValue, mPreferredRoamingPartnerList,
    402                 mPolicyUpdate);
    403     }
    404 
    405     @Override
    406     public String toString() {
    407         StringBuilder builder = new StringBuilder();
    408         builder.append("MinHomeDownlinkBandwidth: ").append(mMinHomeDownlinkBandwidth)
    409                 .append("\n");
    410         builder.append("MinHomeUplinkBandwidth: ").append(mMinHomeUplinkBandwidth).append("\n");
    411         builder.append("MinRoamingDownlinkBandwidth: ").append(mMinRoamingDownlinkBandwidth)
    412                 .append("\n");
    413         builder.append("MinRoamingUplinkBandwidth: ").append(mMinRoamingUplinkBandwidth)
    414                 .append("\n");
    415         builder.append("ExcludedSSIDList: ").append(mExcludedSsidList).append("\n");
    416         builder.append("RequiredProtoPortMap: ").append(mRequiredProtoPortMap).append("\n");
    417         builder.append("MaximumBSSLoadValue: ").append(mMaximumBssLoadValue).append("\n");
    418         builder.append("PreferredRoamingPartnerList: ").append(mPreferredRoamingPartnerList)
    419                 .append("\n");
    420         if (mPolicyUpdate != null) {
    421             builder.append("PolicyUpdate Begin ---\n");
    422             builder.append(mPolicyUpdate);
    423             builder.append("PolicyUpdate End ---\n");
    424         }
    425         return builder.toString();
    426     }
    427 
    428     /**
    429      * Validate Policy data.
    430      *
    431      * @return true on success
    432      * @hide
    433      */
    434     public boolean validate() {
    435         if (mPolicyUpdate == null) {
    436             Log.d(TAG, "PolicyUpdate not specified");
    437             return false;
    438         }
    439         if (!mPolicyUpdate.validate()) {
    440             return false;
    441         }
    442 
    443         // Validate SSID exclusion list.
    444         if (mExcludedSsidList != null) {
    445             if (mExcludedSsidList.length > MAX_EXCLUSION_SSIDS) {
    446                 Log.d(TAG, "SSID exclusion list size exceeded the max: "
    447                         + mExcludedSsidList.length);
    448                 return false;
    449             }
    450             for (String ssid : mExcludedSsidList) {
    451                 if (ssid.getBytes(StandardCharsets.UTF_8).length > MAX_SSID_BYTES) {
    452                     Log.d(TAG, "Invalid SSID: " + ssid);
    453                     return false;
    454                 }
    455             }
    456         }
    457         // Validate required protocol to port map.
    458         if (mRequiredProtoPortMap != null) {
    459             for (Map.Entry<Integer, String> entry : mRequiredProtoPortMap.entrySet()) {
    460                 String portNumber = entry.getValue();
    461                 if (portNumber.getBytes(StandardCharsets.UTF_8).length > MAX_PORT_STRING_BYTES) {
    462                     Log.d(TAG, "PortNumber string bytes exceeded the max: " + portNumber);
    463                     return false;
    464                 }
    465             }
    466         }
    467         // Validate preferred roaming partner list.
    468         if (mPreferredRoamingPartnerList != null) {
    469             for (RoamingPartner partner : mPreferredRoamingPartnerList) {
    470                 if (!partner.validate()) {
    471                     return false;
    472                 }
    473             }
    474         }
    475         return true;
    476     }
    477 
    478     public static final Creator<Policy> CREATOR =
    479         new Creator<Policy>() {
    480             @Override
    481             public Policy createFromParcel(Parcel in) {
    482                 Policy policy = new Policy();
    483                 policy.setMinHomeDownlinkBandwidth(in.readLong());
    484                 policy.setMinHomeUplinkBandwidth(in.readLong());
    485                 policy.setMinRoamingDownlinkBandwidth(in.readLong());
    486                 policy.setMinRoamingUplinkBandwidth(in.readLong());
    487                 policy.setExcludedSsidList(in.createStringArray());
    488                 policy.setRequiredProtoPortMap(readProtoPortMap(in));
    489                 policy.setMaximumBssLoadValue(in.readInt());
    490                 policy.setPreferredRoamingPartnerList(readRoamingPartnerList(in));
    491                 policy.setPolicyUpdate(in.readParcelable(null));
    492                 return policy;
    493             }
    494 
    495             @Override
    496             public Policy[] newArray(int size) {
    497                 return new Policy[size];
    498             }
    499 
    500             /**
    501              * Helper function for reading IP Protocol to Port Number map from a Parcel.
    502              *
    503              * @param in The Parcel to read from
    504              * @return Map of IP protocol to port number
    505              */
    506             private Map<Integer, String> readProtoPortMap(Parcel in) {
    507                 int size = in.readInt();
    508                 if (size == NULL_VALUE) {
    509                     return null;
    510                 }
    511                 Map<Integer, String> protoPortMap = new HashMap<>(size);
    512                 for (int i = 0; i < size; i++) {
    513                     int key = in.readInt();
    514                     String value = in.readString();
    515                     protoPortMap.put(key, value);
    516                 }
    517                 return protoPortMap;
    518             }
    519 
    520             /**
    521              * Helper function for reading roaming partner list from a Parcel.
    522              *
    523              * @param in The Parcel to read from
    524              * @return List of roaming partners
    525              */
    526             private List<RoamingPartner> readRoamingPartnerList(Parcel in) {
    527                 int size = in.readInt();
    528                 if (size == NULL_VALUE) {
    529                     return null;
    530                 }
    531                 List<RoamingPartner> partnerList = new ArrayList<>();
    532                 for (int i = 0; i < size; i++) {
    533                     partnerList.add(in.readParcelable(null));
    534                 }
    535                 return partnerList;
    536             }
    537 
    538         };
    539 
    540     /**
    541      * Helper function for writing IP Protocol to Port Number map to a Parcel.
    542      *
    543      * @param dest The Parcel to write to
    544      * @param protoPortMap The map to write
    545      */
    546     private static void writeProtoPortMap(Parcel dest, Map<Integer, String> protoPortMap) {
    547         if (protoPortMap == null) {
    548             dest.writeInt(NULL_VALUE);
    549             return;
    550         }
    551         dest.writeInt(protoPortMap.size());
    552         for (Map.Entry<Integer, String> entry : protoPortMap.entrySet()) {
    553             dest.writeInt(entry.getKey());
    554             dest.writeString(entry.getValue());
    555         }
    556     }
    557 
    558     /**
    559      * Helper function for writing roaming partner list to a Parcel.
    560      *
    561      * @param dest The Parcel to write to
    562      * @param flags The flag about how the object should be written
    563      * @param partnerList The partner list to write
    564      */
    565     private static void writeRoamingPartnerList(Parcel dest, int flags,
    566             List<RoamingPartner> partnerList) {
    567         if (partnerList == null) {
    568             dest.writeInt(NULL_VALUE);
    569             return;
    570         }
    571         dest.writeInt(partnerList.size());
    572         for (RoamingPartner partner : partnerList) {
    573             dest.writeParcelable(partner, flags);
    574         }
    575     }
    576 }
    577