1 package com.android.hotspot2.osu; 2 3 import android.net.wifi.ScanResult; 4 import android.util.Log; 5 6 import com.android.anqp.HSIconFileElement; 7 import com.android.anqp.I18Name; 8 import com.android.anqp.IconInfo; 9 import com.android.anqp.OSUProvider; 10 import com.android.hotspot2.Utils; 11 12 import java.util.ArrayList; 13 import java.util.Collections; 14 import java.util.HashSet; 15 import java.util.List; 16 import java.util.Locale; 17 import java.util.Set; 18 19 public class OSUInfo { 20 public static final String GenericLocale = "zxx"; 21 22 public enum IconStatus { 23 NotQueried, // 24 InProgress, // Query pending 25 NotAvailable, // Deterministically unavailable 26 Available // Icon data retrieved 27 } 28 29 private final long mBSSID; 30 private final long mHESSID; 31 private final int mAnqpDomID; 32 private final String mSSID; 33 private final String mAdvertisingSSID; 34 private final OSUProvider mOSUProvider; 35 private final int mOsuID; 36 private long mOSUBssid; 37 private IconStatus mIconStatus = IconStatus.NotQueried; 38 private HSIconFileElement mIconFileElement; 39 private IconInfo mIconInfo; 40 41 public OSUInfo(ScanResult scanResult, String ssid, OSUProvider osuProvider, int osuID) { 42 mOsuID = osuID; 43 mBSSID = Utils.parseMac(scanResult.BSSID); 44 mHESSID = scanResult.hessid; 45 mAnqpDomID = scanResult.anqpDomainId; 46 mAdvertisingSSID = scanResult.SSID; 47 mSSID = ssid; 48 mOSUProvider = osuProvider; 49 } 50 51 public long getOSUBssid() { 52 return mOSUBssid; 53 } 54 55 public void setOSUBssid(long OSUBssid) { 56 mOSUBssid = OSUBssid; 57 } 58 59 public long getHESSID() { 60 return mHESSID; 61 } 62 63 public int getAnqpDomID() { 64 return mAnqpDomID; 65 } 66 67 public String getAdvertisingSSID() { 68 return mAdvertisingSSID; 69 } 70 71 public Set<Locale> getNameLocales() { 72 Set<Locale> locales = new HashSet<>(mOSUProvider.getNames().size()); 73 for (I18Name name : mOSUProvider.getNames()) { 74 locales.add(name.getLocale()); 75 } 76 return locales; 77 } 78 79 public Set<Locale> getServiceLocales() { 80 Set<Locale> locales = new HashSet<>(mOSUProvider.getServiceDescriptions().size()); 81 for (I18Name name : mOSUProvider.getServiceDescriptions()) { 82 locales.add(name.getLocale()); 83 } 84 return locales; 85 } 86 87 public Set<String> getIconLanguages() { 88 Set<String> locales = new HashSet<>(mOSUProvider.getIcons().size()); 89 for (IconInfo iconInfo : mOSUProvider.getIcons()) { 90 locales.add(iconInfo.getLanguage()); 91 } 92 return locales; 93 } 94 95 public String getName(Locale locale) { 96 List<ScoreEntry<String>> scoreList = new ArrayList<>(); 97 for (I18Name name : mOSUProvider.getNames()) { 98 if (locale == null || name.getLocale().equals(locale)) { 99 return name.getText(); 100 } 101 scoreList.add(new ScoreEntry<String>(name.getText(), 102 languageScore(name.getLanguage(), locale))); 103 } 104 Collections.sort(scoreList); 105 return scoreList.isEmpty() ? null : scoreList.iterator().next().getData(); 106 } 107 108 public String getServiceDescription(Locale locale) { 109 List<ScoreEntry<String>> scoreList = new ArrayList<>(); 110 for (I18Name service : mOSUProvider.getServiceDescriptions()) { 111 if (locale == null || service.getLocale().equals(locale)) { 112 return service.getText(); 113 } 114 scoreList.add(new ScoreEntry<>(service.getText(), 115 languageScore(service.getLanguage(), locale))); 116 } 117 Collections.sort(scoreList); 118 return scoreList.isEmpty() ? null : scoreList.iterator().next().getData(); 119 } 120 121 public int getOsuID() { 122 return mOsuID; 123 } 124 125 public void setIconStatus(IconStatus iconStatus) { 126 synchronized (mOSUProvider) { 127 mIconStatus = iconStatus; 128 } 129 } 130 131 public IconStatus getIconStatus() { 132 synchronized (mOSUProvider) { 133 return mIconStatus; 134 } 135 } 136 137 public HSIconFileElement getIconFileElement() { 138 synchronized (mOSUProvider) { 139 return mIconFileElement; 140 } 141 } 142 143 public IconInfo getIconInfo() { 144 synchronized (mOSUProvider) { 145 return mIconInfo; 146 } 147 } 148 149 public void setIconFileElement(HSIconFileElement iconFileElement, String fileName) { 150 synchronized (mOSUProvider) { 151 mIconFileElement = iconFileElement; 152 for (IconInfo iconInfo : mOSUProvider.getIcons()) { 153 if (iconInfo.getFileName().equals(fileName)) { 154 mIconInfo = iconInfo; 155 break; 156 } 157 } 158 mIconStatus = IconStatus.Available; 159 } 160 } 161 162 private static class ScoreEntry<T> implements Comparable<ScoreEntry> { 163 private final T mData; 164 private final int mScore; 165 166 private ScoreEntry(T data, int score) { 167 mData = data; 168 mScore = score; 169 } 170 171 public T getData() { 172 return mData; 173 } 174 175 @Override 176 public int compareTo(ScoreEntry other) { 177 return Integer.compare(mScore, other.mScore); 178 } 179 } 180 181 public List<IconInfo> getIconInfo(Locale locale, Set<String> types, int width, int height) { 182 if (mOSUProvider.getIcons().isEmpty()) { 183 return null; 184 } 185 Log.d(OSUManager.TAG, "Matching icons against " + locale 186 + ", types " + types + ", " + width + "*" + height); 187 188 List<ScoreEntry<IconInfo>> matches = new ArrayList<>(); 189 for (IconInfo iconInfo : mOSUProvider.getIcons()) { 190 Log.d(OSUManager.TAG, "Checking icon " + iconInfo.toString()); 191 if (!types.contains(iconInfo.getIconType())) { 192 continue; 193 } 194 195 int score = languageScore(iconInfo.getLanguage(), locale); 196 int delta = iconInfo.getWidth() - width; 197 // Best size score is 1024 for a exact match, i.e. 2048 if both sides match 198 if (delta >= 0) { 199 score += (256 - delta) * 4; // Prefer down-scaling 200 } else { 201 score += 256 + delta; // Before up-scaling 202 } 203 delta = iconInfo.getHeight() - height; 204 if (delta >= 0) { 205 score += (256 - delta) * 4; 206 } else { 207 score += 256 + delta; 208 } 209 matches.add(new ScoreEntry<>(iconInfo, score)); 210 } 211 if (matches.isEmpty()) { 212 return Collections.emptyList(); 213 } 214 Collections.sort(matches); 215 List<IconInfo> icons = new ArrayList<>(matches.size()); 216 for (ScoreEntry<IconInfo> scoredIcon : matches) { 217 icons.add(scoredIcon.getData()); 218 } 219 return icons; 220 } 221 222 private static int languageScore(String language, Locale locale) { 223 if (language.length() == 3 && language.equalsIgnoreCase(locale.getISO3Language()) || 224 language.length() == 2 && language.equalsIgnoreCase(locale.getLanguage())) { 225 return 4096; 226 } else if (language.equalsIgnoreCase(GenericLocale)) { 227 return 3072; 228 } else if (language.equalsIgnoreCase("eng")) { 229 return 2048; 230 } else { 231 return 1024; 232 } 233 } 234 235 public long getBSSID() { 236 return mBSSID; 237 } 238 239 public String getSSID() { 240 return mSSID; 241 } 242 243 public OSUProvider getOSUProvider() { 244 return mOSUProvider; 245 } 246 247 @Override 248 public String toString() { 249 return String.format("OSU Info '%s' %012x -> %s, icon %s", 250 mSSID, mBSSID, getServiceDescription(null), mIconStatus); 251 } 252 } 253