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