WificondControl.java revision 85c806c0d32bb30f421ebc372a59b2f3ea2dce41
1/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.wifi;
18
19import android.net.wifi.IApInterface;
20import android.net.wifi.IClientInterface;
21import android.net.wifi.IPnoScanEvent;
22import android.net.wifi.IScanEvent;
23import android.net.wifi.IWifiScannerImpl;
24import android.net.wifi.IWificond;
25import android.net.wifi.ScanResult;
26import android.net.wifi.WifiScanner;
27import android.net.wifi.WifiSsid;
28import android.os.Binder;
29import android.os.RemoteException;
30import android.util.Log;
31
32import com.android.server.wifi.hotspot2.NetworkDetail;
33import com.android.server.wifi.util.InformationElementUtil;
34import com.android.server.wifi.util.NativeUtil;
35import com.android.server.wifi.util.ScanResultUtil;
36import com.android.server.wifi.wificond.ChannelSettings;
37import com.android.server.wifi.wificond.HiddenNetwork;
38import com.android.server.wifi.wificond.NativeScanResult;
39import com.android.server.wifi.wificond.PnoNetwork;
40import com.android.server.wifi.wificond.PnoSettings;
41import com.android.server.wifi.wificond.SingleScanSettings;
42
43import java.util.ArrayList;
44import java.util.Set;
45
46/**
47 * This class provides methods for WifiNative to send control commands to wificond.
48 * NOTE: This class should only be used from WifiNative.
49 */
50public class WificondControl {
51    private boolean mVerboseLoggingEnabled = false;
52
53    private static final String TAG = "WificondControl";
54    private WifiInjector mWifiInjector;
55    private WifiMonitor mWifiMonitor;
56    private final CarrierNetworkConfig mCarrierNetworkConfig;
57
58    // Cached wificond binder handlers.
59    private IWificond mWificond;
60    private IClientInterface mClientInterface;
61    private IApInterface mApInterface;
62    private IWifiScannerImpl mWificondScanner;
63    private IScanEvent mScanEventHandler;
64    private IPnoScanEvent mPnoScanEventHandler;
65
66    private String mClientInterfaceName;
67
68
69    private class ScanEventHandler extends IScanEvent.Stub {
70        @Override
71        public void OnScanResultReady() {
72            Log.d(TAG, "Scan result ready event");
73            mWifiMonitor.broadcastScanResultEvent(mClientInterfaceName);
74        }
75
76        @Override
77        public void OnScanFailed() {
78            Log.d(TAG, "Scan failed event");
79            mWifiMonitor.broadcastScanFailedEvent(mClientInterfaceName);
80        }
81    }
82
83    WificondControl(WifiInjector wifiInjector, WifiMonitor wifiMonitor,
84            CarrierNetworkConfig carrierNetworkConfig) {
85        mWifiInjector = wifiInjector;
86        mWifiMonitor = wifiMonitor;
87        mCarrierNetworkConfig = carrierNetworkConfig;
88    }
89
90    private class PnoScanEventHandler extends IPnoScanEvent.Stub {
91        @Override
92        public void OnPnoNetworkFound() {
93            Log.d(TAG, "Pno scan result event");
94            mWifiMonitor.broadcastPnoScanResultEvent(mClientInterfaceName);
95        }
96
97        @Override
98        public void OnPnoScanFailed() {
99            Log.d(TAG, "Pno Scan failed event");
100            // Nothing to do for now.
101        }
102
103        @Override
104        public void OnPnoScanOverOffloadStarted() {
105            Log.d(TAG, "Pno scan over offload started");
106            // Update metrics
107        }
108
109        @Override
110        public void OnPnoScanOverOffloadFailed(int reason) {
111            Log.d(TAG, "Pno scan over offload failed");
112            // Update metrics
113        }
114    }
115
116    /** Enable or disable verbose logging of WificondControl.
117     *  @param enable True to enable verbose logging. False to disable verbose logging.
118     */
119    public void enableVerboseLogging(boolean enable) {
120        mVerboseLoggingEnabled = enable;
121    }
122
123    /**
124    * Setup driver for client mode via wificond.
125    * @return An IClientInterface as wificond client interface binder handler.
126    * Returns null on failure.
127    */
128    public IClientInterface setupDriverForClientMode() {
129        Log.d(TAG, "Setting up driver for client mode");
130        mWificond = mWifiInjector.makeWificond();
131        if (mWificond == null) {
132            Log.e(TAG, "Failed to get reference to wificond");
133            return null;
134        }
135
136        IClientInterface clientInterface = null;
137        try {
138            clientInterface = mWificond.createClientInterface();
139        } catch (RemoteException e1) {
140            Log.e(TAG, "Failed to get IClientInterface due to remote exception");
141            return null;
142        }
143
144        if (clientInterface == null) {
145            Log.e(TAG, "Could not get IClientInterface instance from wificond");
146            return null;
147        }
148        Binder.allowBlocking(clientInterface.asBinder());
149
150        // Refresh Handlers
151        mClientInterface = clientInterface;
152        try {
153            mClientInterfaceName = clientInterface.getInterfaceName();
154            mWificondScanner = mClientInterface.getWifiScannerImpl();
155            if (mWificondScanner == null) {
156                Log.e(TAG, "Failed to get WificondScannerImpl");
157                return null;
158            }
159            Binder.allowBlocking(mWificondScanner.asBinder());
160            mScanEventHandler = new ScanEventHandler();
161            mWificondScanner.subscribeScanEvents(mScanEventHandler);
162            mPnoScanEventHandler = new PnoScanEventHandler();
163            mWificondScanner.subscribePnoScanEvents(mPnoScanEventHandler);
164        } catch (RemoteException e) {
165            Log.e(TAG, "Failed to refresh wificond scanner due to remote exception");
166        }
167
168        return clientInterface;
169    }
170
171    /**
172    * Setup driver for softAp mode via wificond.
173    * @return An IApInterface as wificond Ap interface binder handler.
174    * Returns null on failure.
175    */
176    public IApInterface setupDriverForSoftApMode() {
177        Log.d(TAG, "Setting up driver for soft ap mode");
178        mWificond = mWifiInjector.makeWificond();
179        if (mWificond == null) {
180            Log.e(TAG, "Failed to get reference to wificond");
181            return null;
182        }
183
184        IApInterface apInterface = null;
185        try {
186            apInterface = mWificond.createApInterface();
187        } catch (RemoteException e1) {
188            Log.e(TAG, "Failed to get IApInterface due to remote exception");
189            return null;
190        }
191
192        if (apInterface == null) {
193            Log.e(TAG, "Could not get IApInterface instance from wificond");
194            return null;
195        }
196        Binder.allowBlocking(apInterface.asBinder());
197
198        // Refresh Handlers
199        mApInterface = apInterface;
200
201        return apInterface;
202    }
203
204    /**
205    * Teardown all interfaces configured in wificond.
206    * @return Returns true on success.
207    */
208    public boolean tearDownInterfaces() {
209        Log.d(TAG, "tearing down interfaces in wificond");
210        // Explicitly refresh the wificodn handler because |tearDownInterfaces()|
211        // could be used to cleanup before we setup any interfaces.
212        mWificond = mWifiInjector.makeWificond();
213        if (mWificond == null) {
214            Log.e(TAG, "Failed to get reference to wificond");
215            return false;
216        }
217
218        try {
219            if (mWificondScanner != null) {
220                mWificondScanner.unsubscribeScanEvents();
221                mWificondScanner.unsubscribePnoScanEvents();
222            }
223            mWificond.tearDownInterfaces();
224
225            // Refresh handlers
226            mClientInterface = null;
227            mWificondScanner = null;
228            mPnoScanEventHandler = null;
229            mScanEventHandler = null;
230            mApInterface = null;
231
232            return true;
233        } catch (RemoteException e) {
234            Log.e(TAG, "Failed to tear down interfaces due to remote exception");
235        }
236
237        return false;
238    }
239
240    /**
241    * Disable wpa_supplicant via wificond.
242    * @return Returns true on success.
243    */
244    public boolean disableSupplicant() {
245        if (mClientInterface == null) {
246            Log.e(TAG, "No valid wificond client interface handler");
247            return false;
248        }
249        try {
250            return mClientInterface.disableSupplicant();
251        } catch (RemoteException e) {
252            Log.e(TAG, "Failed to disable supplicant due to remote exception");
253        }
254        return false;
255    }
256
257    /**
258    * Enable wpa_supplicant via wificond.
259    * @return Returns true on success.
260    */
261    public boolean enableSupplicant() {
262        if (mClientInterface == null) {
263            Log.e(TAG, "No valid wificond client interface handler");
264            return false;
265        }
266
267        try {
268            return mClientInterface.enableSupplicant();
269        } catch (RemoteException e) {
270            Log.e(TAG, "Failed to enable supplicant due to remote exception");
271        }
272        return false;
273    }
274
275    /**
276    * Request signal polling to wificond.
277    * Returns an SignalPollResult object.
278    * Returns null on failure.
279    */
280    public WifiNative.SignalPollResult signalPoll() {
281        if (mClientInterface == null) {
282            Log.e(TAG, "No valid wificond client interface handler");
283            return null;
284        }
285
286        int[] resultArray;
287        try {
288            resultArray = mClientInterface.signalPoll();
289            if (resultArray == null || resultArray.length != 3) {
290                Log.e(TAG, "Invalid signal poll result from wificond");
291                return null;
292            }
293        } catch (RemoteException e) {
294            Log.e(TAG, "Failed to do signal polling due to remote exception");
295            return null;
296        }
297        WifiNative.SignalPollResult pollResult = new WifiNative.SignalPollResult();
298        pollResult.currentRssi = resultArray[0];
299        pollResult.txBitrate = resultArray[1];
300        pollResult.associationFrequency = resultArray[2];
301        return pollResult;
302    }
303
304    /**
305    * Fetch TX packet counters on current connection from wificond.
306    * Returns an TxPacketCounters object.
307    * Returns null on failure.
308    */
309    public WifiNative.TxPacketCounters getTxPacketCounters() {
310        if (mClientInterface == null) {
311            Log.e(TAG, "No valid wificond client interface handler");
312            return null;
313        }
314
315        int[] resultArray;
316        try {
317            resultArray = mClientInterface.getPacketCounters();
318            if (resultArray == null || resultArray.length != 2) {
319                Log.e(TAG, "Invalid signal poll result from wificond");
320                return null;
321            }
322        } catch (RemoteException e) {
323            Log.e(TAG, "Failed to do signal polling due to remote exception");
324            return null;
325        }
326        WifiNative.TxPacketCounters counters = new WifiNative.TxPacketCounters();
327        counters.txSucceeded = resultArray[0];
328        counters.txFailed = resultArray[1];
329        return counters;
330    }
331
332    /**
333    * Fetch the latest scan result from kernel via wificond.
334    * @return Returns an ArrayList of ScanDetail.
335    * Returns an empty ArrayList on failure.
336    */
337    public ArrayList<ScanDetail> getScanResults() {
338        ArrayList<ScanDetail> results = new ArrayList<>();
339        if (mWificondScanner == null) {
340            Log.e(TAG, "No valid wificond scanner interface handler");
341            return results;
342        }
343        try {
344            NativeScanResult[] nativeResults = mWificondScanner.getScanResults();
345            for (NativeScanResult result : nativeResults) {
346                WifiSsid wifiSsid = WifiSsid.createFromByteArray(result.ssid);
347                String bssid;
348                try {
349                    bssid = NativeUtil.macAddressFromByteArray(result.bssid);
350                } catch (IllegalArgumentException e) {
351                    Log.e(TAG, "Illegal argument " + result.bssid, e);
352                    continue;
353                }
354                if (bssid == null) {
355                    Log.e(TAG, "Illegal null bssid");
356                    continue;
357                }
358                ScanResult.InformationElement[] ies =
359                        InformationElementUtil.parseInformationElements(result.infoElement);
360                InformationElementUtil.Capabilities capabilities =
361                        new InformationElementUtil.Capabilities();
362                capabilities.from(ies, result.capability);
363                String flags = capabilities.generateCapabilitiesString();
364                NetworkDetail networkDetail =
365                        new NetworkDetail(bssid, ies, null, result.frequency);
366
367                ScanDetail scanDetail = new ScanDetail(networkDetail, wifiSsid, bssid, flags,
368                        result.signalMbm / 100, result.frequency, result.tsf, ies, null);
369                // Update carrier network info if this AP's SSID is associated with a carrier Wi-Fi
370                // network and it uses EAP.
371                if (ScanResultUtil.isScanResultForEapNetwork(scanDetail.getScanResult())
372                        && mCarrierNetworkConfig.isCarrierNetwork(wifiSsid.toString())) {
373                    scanDetail.getScanResult().isCarrierAp = true;
374                    scanDetail.getScanResult().carrierApEapType =
375                            mCarrierNetworkConfig.getNetworkEapType(wifiSsid.toString());
376                    scanDetail.getScanResult().carrierName =
377                            mCarrierNetworkConfig.getCarrierName(wifiSsid.toString());
378                }
379                results.add(scanDetail);
380            }
381        } catch (RemoteException e1) {
382            Log.e(TAG, "Failed to create ScanDetail ArrayList");
383        }
384        if (mVerboseLoggingEnabled) {
385            Log.d(TAG, "get " + results.size() + " scan results from wificond");
386        }
387
388        return results;
389    }
390
391    /**
392     * Start a scan using wificond for the given parameters.
393     * @param freqs list of frequencies to scan for, if null scan all supported channels.
394     * @param hiddenNetworkSSIDs List of hidden networks to be scanned for.
395     * @return Returns true on success.
396     */
397    public boolean scan(Set<Integer> freqs, Set<String> hiddenNetworkSSIDs) {
398        if (mWificondScanner == null) {
399            Log.e(TAG, "No valid wificond scanner interface handler");
400            return false;
401        }
402        SingleScanSettings settings = new SingleScanSettings();
403        settings.channelSettings  = new ArrayList<>();
404        settings.hiddenNetworks  = new ArrayList<>();
405
406        if (freqs != null) {
407            for (Integer freq : freqs) {
408                ChannelSettings channel = new ChannelSettings();
409                channel.frequency = freq;
410                settings.channelSettings.add(channel);
411            }
412        }
413        if (hiddenNetworkSSIDs != null) {
414            for (String ssid : hiddenNetworkSSIDs) {
415                HiddenNetwork network = new HiddenNetwork();
416                try {
417                    network.ssid = NativeUtil.byteArrayFromArrayList(NativeUtil.decodeSsid(ssid));
418                } catch (IllegalArgumentException e) {
419                    Log.e(TAG, "Illegal argument " + ssid, e);
420                    continue;
421                }
422                settings.hiddenNetworks.add(network);
423            }
424        }
425
426        try {
427            return mWificondScanner.scan(settings);
428        } catch (RemoteException e1) {
429            Log.e(TAG, "Failed to request scan due to remote exception");
430        }
431        return false;
432    }
433
434    /**
435     * Start PNO scan.
436     * @param pnoSettings Pno scan configuration.
437     * @return true on success.
438     */
439    public boolean startPnoScan(WifiNative.PnoSettings pnoSettings) {
440        if (mWificondScanner == null) {
441            Log.e(TAG, "No valid wificond scanner interface handler");
442            return false;
443        }
444        PnoSettings settings = new PnoSettings();
445        settings.pnoNetworks  = new ArrayList<>();
446        settings.intervalMs = pnoSettings.periodInMs;
447        settings.min2gRssi = pnoSettings.min24GHzRssi;
448        settings.min5gRssi = pnoSettings.min5GHzRssi;
449        if (pnoSettings.networkList != null) {
450            for (WifiNative.PnoNetwork network : pnoSettings.networkList) {
451                PnoNetwork condNetwork = new PnoNetwork();
452                condNetwork.isHidden = (network.flags
453                        & WifiScanner.PnoSettings.PnoNetwork.FLAG_DIRECTED_SCAN) != 0;
454                try {
455                    condNetwork.ssid =
456                            NativeUtil.byteArrayFromArrayList(NativeUtil.decodeSsid(network.ssid));
457                } catch (IllegalArgumentException e) {
458                    Log.e(TAG, "Illegal argument " + network.ssid, e);
459                    continue;
460                }
461                settings.pnoNetworks.add(condNetwork);
462            }
463        }
464
465        try {
466            return mWificondScanner.startPnoScan(settings);
467        } catch (RemoteException e1) {
468            Log.e(TAG, "Failed to stop pno scan due to remote exception");
469        }
470        return false;
471    }
472
473    /**
474     * Stop PNO scan.
475     * @return true on success.
476     */
477    public boolean stopPnoScan() {
478        if (mWificondScanner == null) {
479            Log.e(TAG, "No valid wificond scanner interface handler");
480            return false;
481        }
482        try {
483            return mWificondScanner.stopPnoScan();
484        } catch (RemoteException e1) {
485            Log.e(TAG, "Failed to stop pno scan due to remote exception");
486        }
487        return false;
488    }
489
490    /**
491     * Abort ongoing single scan.
492     */
493    public void abortScan() {
494        if (mWificondScanner == null) {
495            Log.e(TAG, "No valid wificond scanner interface handler");
496            return;
497        }
498        try {
499            mWificondScanner.abortScan();
500        } catch (RemoteException e1) {
501            Log.e(TAG, "Failed to request abortScan due to remote exception");
502        }
503    }
504
505}
506