1ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistpackage com.android.hotspot2.pps; 2ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 3ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport com.android.hotspot2.Utils; 4ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 5ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.util.ArrayList; 6ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.util.Collection; 7ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.util.HashSet; 8ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.util.List; 9ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.util.Map; 10ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.util.Set; 11ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 12ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistpublic class HomeSP { 13ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private final Map<String, Long> mSSIDs; // SSID, HESSID, [0,N] 14ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private final String mFQDN; 15ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private final DomainMatcher mDomainMatcher; 16ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private final Set<String> mOtherHomePartners; 17ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private final HashSet<Long> mRoamingConsortiums; // [0,N] 18ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private final Set<Long> mMatchAnyOIs; // [0,N] 19ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private final List<Long> mMatchAllOIs; // [0,N] 20ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 21ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private final Credential mCredential; 22ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 23ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist // Informational: 24ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private final String mFriendlyName; // [1] 25ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private final String mIconURL; // [0,1] 26ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 27ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private final Policy mPolicy; 28ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private final int mCredentialPriority; 29ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private final Map<String, String> mAAATrustRoots; 30ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private final UpdateInfo mSubscriptionUpdate; 31ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private final SubscriptionParameters mSubscriptionParameters; 32ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private final int mUpdateIdentifier; 33ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 34ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist @Deprecated 35ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist public HomeSP(Map<String, Long> ssidMap, 36ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist /*@NotNull*/ String fqdn, 37ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist /*@NotNull*/ HashSet<Long> roamingConsortiums, 38ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist /*@NotNull*/ Set<String> otherHomePartners, 39ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist /*@NotNull*/ Set<Long> matchAnyOIs, 40ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist /*@NotNull*/ List<Long> matchAllOIs, 41ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist String friendlyName, 42ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist String iconURL, 43ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Credential credential) { 44ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 45ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mSSIDs = ssidMap; 46ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist List<List<String>> otherPartners = new ArrayList<>(otherHomePartners.size()); 47ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist for (String otherPartner : otherHomePartners) { 48ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist otherPartners.add(Utils.splitDomain(otherPartner)); 49ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 50ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mOtherHomePartners = otherHomePartners; 51ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mFQDN = fqdn; 52ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mDomainMatcher = new DomainMatcher(Utils.splitDomain(fqdn), otherPartners); 53ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mRoamingConsortiums = roamingConsortiums; 54ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mMatchAnyOIs = matchAnyOIs; 55ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mMatchAllOIs = matchAllOIs; 56ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mFriendlyName = friendlyName; 57ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mIconURL = iconURL; 58ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mCredential = credential; 59ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 60ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mPolicy = null; 61ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mCredentialPriority = -1; 62ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mAAATrustRoots = null; 63ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mSubscriptionUpdate = null; 64ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mSubscriptionParameters = null; 65ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mUpdateIdentifier = -1; 66ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 67ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 68ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist public HomeSP(Map<String, Long> ssidMap, 69ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist /*@NotNull*/ String fqdn, 70ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist /*@NotNull*/ HashSet<Long> roamingConsortiums, 71ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist /*@NotNull*/ Set<String> otherHomePartners, 72ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist /*@NotNull*/ Set<Long> matchAnyOIs, 73ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist /*@NotNull*/ List<Long> matchAllOIs, 74ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist String friendlyName, 75ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist String iconURL, 76ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Credential credential, 77ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 78ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Policy policy, 79ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist int credentialPriority, 80ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Map<String, String> AAATrustRoots, 81ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist UpdateInfo subscriptionUpdate, 82ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist SubscriptionParameters subscriptionParameters, 83ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist int updateIdentifier) { 84ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 85ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mSSIDs = ssidMap; 86ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist List<List<String>> otherPartners = new ArrayList<>(otherHomePartners.size()); 87ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist for (String otherPartner : otherHomePartners) { 88ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist otherPartners.add(Utils.splitDomain(otherPartner)); 89ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 90ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mOtherHomePartners = otherHomePartners; 91ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mFQDN = fqdn; 92ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mDomainMatcher = new DomainMatcher(Utils.splitDomain(fqdn), otherPartners); 93ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mRoamingConsortiums = roamingConsortiums; 94ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mMatchAnyOIs = matchAnyOIs; 95ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mMatchAllOIs = matchAllOIs; 96ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mFriendlyName = friendlyName; 97ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mIconURL = iconURL; 98ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mCredential = credential; 99ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 100ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mPolicy = policy; 101ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mCredentialPriority = credentialPriority; 102ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mAAATrustRoots = AAATrustRoots; 103ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mSubscriptionUpdate = subscriptionUpdate; 104ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mSubscriptionParameters = subscriptionParameters; 105ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mUpdateIdentifier = updateIdentifier; 106ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 107ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 108ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist public int getUpdateIdentifier() { 109ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist return mUpdateIdentifier; 110ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 111ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 112ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist public UpdateInfo getSubscriptionUpdate() { 113ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist return mSubscriptionUpdate; 114ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 115ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 116ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist public Policy getPolicy() { 117ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist return mPolicy; 118ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 119ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 120ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private String imsiMatch(List<String> imsis, String mccMnc) { 121ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (mCredential.getImsi().matchesMccMnc(mccMnc)) { 122ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist for (String imsi : imsis) { 123ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (imsi.startsWith(mccMnc)) { 124ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist return imsi; 125ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 126ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 127ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 128ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist return null; 129ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 130ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 131ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist public String getFQDN() { 132ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist return mFQDN; 133ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 134ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 135ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist public String getFriendlyName() { 136ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist return mFriendlyName; 137ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 138ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 139ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist public HashSet<Long> getRoamingConsortiums() { 140ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist return mRoamingConsortiums; 141ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 142ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 143ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist public Credential getCredential() { 144ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist return mCredential; 145ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 146ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 147ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist public Map<String, Long> getSSIDs() { 148ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist return mSSIDs; 149ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 150ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 151ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist public Collection<String> getOtherHomePartners() { 152ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist return mOtherHomePartners; 153ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 154ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 155ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist public Set<Long> getMatchAnyOIs() { 156ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist return mMatchAnyOIs; 157ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 158ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 159ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist public List<Long> getMatchAllOIs() { 160ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist return mMatchAllOIs; 161ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 162ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 163ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist public String getIconURL() { 164ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist return mIconURL; 165ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 166ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 167ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist public boolean deepEquals(HomeSP other) { 168ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist return mFQDN.equals(other.mFQDN) && 169ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mSSIDs.equals(other.mSSIDs) && 170ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mOtherHomePartners.equals(other.mOtherHomePartners) && 171ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mRoamingConsortiums.equals(other.mRoamingConsortiums) && 172ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mMatchAnyOIs.equals(other.mMatchAnyOIs) && 173ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mMatchAllOIs.equals(other.mMatchAllOIs) && 174ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mFriendlyName.equals(other.mFriendlyName) && 175ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Utils.compare(mIconURL, other.mIconURL) == 0 && 176ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mCredential.equals(other.mCredential); 177ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 178ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 179ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist @Override 180ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist public boolean equals(Object thatObject) { 181ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (this == thatObject) { 182ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist return true; 183ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } else if (thatObject == null || getClass() != thatObject.getClass()) { 184ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist return false; 185ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 186ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 187ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist HomeSP that = (HomeSP) thatObject; 188ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist return mFQDN.equals(that.mFQDN); 189ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 190ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 191ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist @Override 192ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist public int hashCode() { 193ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist return mFQDN.hashCode(); 194ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 195ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 196ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist @Override 197ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist public String toString() { 198ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist return "HomeSP{" + 199ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist "SSIDs=" + mSSIDs + 200ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist ", FQDN='" + mFQDN + '\'' + 201ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist ", DomainMatcher=" + mDomainMatcher + 202ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist ", RoamingConsortiums={" + Utils.roamingConsortiumsToString(mRoamingConsortiums) + 203ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist '}' + 204ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist ", MatchAnyOIs={" + Utils.roamingConsortiumsToString(mMatchAnyOIs) + '}' + 205ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist ", MatchAllOIs={" + Utils.roamingConsortiumsToString(mMatchAllOIs) + '}' + 206ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist ", Credential=" + mCredential + 207ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist ", FriendlyName='" + mFriendlyName + '\'' + 208ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist ", IconURL='" + mIconURL + '\'' + 209ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist '}'; 210ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 211ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist} 212