1 package com.android.hotspot2.flow; 2 3 import android.net.wifi.ScanResult; 4 import android.os.Parcel; 5 import android.os.Parcelable; 6 import android.util.Log; 7 8 import com.android.anqp.HSIconFileElement; 9 import com.android.anqp.I18Name; 10 import com.android.anqp.IconInfo; 11 import com.android.anqp.OSUProvider; 12 import com.android.hotspot2.Utils; 13 import com.android.hotspot2.osu.OSUManager; 14 15 import java.io.IOException; 16 import java.util.ArrayList; 17 import java.util.Collections; 18 import java.util.HashSet; 19 import java.util.List; 20 import java.util.ListIterator; 21 import java.util.Locale; 22 import java.util.Set; 23 24 public class OSUInfo implements Parcelable { 25 public static final String GenericLocale = "zxx"; 26 27 public enum IconStatus { 28 NotQueried, // 29 InProgress, // Query pending 30 NotAvailable, // Deterministically unavailable 31 Available // Icon data retrieved 32 } 33 34 private final long mBSSID; 35 private final long mHESSID; 36 private final int mAnqpDomID; 37 private final String mAdvertisingSSID; 38 private final OSUProvider mOSUProvider; 39 private final int mOsuID; 40 private long mOSUBssid; 41 private IconStatus mIconStatus = IconStatus.NotQueried; 42 private HSIconFileElement mIconFileElement; 43 private String mIconFileName; 44 private IconInfo mIconInfo; 45 private int mIconIndex; 46 OSUInfo(ScanResult scanResult, OSUProvider osuProvider, int osuID)47 public OSUInfo(ScanResult scanResult, OSUProvider osuProvider, int osuID) { 48 mOsuID = osuID; 49 mBSSID = Utils.parseMac(scanResult.BSSID); 50 mHESSID = scanResult.hessid; 51 mAnqpDomID = scanResult.anqpDomainId; 52 mAdvertisingSSID = scanResult.SSID; 53 mOSUProvider = osuProvider; 54 mIconIndex = -1; 55 } 56 getOSUBssid()57 public long getOSUBssid() { 58 return mOSUBssid; 59 } 60 setOSUBssid(long OSUBssid)61 public void setOSUBssid(long OSUBssid) { 62 mOSUBssid = OSUBssid; 63 } 64 getHESSID()65 public long getHESSID() { 66 return mHESSID; 67 } 68 getAnqpDomID()69 public int getAnqpDomID() { 70 return mAnqpDomID; 71 } 72 getAdvertisingSsid()73 public String getAdvertisingSsid() { 74 return mAdvertisingSSID; 75 } 76 getNameLocales()77 public Set<Locale> getNameLocales() { 78 Set<Locale> locales = new HashSet<>(mOSUProvider.getNames().size()); 79 for (I18Name name : mOSUProvider.getNames()) { 80 locales.add(name.getLocale()); 81 } 82 return locales; 83 } 84 getServiceLocales()85 public Set<Locale> getServiceLocales() { 86 Set<Locale> locales = new HashSet<>(mOSUProvider.getServiceDescriptions().size()); 87 for (I18Name name : mOSUProvider.getServiceDescriptions()) { 88 locales.add(name.getLocale()); 89 } 90 return locales; 91 } 92 getIconLanguages()93 public Set<String> getIconLanguages() { 94 Set<String> locales = new HashSet<>(mOSUProvider.getIcons().size()); 95 for (IconInfo iconInfo : mOSUProvider.getIcons()) { 96 locales.add(iconInfo.getLanguage()); 97 } 98 return locales; 99 } 100 getName(Locale locale)101 public String getName(Locale locale) { 102 List<ScoreEntry<String>> scoreList = new ArrayList<>(); 103 for (I18Name name : mOSUProvider.getNames()) { 104 if (locale == null || name.getLocale().equals(locale)) { 105 return name.getText(); 106 } 107 scoreList.add(new ScoreEntry<>(name.getText(), 108 languageScore(name.getLanguage(), locale))); 109 } 110 Collections.sort(scoreList); 111 return scoreList.isEmpty() ? null : scoreList.get(scoreList.size() - 1).getData(); 112 } 113 getServiceDescription(Locale locale)114 public String getServiceDescription(Locale locale) { 115 List<ScoreEntry<String>> scoreList = new ArrayList<>(); 116 for (I18Name service : mOSUProvider.getServiceDescriptions()) { 117 if (locale == null || service.getLocale().equals(locale)) { 118 return service.getText(); 119 } 120 scoreList.add(new ScoreEntry<>(service.getText(), 121 languageScore(service.getLanguage(), locale))); 122 } 123 Collections.sort(scoreList); 124 return scoreList.isEmpty() ? null : scoreList.get(scoreList.size() - 1).getData(); 125 } 126 getOsuID()127 public int getOsuID() { 128 return mOsuID; 129 } 130 setIconStatus(IconStatus iconStatus)131 public void setIconStatus(IconStatus iconStatus) { 132 synchronized (mOSUProvider) { 133 mIconStatus = iconStatus; 134 } 135 } 136 getIconStatus()137 public IconStatus getIconStatus() { 138 synchronized (mOSUProvider) { 139 return mIconStatus; 140 } 141 } 142 getIconFileElement()143 public HSIconFileElement getIconFileElement() { 144 synchronized (mOSUProvider) { 145 return mIconFileElement; 146 } 147 } 148 getIconInfo()149 public IconInfo getIconInfo() { 150 synchronized (mOSUProvider) { 151 return mIconInfo; 152 } 153 } 154 getIconFileName()155 public String getIconFileName() { 156 return mIconFileName; 157 } 158 setIconFileElement(HSIconFileElement iconFileElement, String fileName)159 public void setIconFileElement(HSIconFileElement iconFileElement, String fileName) { 160 synchronized (mOSUProvider) { 161 mIconFileElement = iconFileElement; 162 int index = 0; 163 for (IconInfo iconInfo : mOSUProvider.getIcons()) { 164 if (iconInfo.getFileName().equals(fileName)) { 165 mIconInfo = iconInfo; 166 mIconFileName = fileName; 167 mIconIndex = index; 168 break; 169 } 170 index++; 171 } 172 mIconStatus = IconStatus.Available; 173 } 174 } 175 176 private static class ScoreEntry<T> implements Comparable<ScoreEntry> { 177 private final T mData; 178 private final int mScore; 179 ScoreEntry(T data, int score)180 private ScoreEntry(T data, int score) { 181 mData = data; 182 mScore = score; 183 } 184 getData()185 public T getData() { 186 return mData; 187 } 188 189 @Override compareTo(ScoreEntry other)190 public int compareTo(ScoreEntry other) { 191 return Integer.compare(mScore, other.mScore); 192 } 193 194 @Override toString()195 public String toString() { 196 return String.format("%d for '%s'", mScore, mData); 197 } 198 } 199 getIconInfo(Locale locale, Set<String> types, int width, int height)200 public List<IconInfo> getIconInfo(Locale locale, Set<String> types, int width, int height) { 201 if (mOSUProvider.getIcons().isEmpty()) { 202 return null; 203 } 204 Log.d(OSUManager.TAG, "Matching icons against " + locale 205 + ", types " + types + ", " + width + "*" + height); 206 207 List<ScoreEntry<IconInfo>> matches = new ArrayList<>(); 208 for (IconInfo iconInfo : mOSUProvider.getIcons()) { 209 Log.d(OSUManager.TAG, "Checking icon " + iconInfo.toString()); 210 if (!types.contains(iconInfo.getIconType())) { 211 continue; 212 } 213 214 int score = languageScore(iconInfo.getLanguage(), locale); 215 int delta = iconInfo.getWidth() - width; 216 // Best size score is 1024 for a exact match, i.e. 2048 if both sides match 217 if (delta >= 0) { 218 score += (256 - delta) * 4; // Prefer down-scaling 219 } else { 220 score += 256 + delta; // Before up-scaling 221 } 222 delta = iconInfo.getHeight() - height; 223 if (delta >= 0) { 224 score += (256 - delta) * 4; 225 } else { 226 score += 256 + delta; 227 } 228 matches.add(new ScoreEntry<>(iconInfo, score)); 229 } 230 if (matches.isEmpty()) { 231 return Collections.emptyList(); 232 } 233 Collections.sort(matches); 234 List<IconInfo> icons = new ArrayList<>(matches.size()); 235 ListIterator<ScoreEntry<IconInfo>> matchIterator = matches.listIterator(matches.size()); 236 while (matchIterator.hasPrevious()) { 237 icons.add(matchIterator.previous().getData()); 238 } 239 return icons; 240 } 241 languageScore(String language, Locale locale)242 private static int languageScore(String language, Locale locale) { 243 if (language.length() == 3 && language.equalsIgnoreCase(locale.getISO3Language()) || 244 language.length() == 2 && language.equalsIgnoreCase(locale.getLanguage())) { 245 return 4096; 246 } else if (language.equalsIgnoreCase(GenericLocale)) { 247 return 3072; 248 } else if (language.equalsIgnoreCase("eng")) { 249 return 2048; 250 } else { 251 return 1024; 252 } 253 } 254 getBSSID()255 public long getBSSID() { 256 return mBSSID; 257 } 258 getOsuSsid()259 public String getOsuSsid() { 260 return mOSUProvider.getSSID(); 261 } 262 getOSUProvider()263 public OSUProvider getOSUProvider() { 264 return mOSUProvider; 265 } 266 267 @Override toString()268 public String toString() { 269 return String.format("OSU Info '%s' %012x -> %s, icon %s", 270 getOsuSsid(), mBSSID, getServiceDescription(null), mIconStatus); 271 } 272 OSUInfo(Parcel in)273 private OSUInfo(Parcel in) { 274 mBSSID = in.readLong(); 275 mHESSID = in.readLong(); 276 mAnqpDomID = in.readInt(); 277 mAdvertisingSSID = in.readString(); 278 mOsuID = in.readInt(); 279 mOSUBssid = in.readLong(); 280 mIconFileName = in.readString(); 281 mIconStatus = Utils.mapEnum(in.readInt(), IconStatus.class); 282 OSUProvider osuProvider; 283 try { 284 osuProvider = new OSUProvider(in); 285 } catch (IOException ioe) { 286 osuProvider = null; 287 } 288 mOSUProvider = osuProvider; 289 if (osuProvider == null) { 290 return; 291 } 292 mIconFileElement = new HSIconFileElement(in); 293 mIconIndex = in.readInt(); 294 mIconInfo = mIconIndex >= 0 && mIconIndex < mOSUProvider.getIcons().size() 295 ? mOSUProvider.getIcons().get(mIconIndex) : null; 296 } 297 298 public static final Parcelable.Creator<OSUInfo> CREATOR = new Parcelable.Creator<OSUInfo>() { 299 public OSUInfo createFromParcel(Parcel in) { 300 return new OSUInfo(in); 301 } 302 303 public OSUInfo[] newArray(int size) { 304 return new OSUInfo[size]; 305 } 306 }; 307 308 @Override 309 public int describeContents() { 310 return 0; 311 } 312 313 @Override 314 public void writeToParcel(Parcel dest, int flags) { 315 dest.writeLong(mBSSID); 316 dest.writeLong(mHESSID); 317 dest.writeInt(mAnqpDomID); 318 dest.writeString(mAdvertisingSSID); 319 dest.writeInt(mOsuID); 320 dest.writeLong(mOSUBssid); 321 dest.writeString(mIconFileName); 322 dest.writeInt(mIconStatus.ordinal()); 323 mOSUProvider.writeParcel(dest); 324 mIconFileElement.writeParcel(dest); 325 dest.writeInt(mIconIndex); 326 } 327 } 328