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.annotation.NonNull;
20import android.net.MacAddress;
21import android.net.wifi.IApInterface;
22import android.net.wifi.IApInterfaceEventCallback;
23import android.net.wifi.IClientInterface;
24import android.net.wifi.IPnoScanEvent;
25import android.net.wifi.IScanEvent;
26import android.net.wifi.IWifiScannerImpl;
27import android.net.wifi.IWificond;
28import android.net.wifi.ScanResult;
29import android.net.wifi.WifiScanner;
30import android.net.wifi.WifiSsid;
31import android.os.Binder;
32import android.os.IBinder;
33import android.os.RemoteException;
34import android.util.Log;
35
36import com.android.server.wifi.WifiNative.SoftApListener;
37import com.android.server.wifi.hotspot2.NetworkDetail;
38import com.android.server.wifi.util.InformationElementUtil;
39import com.android.server.wifi.util.NativeUtil;
40import com.android.server.wifi.util.ScanResultUtil;
41import com.android.server.wifi.wificond.ChannelSettings;
42import com.android.server.wifi.wificond.HiddenNetwork;
43import com.android.server.wifi.wificond.NativeScanResult;
44import com.android.server.wifi.wificond.PnoNetwork;
45import com.android.server.wifi.wificond.PnoSettings;
46import com.android.server.wifi.wificond.RadioChainInfo;
47import com.android.server.wifi.wificond.SingleScanSettings;
48
49import java.util.ArrayList;
50import java.util.HashMap;
51import java.util.Map;
52import java.util.Set;
53
54/**
55 * This class provides methods for WifiNative to send control commands to wificond.
56 * NOTE: This class should only be used from WifiNative.
57 */
58public class WificondControl implements IBinder.DeathRecipient {
59    private boolean mVerboseLoggingEnabled = false;
60
61    private static final String TAG = "WificondControl";
62
63    /* Get scan results for a single scan */
64    public static final int SCAN_TYPE_SINGLE_SCAN = 0;
65
66    /* Get scan results for Pno Scan */
67    public static final int SCAN_TYPE_PNO_SCAN = 1;
68
69    private WifiInjector mWifiInjector;
70    private WifiMonitor mWifiMonitor;
71    private final CarrierNetworkConfig mCarrierNetworkConfig;
72
73    // Cached wificond binder handlers.
74    private IWificond mWificond;
75    private HashMap<String, IClientInterface> mClientInterfaces = new HashMap<>();
76    private HashMap<String, IApInterface> mApInterfaces = new HashMap<>();
77    private HashMap<String, IWifiScannerImpl> mWificondScanners = new HashMap<>();
78    private HashMap<String, IScanEvent> mScanEventHandlers = new HashMap<>();
79    private HashMap<String, IPnoScanEvent> mPnoScanEventHandlers = new HashMap<>();
80    private HashMap<String, IApInterfaceEventCallback> mApInterfaceListeners = new HashMap<>();
81    private WifiNative.WificondDeathEventHandler mDeathEventHandler;
82
83    private class ScanEventHandler extends IScanEvent.Stub {
84        private String mIfaceName;
85
86        ScanEventHandler(@NonNull String ifaceName) {
87            mIfaceName = ifaceName;
88        }
89
90        @Override
91        public void OnScanResultReady() {
92            Log.d(TAG, "Scan result ready event");
93            mWifiMonitor.broadcastScanResultEvent(mIfaceName);
94        }
95
96        @Override
97        public void OnScanFailed() {
98            Log.d(TAG, "Scan failed event");
99            mWifiMonitor.broadcastScanFailedEvent(mIfaceName);
100        }
101    }
102
103    WificondControl(WifiInjector wifiInjector, WifiMonitor wifiMonitor,
104            CarrierNetworkConfig carrierNetworkConfig) {
105        mWifiInjector = wifiInjector;
106        mWifiMonitor = wifiMonitor;
107        mCarrierNetworkConfig = carrierNetworkConfig;
108    }
109
110    private class PnoScanEventHandler extends IPnoScanEvent.Stub {
111        private String mIfaceName;
112
113        PnoScanEventHandler(@NonNull String ifaceName) {
114            mIfaceName = ifaceName;
115        }
116
117        @Override
118        public void OnPnoNetworkFound() {
119            Log.d(TAG, "Pno scan result event");
120            mWifiMonitor.broadcastPnoScanResultEvent(mIfaceName);
121            mWifiInjector.getWifiMetrics().incrementPnoFoundNetworkEventCount();
122        }
123
124        @Override
125        public void OnPnoScanFailed() {
126            Log.d(TAG, "Pno Scan failed event");
127            mWifiInjector.getWifiMetrics().incrementPnoScanFailedCount();
128        }
129
130        @Override
131        public void OnPnoScanOverOffloadStarted() {
132            Log.d(TAG, "Pno scan over offload started");
133            mWifiInjector.getWifiMetrics().incrementPnoScanStartedOverOffloadCount();
134        }
135
136        @Override
137        public void OnPnoScanOverOffloadFailed(int reason) {
138            Log.d(TAG, "Pno scan over offload failed");
139            mWifiInjector.getWifiMetrics().incrementPnoScanFailedOverOffloadCount();
140        }
141    }
142
143    /**
144     * Listener for AP Interface events.
145     */
146    private class ApInterfaceEventCallback extends IApInterfaceEventCallback.Stub {
147        private SoftApListener mSoftApListener;
148
149        ApInterfaceEventCallback(SoftApListener listener) {
150            mSoftApListener = listener;
151        }
152
153        @Override
154        public void onNumAssociatedStationsChanged(int numStations) {
155            mSoftApListener.onNumAssociatedStationsChanged(numStations);
156        }
157
158        @Override
159        public void onSoftApChannelSwitched(int frequency, int bandwidth) {
160            mSoftApListener.onSoftApChannelSwitched(frequency, bandwidth);
161        }
162    }
163
164    /**
165     * Called by the binder subsystem upon remote object death.
166     * Invoke all the register death handlers and clear state.
167     */
168    @Override
169    public void binderDied() {
170        Log.e(TAG, "Wificond died!");
171        clearState();
172        // Invalidate the global wificond handle on death. Will be refreshed
173        // on the next setup call.
174        mWificond = null;
175        if (mDeathEventHandler != null) {
176            mDeathEventHandler.onDeath();
177        }
178    }
179
180    /** Enable or disable verbose logging of WificondControl.
181     *  @param enable True to enable verbose logging. False to disable verbose logging.
182     */
183    public void enableVerboseLogging(boolean enable) {
184        mVerboseLoggingEnabled = enable;
185    }
186
187    /**
188     * Initializes wificond & registers a death notification for wificond.
189     * This method clears any existing state in wificond daemon.
190     *
191     * @return Returns true on success.
192     */
193    public boolean initialize(@NonNull WifiNative.WificondDeathEventHandler handler) {
194        if (mDeathEventHandler != null) {
195            Log.e(TAG, "Death handler already present");
196        }
197        mDeathEventHandler = handler;
198        tearDownInterfaces();
199        return true;
200    }
201
202    /**
203     * Helper method to retrieve the global wificond handle and register for
204     * death notifications.
205     */
206    private boolean retrieveWificondAndRegisterForDeath() {
207        if (mWificond != null) {
208            if (mVerboseLoggingEnabled) {
209                Log.d(TAG, "Wificond handle already retrieved");
210            }
211            // We already have a wificond handle.
212            return true;
213        }
214        mWificond = mWifiInjector.makeWificond();
215        if (mWificond == null) {
216            Log.e(TAG, "Failed to get reference to wificond");
217            return false;
218        }
219        try {
220            mWificond.asBinder().linkToDeath(this, 0);
221        } catch (RemoteException e) {
222            Log.e(TAG, "Failed to register death notification for wificond");
223            // The remote has already died.
224            return false;
225        }
226        return true;
227    }
228
229    /**
230    * Setup interface for client mode via wificond.
231    * @return An IClientInterface as wificond client interface binder handler.
232    * Returns null on failure.
233    */
234    public IClientInterface setupInterfaceForClientMode(@NonNull String ifaceName) {
235        Log.d(TAG, "Setting up interface for client mode");
236        if (!retrieveWificondAndRegisterForDeath()) {
237            return null;
238        }
239
240        IClientInterface clientInterface = null;
241        try {
242            clientInterface = mWificond.createClientInterface(ifaceName);
243        } catch (RemoteException e1) {
244            Log.e(TAG, "Failed to get IClientInterface due to remote exception");
245            return null;
246        }
247
248        if (clientInterface == null) {
249            Log.e(TAG, "Could not get IClientInterface instance from wificond");
250            return null;
251        }
252        Binder.allowBlocking(clientInterface.asBinder());
253
254        // Refresh Handlers
255        mClientInterfaces.put(ifaceName, clientInterface);
256        try {
257            IWifiScannerImpl wificondScanner = clientInterface.getWifiScannerImpl();
258            if (wificondScanner == null) {
259                Log.e(TAG, "Failed to get WificondScannerImpl");
260                return null;
261            }
262            mWificondScanners.put(ifaceName, wificondScanner);
263            Binder.allowBlocking(wificondScanner.asBinder());
264            ScanEventHandler scanEventHandler = new ScanEventHandler(ifaceName);
265            mScanEventHandlers.put(ifaceName,  scanEventHandler);
266            wificondScanner.subscribeScanEvents(scanEventHandler);
267            PnoScanEventHandler pnoScanEventHandler = new PnoScanEventHandler(ifaceName);
268            mPnoScanEventHandlers.put(ifaceName,  pnoScanEventHandler);
269            wificondScanner.subscribePnoScanEvents(pnoScanEventHandler);
270        } catch (RemoteException e) {
271            Log.e(TAG, "Failed to refresh wificond scanner due to remote exception");
272        }
273
274        return clientInterface;
275    }
276
277    /**
278     * Teardown a specific STA interface configured in wificond.
279     *
280     * @return Returns true on success.
281     */
282    public boolean tearDownClientInterface(@NonNull String ifaceName) {
283        if (getClientInterface(ifaceName) == null) {
284            Log.e(TAG, "No valid wificond client interface handler");
285            return false;
286        }
287        try {
288            IWifiScannerImpl scannerImpl = mWificondScanners.get(ifaceName);
289            if (scannerImpl != null) {
290                scannerImpl.unsubscribeScanEvents();
291                scannerImpl.unsubscribePnoScanEvents();
292            }
293        } catch (RemoteException e) {
294            Log.e(TAG, "Failed to unsubscribe wificond scanner due to remote exception");
295            return false;
296        }
297
298        boolean success;
299        try {
300            success = mWificond.tearDownClientInterface(ifaceName);
301        } catch (RemoteException e1) {
302            Log.e(TAG, "Failed to teardown client interface due to remote exception");
303            return false;
304        }
305        if (!success) {
306            Log.e(TAG, "Failed to teardown client interface");
307            return false;
308        }
309
310        mClientInterfaces.remove(ifaceName);
311        mWificondScanners.remove(ifaceName);
312        mScanEventHandlers.remove(ifaceName);
313        mPnoScanEventHandlers.remove(ifaceName);
314        return true;
315    }
316
317    /**
318    * Setup interface for softAp mode via wificond.
319    * @return An IApInterface as wificond Ap interface binder handler.
320    * Returns null on failure.
321    */
322    public IApInterface setupInterfaceForSoftApMode(@NonNull String ifaceName) {
323        Log.d(TAG, "Setting up interface for soft ap mode");
324        if (!retrieveWificondAndRegisterForDeath()) {
325            return null;
326        }
327
328        IApInterface apInterface = null;
329        try {
330            apInterface = mWificond.createApInterface(ifaceName);
331        } catch (RemoteException e1) {
332            Log.e(TAG, "Failed to get IApInterface due to remote exception");
333            return null;
334        }
335
336        if (apInterface == null) {
337            Log.e(TAG, "Could not get IApInterface instance from wificond");
338            return null;
339        }
340        Binder.allowBlocking(apInterface.asBinder());
341
342        // Refresh Handlers
343        mApInterfaces.put(ifaceName, apInterface);
344        return apInterface;
345    }
346
347    /**
348     * Teardown a specific AP interface configured in wificond.
349     *
350     * @return Returns true on success.
351     */
352    public boolean tearDownSoftApInterface(@NonNull String ifaceName) {
353        if (getApInterface(ifaceName) == null) {
354            Log.e(TAG, "No valid wificond ap interface handler");
355            return false;
356        }
357        boolean success;
358        try {
359            success = mWificond.tearDownApInterface(ifaceName);
360        } catch (RemoteException e1) {
361            Log.e(TAG, "Failed to teardown AP interface due to remote exception");
362            return false;
363        }
364        if (!success) {
365            Log.e(TAG, "Failed to teardown AP interface");
366            return false;
367        }
368        mApInterfaces.remove(ifaceName);
369        mApInterfaceListeners.remove(ifaceName);
370        return true;
371    }
372
373    /**
374    * Teardown all interfaces configured in wificond.
375    * @return Returns true on success.
376    */
377    public boolean tearDownInterfaces() {
378        Log.d(TAG, "tearing down interfaces in wificond");
379        // Explicitly refresh the wificodn handler because |tearDownInterfaces()|
380        // could be used to cleanup before we setup any interfaces.
381        if (!retrieveWificondAndRegisterForDeath()) {
382            return false;
383        }
384
385        try {
386            for (Map.Entry<String, IWifiScannerImpl> entry : mWificondScanners.entrySet()) {
387                entry.getValue().unsubscribeScanEvents();
388                entry.getValue().unsubscribePnoScanEvents();
389            }
390            mWificond.tearDownInterfaces();
391            clearState();
392            return true;
393        } catch (RemoteException e) {
394            Log.e(TAG, "Failed to tear down interfaces due to remote exception");
395        }
396
397        return false;
398    }
399
400    /** Helper function to look up the interface handle using name */
401    private IClientInterface getClientInterface(@NonNull String ifaceName) {
402        return mClientInterfaces.get(ifaceName);
403    }
404
405    /**
406    * Disable wpa_supplicant via wificond.
407    * @return Returns true on success.
408    */
409    public boolean disableSupplicant() {
410        if (!retrieveWificondAndRegisterForDeath()) {
411            return false;
412        }
413        try {
414            return mWificond.disableSupplicant();
415        } catch (RemoteException e) {
416            Log.e(TAG, "Failed to disable supplicant due to remote exception");
417        }
418        return false;
419    }
420
421    /**
422    * Enable wpa_supplicant via wificond.
423    * @return Returns true on success.
424    */
425    public boolean enableSupplicant() {
426        if (!retrieveWificondAndRegisterForDeath()) {
427            return false;
428        }
429        try {
430            return mWificond.enableSupplicant();
431        } catch (RemoteException e) {
432            Log.e(TAG, "Failed to enable supplicant due to remote exception");
433        }
434        return false;
435    }
436
437    /**
438     * Request signal polling to wificond.
439     * @param ifaceName Name of the interface.
440     * Returns an SignalPollResult object.
441     * Returns null on failure.
442     */
443    public WifiNative.SignalPollResult signalPoll(@NonNull String ifaceName) {
444        IClientInterface iface = getClientInterface(ifaceName);
445        if (iface == null) {
446            Log.e(TAG, "No valid wificond client interface handler");
447            return null;
448        }
449
450        int[] resultArray;
451        try {
452            resultArray = iface.signalPoll();
453            if (resultArray == null || resultArray.length != 3) {
454                Log.e(TAG, "Invalid signal poll result from wificond");
455                return null;
456            }
457        } catch (RemoteException e) {
458            Log.e(TAG, "Failed to do signal polling due to remote exception");
459            return null;
460        }
461        WifiNative.SignalPollResult pollResult = new WifiNative.SignalPollResult();
462        pollResult.currentRssi = resultArray[0];
463        pollResult.txBitrate = resultArray[1];
464        pollResult.associationFrequency = resultArray[2];
465        return pollResult;
466    }
467
468    /**
469     * Fetch TX packet counters on current connection from wificond.
470     * @param ifaceName Name of the interface.
471     * Returns an TxPacketCounters object.
472     * Returns null on failure.
473     */
474    public WifiNative.TxPacketCounters getTxPacketCounters(@NonNull String ifaceName) {
475        IClientInterface iface = getClientInterface(ifaceName);
476        if (iface == null) {
477            Log.e(TAG, "No valid wificond client interface handler");
478            return null;
479        }
480
481        int[] resultArray;
482        try {
483            resultArray = iface.getPacketCounters();
484            if (resultArray == null || resultArray.length != 2) {
485                Log.e(TAG, "Invalid signal poll result from wificond");
486                return null;
487            }
488        } catch (RemoteException e) {
489            Log.e(TAG, "Failed to do signal polling due to remote exception");
490            return null;
491        }
492        WifiNative.TxPacketCounters counters = new WifiNative.TxPacketCounters();
493        counters.txSucceeded = resultArray[0];
494        counters.txFailed = resultArray[1];
495        return counters;
496    }
497
498    /** Helper function to look up the scanner impl handle using name */
499    private IWifiScannerImpl getScannerImpl(@NonNull String ifaceName) {
500        return mWificondScanners.get(ifaceName);
501    }
502
503    /**
504    * Fetch the latest scan result from kernel via wificond.
505    * @param ifaceName Name of the interface.
506    * @return Returns an ArrayList of ScanDetail.
507    * Returns an empty ArrayList on failure.
508    */
509    public ArrayList<ScanDetail> getScanResults(@NonNull String ifaceName, int scanType) {
510        ArrayList<ScanDetail> results = new ArrayList<>();
511        IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName);
512        if (scannerImpl == null) {
513            Log.e(TAG, "No valid wificond scanner interface handler");
514            return results;
515        }
516        try {
517            NativeScanResult[] nativeResults;
518            if (scanType == SCAN_TYPE_SINGLE_SCAN) {
519                nativeResults = scannerImpl.getScanResults();
520            } else {
521                nativeResults = scannerImpl.getPnoScanResults();
522            }
523            for (NativeScanResult result : nativeResults) {
524                WifiSsid wifiSsid = WifiSsid.createFromByteArray(result.ssid);
525                String bssid;
526                try {
527                    bssid = NativeUtil.macAddressFromByteArray(result.bssid);
528                } catch (IllegalArgumentException e) {
529                    Log.e(TAG, "Illegal argument " + result.bssid, e);
530                    continue;
531                }
532                if (bssid == null) {
533                    Log.e(TAG, "Illegal null bssid");
534                    continue;
535                }
536                ScanResult.InformationElement[] ies =
537                        InformationElementUtil.parseInformationElements(result.infoElement);
538                InformationElementUtil.Capabilities capabilities =
539                        new InformationElementUtil.Capabilities();
540                capabilities.from(ies, result.capability);
541                String flags = capabilities.generateCapabilitiesString();
542                NetworkDetail networkDetail;
543                try {
544                    networkDetail = new NetworkDetail(bssid, ies, null, result.frequency);
545                } catch (IllegalArgumentException e) {
546                    Log.e(TAG, "Illegal argument for scan result with bssid: " + bssid, e);
547                    continue;
548                }
549
550                ScanDetail scanDetail = new ScanDetail(networkDetail, wifiSsid, bssid, flags,
551                        result.signalMbm / 100, result.frequency, result.tsf, ies, null);
552                ScanResult scanResult = scanDetail.getScanResult();
553                // Update carrier network info if this AP's SSID is associated with a carrier Wi-Fi
554                // network and it uses EAP.
555                if (ScanResultUtil.isScanResultForEapNetwork(scanDetail.getScanResult())
556                        && mCarrierNetworkConfig.isCarrierNetwork(wifiSsid.toString())) {
557                    scanResult.isCarrierAp = true;
558                    scanResult.carrierApEapType =
559                            mCarrierNetworkConfig.getNetworkEapType(wifiSsid.toString());
560                    scanResult.carrierName =
561                            mCarrierNetworkConfig.getCarrierName(wifiSsid.toString());
562                }
563                // Fill up the radio chain info.
564                if (result.radioChainInfos != null) {
565                    scanResult.radioChainInfos =
566                        new ScanResult.RadioChainInfo[result.radioChainInfos.size()];
567                    int idx = 0;
568                    for (RadioChainInfo nativeRadioChainInfo : result.radioChainInfos) {
569                        scanResult.radioChainInfos[idx] = new ScanResult.RadioChainInfo();
570                        scanResult.radioChainInfos[idx].id = nativeRadioChainInfo.chainId;
571                        scanResult.radioChainInfos[idx].level = nativeRadioChainInfo.level;
572                        idx++;
573                    }
574                }
575                results.add(scanDetail);
576            }
577        } catch (RemoteException e1) {
578            Log.e(TAG, "Failed to create ScanDetail ArrayList");
579        }
580        if (mVerboseLoggingEnabled) {
581            Log.d(TAG, "get " + results.size() + " scan results from wificond");
582        }
583
584        return results;
585    }
586
587    /**
588     * Return scan type for the parcelable {@link SingleScanSettings}
589     */
590    private static int getScanType(int scanType) {
591        switch (scanType) {
592            case WifiNative.SCAN_TYPE_LOW_LATENCY:
593                return IWifiScannerImpl.SCAN_TYPE_LOW_SPAN;
594            case WifiNative.SCAN_TYPE_LOW_POWER:
595                return IWifiScannerImpl.SCAN_TYPE_LOW_POWER;
596            case WifiNative.SCAN_TYPE_HIGH_ACCURACY:
597                return IWifiScannerImpl.SCAN_TYPE_HIGH_ACCURACY;
598            default:
599                throw new IllegalArgumentException("Invalid scan type " + scanType);
600        }
601    }
602
603    /**
604     * Start a scan using wificond for the given parameters.
605     * @param ifaceName Name of the interface.
606     * @param scanType Type of scan to perform.
607     * @param freqs list of frequencies to scan for, if null scan all supported channels.
608     * @param hiddenNetworkSSIDs List of hidden networks to be scanned for.
609     * @return Returns true on success.
610     */
611    public boolean scan(@NonNull String ifaceName,
612                        int scanType,
613                        Set<Integer> freqs,
614                        Set<String> hiddenNetworkSSIDs) {
615        IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName);
616        if (scannerImpl == null) {
617            Log.e(TAG, "No valid wificond scanner interface handler");
618            return false;
619        }
620        SingleScanSettings settings = new SingleScanSettings();
621        try {
622            settings.scanType = getScanType(scanType);
623        } catch (IllegalArgumentException e) {
624            Log.e(TAG, "Invalid scan type ", e);
625            return false;
626        }
627        settings.channelSettings  = new ArrayList<>();
628        settings.hiddenNetworks  = new ArrayList<>();
629
630        if (freqs != null) {
631            for (Integer freq : freqs) {
632                ChannelSettings channel = new ChannelSettings();
633                channel.frequency = freq;
634                settings.channelSettings.add(channel);
635            }
636        }
637        if (hiddenNetworkSSIDs != null) {
638            for (String ssid : hiddenNetworkSSIDs) {
639                HiddenNetwork network = new HiddenNetwork();
640                try {
641                    network.ssid = NativeUtil.byteArrayFromArrayList(NativeUtil.decodeSsid(ssid));
642                } catch (IllegalArgumentException e) {
643                    Log.e(TAG, "Illegal argument " + ssid, e);
644                    continue;
645                }
646                settings.hiddenNetworks.add(network);
647            }
648        }
649
650        try {
651            return scannerImpl.scan(settings);
652        } catch (RemoteException e1) {
653            Log.e(TAG, "Failed to request scan due to remote exception");
654        }
655        return false;
656    }
657
658    /**
659     * Start PNO scan.
660     * @param ifaceName Name of the interface.
661     * @param pnoSettings Pno scan configuration.
662     * @return true on success.
663     */
664    public boolean startPnoScan(@NonNull String ifaceName, WifiNative.PnoSettings pnoSettings) {
665        IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName);
666        if (scannerImpl == null) {
667            Log.e(TAG, "No valid wificond scanner interface handler");
668            return false;
669        }
670        PnoSettings settings = new PnoSettings();
671        settings.pnoNetworks  = new ArrayList<>();
672        settings.intervalMs = pnoSettings.periodInMs;
673        settings.min2gRssi = pnoSettings.min24GHzRssi;
674        settings.min5gRssi = pnoSettings.min5GHzRssi;
675        if (pnoSettings.networkList != null) {
676            for (WifiNative.PnoNetwork network : pnoSettings.networkList) {
677                PnoNetwork condNetwork = new PnoNetwork();
678                condNetwork.isHidden = (network.flags
679                        & WifiScanner.PnoSettings.PnoNetwork.FLAG_DIRECTED_SCAN) != 0;
680                try {
681                    condNetwork.ssid =
682                            NativeUtil.byteArrayFromArrayList(NativeUtil.decodeSsid(network.ssid));
683                } catch (IllegalArgumentException e) {
684                    Log.e(TAG, "Illegal argument " + network.ssid, e);
685                    continue;
686                }
687                settings.pnoNetworks.add(condNetwork);
688            }
689        }
690
691        try {
692            boolean success = scannerImpl.startPnoScan(settings);
693            mWifiInjector.getWifiMetrics().incrementPnoScanStartAttempCount();
694            if (!success) {
695                mWifiInjector.getWifiMetrics().incrementPnoScanFailedCount();
696            }
697            return success;
698        } catch (RemoteException e1) {
699            Log.e(TAG, "Failed to start pno scan due to remote exception");
700        }
701        return false;
702    }
703
704    /**
705     * Stop PNO scan.
706     * @param ifaceName Name of the interface.
707     * @return true on success.
708     */
709    public boolean stopPnoScan(@NonNull String ifaceName) {
710        IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName);
711        if (scannerImpl == null) {
712            Log.e(TAG, "No valid wificond scanner interface handler");
713            return false;
714        }
715        try {
716            return scannerImpl.stopPnoScan();
717        } catch (RemoteException e1) {
718            Log.e(TAG, "Failed to stop pno scan due to remote exception");
719        }
720        return false;
721    }
722
723    /**
724     * Abort ongoing single scan.
725     * @param ifaceName Name of the interface.
726     */
727    public void abortScan(@NonNull String ifaceName) {
728        IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName);
729        if (scannerImpl == null) {
730            Log.e(TAG, "No valid wificond scanner interface handler");
731            return;
732        }
733        try {
734            scannerImpl.abortScan();
735        } catch (RemoteException e1) {
736            Log.e(TAG, "Failed to request abortScan due to remote exception");
737        }
738    }
739
740    /**
741     * Query the list of valid frequencies for the provided band.
742     * The result depends on the on the country code that has been set.
743     *
744     * @param band as specified by one of the WifiScanner.WIFI_BAND_* constants.
745     * The following bands are supported:
746     * WifiScanner.WIFI_BAND_24_GHZ
747     * WifiScanner.WIFI_BAND_5_GHZ
748     * WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY
749     * @return frequencies vector of valid frequencies (MHz), or null for error.
750     * @throws IllegalArgumentException if band is not recognized.
751     */
752    public int [] getChannelsForBand(int band) {
753        if (mWificond == null) {
754            Log.e(TAG, "No valid wificond scanner interface handler");
755            return null;
756        }
757        try {
758            switch (band) {
759                case WifiScanner.WIFI_BAND_24_GHZ:
760                    return mWificond.getAvailable2gChannels();
761                case WifiScanner.WIFI_BAND_5_GHZ:
762                    return mWificond.getAvailable5gNonDFSChannels();
763                case WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY:
764                    return mWificond.getAvailableDFSChannels();
765                default:
766                    throw new IllegalArgumentException("unsupported band " + band);
767            }
768        } catch (RemoteException e1) {
769            Log.e(TAG, "Failed to request getChannelsForBand due to remote exception");
770        }
771        return null;
772    }
773
774    /** Helper function to look up the interface handle using name */
775    private IApInterface getApInterface(@NonNull String ifaceName) {
776        return mApInterfaces.get(ifaceName);
777    }
778
779    /**
780     * Start hostapd
781     * TODO(b/71513606): Move this to a global operation.
782     *
783     * @param ifaceName Name of the interface.
784     * @param listener Callback for AP events.
785     * @return true on success, false otherwise.
786     */
787    public boolean startHostapd(@NonNull String ifaceName,
788                               SoftApListener listener) {
789        IApInterface iface = getApInterface(ifaceName);
790        if (iface == null) {
791            Log.e(TAG, "No valid ap interface handler");
792            return false;
793        }
794        try {
795            IApInterfaceEventCallback  callback = new ApInterfaceEventCallback(listener);
796            mApInterfaceListeners.put(ifaceName, callback);
797            boolean success = iface.startHostapd(callback);
798            if (!success) {
799                Log.e(TAG, "Failed to start hostapd.");
800                return false;
801            }
802        } catch (RemoteException e) {
803            Log.e(TAG, "Exception in starting soft AP: " + e);
804            return false;
805        }
806        return true;
807    }
808
809    /**
810     * Stop hostapd
811     * TODO(b/71513606): Move this to a global operation.
812     *
813     * @param ifaceName Name of the interface.
814     * @return true on success, false otherwise.
815     */
816    public boolean stopHostapd(@NonNull String ifaceName) {
817        IApInterface iface = getApInterface(ifaceName);
818        if (iface == null) {
819            Log.e(TAG, "No valid ap interface handler");
820            return false;
821        }
822        try {
823            boolean success = iface.stopHostapd();
824            if (!success) {
825                Log.e(TAG, "Failed to stop hostapd.");
826                return false;
827            }
828        } catch (RemoteException e) {
829            Log.e(TAG, "Exception in stopping soft AP: " + e);
830            return false;
831        }
832        mApInterfaceListeners.remove(ifaceName);
833        return true;
834    }
835
836    /**
837     * Set Mac address on the given interface
838     * @param interfaceName Name of the interface.
839     * @param mac Mac address to change into
840     * @return true on success, false otherwise.
841     */
842    public boolean setMacAddress(@NonNull String interfaceName, @NonNull MacAddress mac) {
843        IClientInterface mClientInterface = getClientInterface(interfaceName);
844        if (mClientInterface == null) {
845            Log.e(TAG, "No valid wificond client interface handler");
846            return false;
847        }
848        byte[] macByteArray = mac.toByteArray();
849
850        try {
851            mClientInterface.setMacAddress(macByteArray);
852        } catch (RemoteException e) {
853            Log.e(TAG, "Failed to setMacAddress due to remote exception");
854            return false;
855        }
856        return true;
857    }
858
859    /**
860     * Clear all internal handles.
861     */
862    private void clearState() {
863        // Refresh handlers
864        mClientInterfaces.clear();
865        mWificondScanners.clear();
866        mPnoScanEventHandlers.clear();
867        mScanEventHandlers.clear();
868        mApInterfaces.clear();
869        mApInterfaceListeners.clear();
870    }
871}
872