• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.android.hotspot2.osu;
2 
3 import android.content.ComponentName;
4 import android.content.Context;
5 import android.content.Intent;
6 import android.content.ServiceConnection;
7 import android.net.wifi.ScanResult;
8 import android.net.wifi.WifiConfiguration;
9 import android.net.wifi.WifiInfo;
10 import android.net.wifi.WifiManager;
11 import android.os.IBinder;
12 import android.os.RemoteException;
13 import android.util.Log;
14 
15 import com.android.anqp.HSIconFileElement;
16 import com.android.anqp.OSUProvider;
17 import com.android.hotspot2.AppBridge;
18 import com.android.hotspot2.PasspointMatch;
19 import com.android.hotspot2.Utils;
20 import com.android.hotspot2.app.OSUData;
21 import com.android.hotspot2.flow.FlowService;
22 import com.android.hotspot2.flow.OSUInfo;
23 import com.android.hotspot2.osu.service.RemediationHandler;
24 import com.android.hotspot2.flow.IFlowService;
25 
26 import java.io.File;
27 import java.net.MalformedURLException;
28 import java.util.ArrayList;
29 import java.util.Collection;
30 import java.util.HashMap;
31 import java.util.HashSet;
32 import java.util.List;
33 import java.util.Locale;
34 import java.util.Map;
35 import java.util.Set;
36 import java.util.concurrent.atomic.AtomicInteger;
37 
38 public class OSUManager {
39     public static final String TAG = "OSUMGR";
40     public static final boolean R2_MOCK = true;
41     private static final String REMEDIATION_FILE = "remediation.state";
42 
43     // Preferred icon parameters
44     public static final Locale LOCALE = java.util.Locale.getDefault();
45 
46     private final AppBridge mAppBridge;
47     private final Context mContext;
48     private final IconCache mIconCache;
49     private final RemediationHandler mRemediationHandler;
50     private final Set<String> mOSUSSIDs = new HashSet<>();
51     private final Map<OSUProvider, OSUInfo> mOSUMap = new HashMap<>();
52     private final AtomicInteger mOSUSequence = new AtomicInteger();
53 
54     private final OSUCache mOSUCache;
55 
OSUManager(Context context)56     public OSUManager(Context context) {
57         mContext = context;
58         mAppBridge = new AppBridge(context);
59         mIconCache = new IconCache(this);
60         File appFolder = context.getFilesDir();
61         mRemediationHandler =
62                 new RemediationHandler(context, new File(appFolder, REMEDIATION_FILE));
63         mOSUCache = new OSUCache();
64     }
65 
getContext()66     public Context getContext() {
67         return mContext;
68     }
69 
getAvailableOSUs()70     public List<OSUData> getAvailableOSUs() {
71         synchronized (mOSUMap) {
72             List<OSUData> completeOSUs = new ArrayList<>();
73             for (OSUInfo osuInfo : mOSUMap.values()) {
74                 if (osuInfo.getIconStatus() == OSUInfo.IconStatus.Available) {
75                     completeOSUs.add(new OSUData(osuInfo));
76                 }
77             }
78             return completeOSUs;
79         }
80     }
81 
setOSUSelection(int osuID)82     public void setOSUSelection(int osuID) {
83         OSUInfo selection = null;
84         for (OSUInfo osuInfo : mOSUMap.values()) {
85             if (osuInfo.getOsuID() == osuID &&
86                     osuInfo.getIconStatus() == OSUInfo.IconStatus.Available) {
87                 selection = osuInfo;
88                 break;
89             }
90         }
91 
92         Log.d(TAG, "Selected OSU ID " + osuID + ": " + selection);
93 
94         if (selection == null) {
95             return;
96         }
97 
98         final OSUInfo osu = selection;
99 
100         mContext.bindService(new Intent(mContext, FlowService.class), new ServiceConnection() {
101             @Override
102             public void onServiceConnected(ComponentName name, IBinder service) {
103                 try {
104                     IFlowService fs = IFlowService.Stub.asInterface(service);
105                     fs.provision(osu);
106                 } catch (RemoteException re) {
107                     Log.e(OSUManager.TAG, "Caught re: " + re);
108                 }
109             }
110 
111             @Override
112             public void onServiceDisconnected(ComponentName name) {
113                 Log.d(OSUManager.TAG, "Service disconnect: " + name);
114             }
115         }, Context.BIND_AUTO_CREATE);
116     }
117 
networkDeleted(final WifiConfiguration configuration)118     public void networkDeleted(final WifiConfiguration configuration) {
119         if (configuration.FQDN == null) {
120             return;
121         }
122 
123         mRemediationHandler.networkConfigChange();
124         mContext.bindService(new Intent(mContext, FlowService.class), new ServiceConnection() {
125             @Override
126             public void onServiceConnected(ComponentName name, IBinder service) {
127                 try {
128                     IFlowService fs = IFlowService.Stub.asInterface(service);
129                     fs.spDeleted(configuration.FQDN);
130                 } catch (RemoteException re) {
131                     Log.e(OSUManager.TAG, "Caught re: " + re);
132                 }
133             }
134 
135             @Override
136             public void onServiceDisconnected(ComponentName name) {
137 
138             }
139         }, Context.BIND_AUTO_CREATE);
140     }
141 
networkConnectChange(WifiInfo newNetwork)142     public void networkConnectChange(WifiInfo newNetwork) {
143         mRemediationHandler.newConnection(newNetwork);
144     }
145 
networkConfigChanged()146     public void networkConfigChanged() {
147         mRemediationHandler.networkConfigChange();
148     }
149 
wifiStateChange(boolean on)150     public void wifiStateChange(boolean on) {
151         if (on) {
152             return;
153         }
154 
155         // Notify the remediation handler that there are no WiFi networks available.
156         // Do NOT turn it off though as remediation, per at least this implementation, can take
157         // place over cellular. The subject of remediation over cellular (when restriction is
158         // "unrestricted") is not addresses by the WFA spec and direct ask to authors gives no
159         // distinct answer one way or the other.
160         mRemediationHandler.newConnection(null);
161         int current = mOSUMap.size();
162         mOSUMap.clear();
163         mOSUCache.clearAll();
164         mIconCache.tick(true);
165         if (current > 0) {
166             notifyOSUCount();
167         }
168     }
169 
isOSU(String ssid)170     public boolean isOSU(String ssid) {
171         synchronized (mOSUMap) {
172             return mOSUSSIDs.contains(ssid);
173         }
174     }
175 
pushScanResults(Collection<ScanResult> scanResults)176     public void pushScanResults(Collection<ScanResult> scanResults) {
177         Map<OSUProvider, ScanResult> results = mOSUCache.pushScanResults(scanResults);
178         if (results != null) {
179             updateOSUInfoCache(results);
180         }
181         mIconCache.tick(false);
182     }
183 
updateOSUInfoCache(Map<OSUProvider, ScanResult> results)184     private void updateOSUInfoCache(Map<OSUProvider, ScanResult> results) {
185         Map<OSUProvider, OSUInfo> osus = new HashMap<>();
186         for (Map.Entry<OSUProvider, ScanResult> entry : results.entrySet()) {
187             OSUInfo existing = mOSUMap.get(entry.getKey());
188             long bssid = Utils.parseMac(entry.getValue().BSSID);
189 
190             if (existing == null) {
191                 osus.put(entry.getKey(), new OSUInfo(entry.getValue(), entry.getKey(),
192                         mOSUSequence.getAndIncrement()));
193             } else if (existing.getBSSID() != bssid) {
194                 HSIconFileElement icon = mIconCache.getIcon(existing);
195                 if (icon != null && icon.equals(existing.getIconFileElement())) {
196                     OSUInfo osuInfo = new OSUInfo(entry.getValue(), entry.getKey(),
197                             existing.getOsuID());
198                     osuInfo.setIconFileElement(icon, existing.getIconFileName());
199                     osus.put(entry.getKey(), osuInfo);
200                 } else {
201                     osus.put(entry.getKey(), new OSUInfo(entry.getValue(),
202                             entry.getKey(), mOSUSequence.getAndIncrement()));
203                 }
204             } else {
205                 // Maintain existing entries.
206                 osus.put(entry.getKey(), existing);
207             }
208         }
209 
210         mOSUMap.clear();
211         mOSUMap.putAll(osus);
212 
213         mOSUSSIDs.clear();
214         for (OSUInfo osuInfo : mOSUMap.values()) {
215             mOSUSSIDs.add(osuInfo.getOsuSsid());
216         }
217 
218         int mods = mIconCache.resolveIcons(mOSUMap.values());
219 
220         if (mOSUMap.isEmpty() || mods > 0) {
221             notifyOSUCount();
222         }
223     }
224 
notifyIconReceived(long bssid, String fileName, byte[] data)225     public void notifyIconReceived(long bssid, String fileName, byte[] data) {
226         if (mIconCache.notifyIconReceived(bssid, fileName, data) > 0) {
227             notifyOSUCount();
228         }
229     }
230 
doIconQuery(long bssid, String fileName)231     public void doIconQuery(long bssid, String fileName) {
232         WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
233         wifiManager.queryPasspointIcon(bssid, fileName);
234     }
235 
notifyOSUCount()236     private void notifyOSUCount() {
237         int count = 0;
238         for (OSUInfo existing : mOSUMap.values()) {
239             if (existing.getIconStatus() == OSUInfo.IconStatus.Available) {
240                 count++;
241             }
242         }
243         Log.d(TAG, "Latest OSU info: " + count + " with icons, map " + mOSUMap);
244         mAppBridge.showOsuCount(count);
245     }
246 
deauth(long bssid, boolean ess, int delay, String url)247     public void deauth(long bssid, boolean ess, int delay, String url)
248             throws MalformedURLException {
249         Log.d(TAG, String.format("De-auth imminent on %s, delay %ss to '%s'",
250                 ess ? "ess" : "bss", delay, url));
251         // TODO: Missing framework functionality:
252         // mWifiNetworkAdapter.setHoldoffTime(delay * Constants.MILLIS_IN_A_SEC, ess);
253         String spName = mRemediationHandler.getCurrentSpName();
254         mAppBridge.showDeauth(spName, ess, delay, url);
255     }
256 
wnmRemediate(final long bssid, final String url, PasspointMatch match)257     public void wnmRemediate(final long bssid, final String url, PasspointMatch match) {
258         mRemediationHandler.wnmReceived(bssid, url);
259     }
260 
remediationDone(String fqdn, boolean policy)261     public void remediationDone(String fqdn, boolean policy) {
262         mRemediationHandler.remediationDone(fqdn, policy);
263     }
264 }
265