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