1 package com.android.anqp; 2 3 import android.os.Parcel; 4 5 import com.android.hotspot2.Utils; 6 7 import java.io.IOException; 8 import java.net.ProtocolException; 9 import java.nio.ByteBuffer; 10 import java.nio.ByteOrder; 11 import java.nio.charset.StandardCharsets; 12 import java.util.ArrayList; 13 import java.util.List; 14 15 import static com.android.anqp.Constants.BYTE_MASK; 16 import static com.android.anqp.Constants.SHORT_MASK; 17 18 /** 19 * An OSU Provider, as specified in 20 * Wi-Fi Alliance Hotspot 2.0 (Release 2) Technical Specification - Version 5.00, 21 * section 4.8.1 22 */ 23 public class OSUProvider { 24 25 public enum OSUMethod {OmaDm, SoapXml} 26 27 private final String mSSID; 28 private final List<I18Name> mNames; 29 private final String mOSUServer; 30 private final List<OSUMethod> mOSUMethods; 31 private final List<IconInfo> mIcons; 32 private final String mOsuNai; 33 private final List<I18Name> mServiceDescriptions; 34 private final int mHashCode; 35 OSUProvider(String ssid, ByteBuffer payload)36 public OSUProvider(String ssid, ByteBuffer payload) throws ProtocolException { 37 if (payload.remaining() < 11) { 38 throw new ProtocolException("Truncated OSU provider: " + payload.remaining()); 39 } 40 41 mSSID = ssid; 42 43 int length = payload.getShort() & SHORT_MASK; 44 int namesLength = payload.getShort() & SHORT_MASK; 45 46 ByteBuffer namesBuffer = payload.duplicate().order(ByteOrder.LITTLE_ENDIAN); 47 namesBuffer.limit(namesBuffer.position() + namesLength); 48 payload.position(payload.position() + namesLength); 49 50 mNames = new ArrayList<>(); 51 52 while (namesBuffer.hasRemaining()) { 53 mNames.add(new I18Name(namesBuffer)); 54 } 55 56 mOSUServer = Constants.getPrefixedString(payload, 1, StandardCharsets.UTF_8); 57 int methodLength = payload.get() & BYTE_MASK; 58 mOSUMethods = new ArrayList<>(methodLength); 59 while (methodLength > 0) { 60 int methodID = payload.get() & BYTE_MASK; 61 mOSUMethods.add(methodID < OSUMethod.values().length ? 62 OSUMethod.values()[methodID] : 63 null); 64 methodLength--; 65 } 66 67 int iconsLength = payload.getShort() & SHORT_MASK; 68 ByteBuffer iconsBuffer = payload.duplicate().order(ByteOrder.LITTLE_ENDIAN); 69 iconsBuffer.limit(iconsBuffer.position() + iconsLength); 70 payload.position(payload.position() + iconsLength); 71 72 mIcons = new ArrayList<>(); 73 74 while (iconsBuffer.hasRemaining()) { 75 mIcons.add(new IconInfo(iconsBuffer)); 76 } 77 78 mOsuNai = Constants.getPrefixedString(payload, 1, StandardCharsets.UTF_8, true); 79 80 int descriptionsLength = payload.getShort() & SHORT_MASK; 81 ByteBuffer descriptionsBuffer = payload.duplicate().order(ByteOrder.LITTLE_ENDIAN); 82 descriptionsBuffer.limit(descriptionsBuffer.position() + descriptionsLength); 83 payload.position(payload.position() + descriptionsLength); 84 85 mServiceDescriptions = new ArrayList<>(); 86 87 while (descriptionsBuffer.hasRemaining()) { 88 mServiceDescriptions.add(new I18Name(descriptionsBuffer)); 89 } 90 91 int result = mNames.hashCode(); 92 result = 31 * result + mSSID.hashCode(); 93 result = 31 * result + mOSUServer.hashCode(); 94 result = 31 * result + mOSUMethods.hashCode(); 95 result = 31 * result + mIcons.hashCode(); 96 result = 31 * result + (mOsuNai != null ? mOsuNai.hashCode() : 0); 97 result = 31 * result + mServiceDescriptions.hashCode(); 98 mHashCode = result; 99 } 100 101 public String getSSID() { 102 return mSSID; 103 } 104 105 public List<I18Name> getNames() { 106 return mNames; 107 } 108 109 public String getOSUServer() { 110 return mOSUServer; 111 } 112 113 public List<OSUMethod> getOSUMethods() { 114 return mOSUMethods; 115 } 116 117 public List<IconInfo> getIcons() { 118 return mIcons; 119 } 120 121 public String getOsuNai() { 122 return mOsuNai; 123 } 124 125 public List<I18Name> getServiceDescriptions() { 126 return mServiceDescriptions; 127 } 128 129 @Override 130 public boolean equals(Object o) { 131 if (this == o) return true; 132 if (o == null || getClass() != o.getClass()) return false; 133 134 OSUProvider that = (OSUProvider) o; 135 136 if (!mSSID.equals(that.mSSID)) return false; 137 if (!mOSUServer.equals(that.mOSUServer)) return false; 138 if (!mNames.equals(that.mNames)) return false; 139 if (!mServiceDescriptions.equals(that.mServiceDescriptions)) return false; 140 if (!mIcons.equals(that.mIcons)) return false; 141 if (!mOSUMethods.equals(that.mOSUMethods)) return false; 142 if (mOsuNai != null ? !mOsuNai.equals(that.mOsuNai) : that.mOsuNai != null) return false; 143 144 return true; 145 } 146 147 @Override 148 public int hashCode() { 149 return mHashCode; 150 } 151 152 @Override 153 public String toString() { 154 return "OSUProvider{" + 155 "names=" + mNames + 156 ", OSUServer='" + mOSUServer + '\'' + 157 ", OSUMethods=" + mOSUMethods + 158 ", icons=" + mIcons + 159 ", NAI='" + mOsuNai + '\'' + 160 ", serviceDescriptions=" + mServiceDescriptions + 161 '}'; 162 } 163 164 public OSUProvider(Parcel in) throws IOException { 165 mSSID = in.readString(); 166 int nameCount = in.readInt(); 167 mNames = new ArrayList<>(nameCount); 168 for (int n = 0; n < nameCount; n++) { 169 mNames.add(new I18Name(in)); 170 } 171 mOSUServer = in.readString(); 172 int methodCount = in.readInt(); 173 mOSUMethods = new ArrayList<>(methodCount); 174 for (int n = 0; n < methodCount; n++) { 175 mOSUMethods.add(Utils.mapEnum(in.readInt(), OSUMethod.class)); 176 } 177 int iconCount = in.readInt(); 178 mIcons = new ArrayList<>(iconCount); 179 for (int n = 0; n < iconCount; n++) { 180 mIcons.add(new IconInfo(in)); 181 } 182 mOsuNai = in.readString(); 183 int serviceCount = in.readInt(); 184 mServiceDescriptions = new ArrayList<>(serviceCount); 185 for (int n = 0; n < serviceCount; n++) { 186 mServiceDescriptions.add(new I18Name(in)); 187 } 188 mHashCode = in.readInt(); 189 } 190 191 public void writeParcel(Parcel out) { 192 out.writeString(mSSID); 193 out.writeInt(mNames.size()); 194 for (I18Name name : mNames) { 195 name.writeParcel(out); 196 } 197 out.writeString(mOSUServer); 198 out.writeInt(mOSUMethods.size()); 199 for (OSUMethod method : mOSUMethods) { 200 out.writeInt(method.ordinal()); 201 } 202 out.writeInt(mIcons.size()); 203 for (IconInfo iconInfo : mIcons) { 204 iconInfo.writeParcel(out); 205 } 206 out.writeString(mOsuNai); 207 out.writeInt(mServiceDescriptions.size()); 208 for (I18Name serviceDescription : mServiceDescriptions) { 209 serviceDescription.writeParcel(out); 210 } 211 out.writeInt(mHashCode); 212 } 213 } 214