1package com.android.hotspot2.osu;
2
3import android.net.wifi.ScanResult;
4import android.util.Log;
5
6import com.android.anqp.HSIconFileElement;
7import com.android.anqp.I18Name;
8import com.android.anqp.IconInfo;
9import com.android.anqp.OSUProvider;
10import com.android.hotspot2.Utils;
11
12import java.util.ArrayList;
13import java.util.Collections;
14import java.util.HashSet;
15import java.util.List;
16import java.util.Locale;
17import java.util.Set;
18
19public 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