WificondControl.java revision a2dae3436431f3fe2ac79ad087fe62f6fcda8ad7
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                        new NetworkDetail(bssid, ies, null, result.frequency);
379
380                ScanDetail scanDetail = new ScanDetail(networkDetail, wifiSsid, bssid, flags,
381                        result.signalMbm / 100, result.frequency, result.tsf, ies, null);
382                // Update carrier network info if this AP's SSID is associated with a carrier Wi-Fi
383                // network and it uses EAP.
384                if (ScanResultUtil.isScanResultForEapNetwork(scanDetail.getScanResult())
385                        && mCarrierNetworkConfig.isCarrierNetwork(wifiSsid.toString())) {
386                    scanDetail.getScanResult().isCarrierAp = true;
387                    scanDetail.getScanResult().carrierApEapType =
388                            mCarrierNetworkConfig.getNetworkEapType(wifiSsid.toString());
389                    scanDetail.getScanResult().carrierName =
390                            mCarrierNetworkConfig.getCarrierName(wifiSsid.toString());
391                }
392                results.add(scanDetail);
393            }
394        } catch (RemoteException e1) {
395            Log.e(TAG, "Failed to create ScanDetail ArrayList");
396        }
397        if (mVerboseLoggingEnabled) {
398            Log.d(TAG, "get " + results.size() + " scan results from wificond");
399        }
400
401        return results;
402    }
403
404    /**
405     * Start a scan using wificond for the given parameters.
406     * @param freqs list of frequencies to scan for, if null scan all supported channels.
407     * @param hiddenNetworkSSIDs List of hidden networks to be scanned for.
408     * @return Returns true on success.
409     */
410    public boolean scan(Set<Integer> freqs, Set<String> hiddenNetworkSSIDs) {
411        if (mWificondScanner == null) {
412            Log.e(TAG, "No valid wificond scanner interface handler");
413            return false;
414        }
415        SingleScanSettings settings = new SingleScanSettings();
416        settings.channelSettings  = new ArrayList<>();
417        settings.hiddenNetworks  = new ArrayList<>();
418
419        if (freqs != null) {
420            for (Integer freq : freqs) {
421                ChannelSettings channel = new ChannelSettings();
422                channel.frequency = freq;
423                settings.channelSettings.add(channel);
424            }
425        }
426        if (hiddenNetworkSSIDs != null) {
427            for (String ssid : hiddenNetworkSSIDs) {
428                HiddenNetwork network = new HiddenNetwork();
429                try {
430                    network.ssid = NativeUtil.byteArrayFromArrayList(NativeUtil.decodeSsid(ssid));
431                } catch (IllegalArgumentException e) {
432                    Log.e(TAG, "Illegal argument " + ssid, e);
433                    continue;
434                }
435                settings.hiddenNetworks.add(network);
436            }
437        }
438
439        try {
440            return mWificondScanner.scan(settings);
441        } catch (RemoteException e1) {
442            Log.e(TAG, "Failed to request scan due to remote exception");
443        }
444        return false;
445    }
446
447    /**
448     * Start PNO scan.
449     * @param pnoSettings Pno scan configuration.
450     * @return true on success.
451     */
452    public boolean startPnoScan(WifiNative.PnoSettings pnoSettings) {
453        if (mWificondScanner == null) {
454            Log.e(TAG, "No valid wificond scanner interface handler");
455            return false;
456        }
457        PnoSettings settings = new PnoSettings();
458        settings.pnoNetworks  = new ArrayList<>();
459        settings.intervalMs = pnoSettings.periodInMs;
460        settings.min2gRssi = pnoSettings.min24GHzRssi;
461        settings.min5gRssi = pnoSettings.min5GHzRssi;
462        if (pnoSettings.networkList != null) {
463            for (WifiNative.PnoNetwork network : pnoSettings.networkList) {
464                PnoNetwork condNetwork = new PnoNetwork();
465                condNetwork.isHidden = (network.flags
466                        & WifiScanner.PnoSettings.PnoNetwork.FLAG_DIRECTED_SCAN) != 0;
467                try {
468                    condNetwork.ssid =
469                            NativeUtil.byteArrayFromArrayList(NativeUtil.decodeSsid(network.ssid));
470                } catch (IllegalArgumentException e) {
471                    Log.e(TAG, "Illegal argument " + network.ssid, e);
472                    continue;
473                }
474                settings.pnoNetworks.add(condNetwork);
475            }
476        }
477
478        try {
479            boolean success = mWificondScanner.startPnoScan(settings);
480            mWifiInjector.getWifiMetrics().incrementPnoScanStartAttempCount();
481            if (!success) {
482                mWifiInjector.getWifiMetrics().incrementPnoScanFailedCount();
483            }
484            return success;
485        } catch (RemoteException e1) {
486            Log.e(TAG, "Failed to start pno scan due to remote exception");
487        }
488        return false;
489    }
490
491    /**
492     * Stop PNO scan.
493     * @return true on success.
494     */
495    public boolean stopPnoScan() {
496        if (mWificondScanner == null) {
497            Log.e(TAG, "No valid wificond scanner interface handler");
498            return false;
499        }
500        try {
501            return mWificondScanner.stopPnoScan();
502        } catch (RemoteException e1) {
503            Log.e(TAG, "Failed to stop pno scan due to remote exception");
504        }
505        return false;
506    }
507
508    /**
509     * Abort ongoing single scan.
510     */
511    public void abortScan() {
512        if (mWificondScanner == null) {
513            Log.e(TAG, "No valid wificond scanner interface handler");
514            return;
515        }
516        try {
517            mWificondScanner.abortScan();
518        } catch (RemoteException e1) {
519            Log.e(TAG, "Failed to request abortScan due to remote exception");
520        }
521    }
522
523}
524