• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 package com.android.server.wifi;
17 
18 import static com.android.server.wifi.HalDeviceManager.HDM_CREATE_IFACE_AP;
19 import static com.android.server.wifi.HalDeviceManager.HDM_CREATE_IFACE_AP_BRIDGE;
20 import static com.android.server.wifi.HalDeviceManager.HDM_CREATE_IFACE_STA;
21 
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.content.Context;
25 import android.content.pm.PackageManager;
26 import android.hardware.wifi.WifiStatusCode;
27 import android.net.MacAddress;
28 import android.net.apf.ApfCapabilities;
29 import android.net.wifi.ScanResult;
30 import android.net.wifi.SoftApConfiguration;
31 import android.net.wifi.WifiAvailableChannel;
32 import android.net.wifi.WifiManager;
33 import android.net.wifi.WifiScanner;
34 import android.net.wifi.WifiSsid;
35 import android.os.Handler;
36 import android.os.WorkSource;
37 import android.text.TextUtils;
38 import android.util.Log;
39 import android.util.Pair;
40 import android.util.SparseArray;
41 
42 import com.android.internal.annotations.VisibleForTesting;
43 import com.android.server.wifi.HalDeviceManager.InterfaceDestroyedListener;
44 import com.android.server.wifi.WifiNative.RxFateReport;
45 import com.android.server.wifi.WifiNative.TxFateReport;
46 import com.android.server.wifi.hal.WifiApIface;
47 import com.android.server.wifi.hal.WifiChip;
48 import com.android.server.wifi.hal.WifiHal;
49 import com.android.server.wifi.hal.WifiStaIface;
50 
51 import com.google.errorprone.annotations.CompileTimeConstant;
52 
53 import java.util.ArrayList;
54 import java.util.Arrays;
55 import java.util.HashMap;
56 import java.util.List;
57 import java.util.Set;
58 import java.util.stream.Collectors;
59 
60 /**
61  * Wi-Fi Vendor HAL.
62  * Interface management is handled by the HalDeviceManager.
63  */
64 public class WifiVendorHal {
65 
66     private static final WifiLog sNoLog = new FakeWifiLog();
67 
68     /**
69      * Chatty logging should use mVerboseLog
70      */
71     @VisibleForTesting
72     WifiLog mVerboseLog = sNoLog;
73 
74     /**
75      * Errors should use mLog
76      */
77     @VisibleForTesting
78     WifiLog mLog = new LogcatLog("WifiVendorHal");
79 
80     /**
81      * Enables or disables verbose logging
82      *
83      */
enableVerboseLogging(boolean verboseEnabled, boolean halVerboseEnabled)84     public void enableVerboseLogging(boolean verboseEnabled, boolean halVerboseEnabled) {
85         synchronized (sLock) {
86             if (verboseEnabled) {
87                 mVerboseLog = mLog;
88                 enter("verbose=true").flush();
89             } else {
90                 enter("verbose=false").flush();
91                 mVerboseLog = sNoLog;
92             }
93         }
94     }
95 
96     /**
97      * Logs at method entry
98      *
99      * @param format string with % placeholders
100      * @return LogMessage formatter (remember to .flush())
101      */
enter(@ompileTimeConstant final String format)102     private WifiLog.LogMessage enter(@CompileTimeConstant final String format) {
103         if (mVerboseLog == sNoLog) return sNoLog.info(format);
104         return mVerboseLog.trace(format, 1);
105     }
106 
107     // Vendor HAL interface objects.
108     private WifiChip mWifiChip;
109     private HashMap<String, WifiStaIface> mWifiStaIfaces = new HashMap<>();
110     private HashMap<String, WifiApIface> mWifiApIfaces = new HashMap<>();
111     private static Context sContext;
112     private final HalDeviceManager mHalDeviceManager;
113     private final WifiGlobals mWifiGlobals;
114     private final SsidTranslator mSsidTranslator;
115     private final HalDeviceManagerStatusListener mHalDeviceManagerStatusCallbacks;
116     private final WifiStaIface.Callback mWifiStaIfaceEventCallback;
117     private final ChipEventCallback mWifiChipEventCallback;
118 
119     // Plumbing for event handling.
120     //
121     // Being final fields, they can be accessed without synchronization under
122     // some reasonable assumptions. See
123     // https://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5
124     private final Handler mHalEventHandler;
125 
126 
127     /**
128      * Wi-Fi chip related info.
129      */
130     private static class WifiChipInfo {
131         public WifiChip.WifiChipCapabilities capabilities = null;
132         /**
133          * Add more chip specific parameters here. Basically it avoids frequent call to chip by
134          * caching it on {@link mCachedWifiChipInfos}.
135          */
136     }
137     /** A cache which maps chip id to {@link WifiChipInfo} */
138     private SparseArray<WifiChipInfo> mCachedWifiChipInfos = new SparseArray<>();
139 
WifiVendorHal(Context context, HalDeviceManager halDeviceManager, Handler handler, WifiGlobals wifiGlobals, @NonNull SsidTranslator ssidTranslator)140     public WifiVendorHal(Context context, HalDeviceManager halDeviceManager, Handler handler,
141             WifiGlobals wifiGlobals,
142             @NonNull SsidTranslator ssidTranslator) {
143         sContext = context;
144         mHalDeviceManager = halDeviceManager;
145         mHalEventHandler = handler;
146         mWifiGlobals = wifiGlobals;
147         mSsidTranslator = ssidTranslator;
148         mHalDeviceManagerStatusCallbacks = new HalDeviceManagerStatusListener();
149         mWifiStaIfaceEventCallback = new StaIfaceEventCallback();
150         mWifiChipEventCallback = new ChipEventCallback();
151     }
152 
153     public static final Object sLock = new Object();
154 
155     private WifiNative.VendorHalDeathEventHandler mDeathEventHandler;
156 
157     /**
158      * Initialize the Hal device manager and register for status callbacks.
159      *
160      * @param handler Handler to notify if the vendor HAL dies.
161      * @return true on success, false otherwise.
162      */
initialize(WifiNative.VendorHalDeathEventHandler handler)163     public boolean initialize(WifiNative.VendorHalDeathEventHandler handler) {
164         synchronized (sLock) {
165             mHalDeviceManager.initialize();
166             mHalDeviceManager.registerStatusListener(
167                     mHalDeviceManagerStatusCallbacks, mHalEventHandler);
168             mDeathEventHandler = handler;
169             return true;
170         }
171     }
172 
173     private WifiNative.VendorHalRadioModeChangeEventHandler mRadioModeChangeEventHandler;
174 
175     /**
176      * Register to listen for radio mode change events from the HAL.
177      *
178      * @param handler Handler to notify when the vendor HAL detects a radio mode change.
179      */
registerRadioModeChangeHandler( WifiNative.VendorHalRadioModeChangeEventHandler handler)180     public void registerRadioModeChangeHandler(
181             WifiNative.VendorHalRadioModeChangeEventHandler handler) {
182         synchronized (sLock) {
183             mRadioModeChangeEventHandler = handler;
184         }
185     }
186 
187     /**
188      * Register to listen for subsystem restart events from the HAL.
189      *
190      * @param listener SubsystemRestartListener listener object.
191      */
registerSubsystemRestartListener( HalDeviceManager.SubsystemRestartListener listener)192     public void registerSubsystemRestartListener(
193             HalDeviceManager.SubsystemRestartListener listener) {
194         mHalDeviceManager.registerSubsystemRestartListener(listener, mHalEventHandler);
195     }
196 
197     /**
198      * Returns whether the vendor HAL is supported on this device or not.
199      */
isVendorHalSupported()200     public boolean isVendorHalSupported() {
201         synchronized (sLock) {
202             return mHalDeviceManager.isSupported();
203         }
204     }
205 
206     /**
207      * Returns whether the vendor HAL is ready or not.
208      */
isVendorHalReady()209     public boolean isVendorHalReady() {
210         synchronized (sLock) {
211             return mHalDeviceManager.isReady();
212         }
213     }
214 
215     /**
216      * Bring up the Vendor HAL and configure for STA (Station) mode
217      *
218      * @return true for success
219      */
startVendorHalSta(@onNull ConcreteClientModeManager concreteClientModeManager)220     public boolean startVendorHalSta(@NonNull ConcreteClientModeManager concreteClientModeManager) {
221         synchronized (sLock) {
222             if (!startVendorHal()) {
223                 return false;
224             }
225             if (TextUtils.isEmpty(createStaIface(null, null,
226                     concreteClientModeManager))) {
227                 stopVendorHal();
228                 return false;
229             }
230             return true;
231         }
232     }
233 
234     /**
235      * Bring up the Vendor HAL.
236      * @return true on success, false otherwise.
237      */
startVendorHal()238     public boolean startVendorHal() {
239         synchronized (sLock) {
240             if (!mHalDeviceManager.start()) {
241                 mLog.err("Failed to start vendor HAL").flush();
242                 return false;
243             }
244             mLog.info("Vendor Hal started successfully").flush();
245             return true;
246         }
247     }
248 
249 
250     /** Helper method to lookup the corresponding STA iface object using iface name. */
getStaIface(@onNull String ifaceName)251     private WifiStaIface getStaIface(@NonNull String ifaceName) {
252         synchronized (sLock) {
253             return mWifiStaIfaces.get(ifaceName);
254         }
255     }
256 
257     private class StaInterfaceDestroyedListenerInternal implements InterfaceDestroyedListener {
258         private final InterfaceDestroyedListener mExternalListener;
259 
StaInterfaceDestroyedListenerInternal(InterfaceDestroyedListener externalListener)260         StaInterfaceDestroyedListenerInternal(InterfaceDestroyedListener externalListener) {
261             mExternalListener = externalListener;
262         }
263 
264         @Override
onDestroyed(@onNull String ifaceName)265         public void onDestroyed(@NonNull String ifaceName) {
266             synchronized (sLock) {
267                 mWifiStaIfaces.remove(ifaceName);
268             }
269             if (mExternalListener != null) {
270                 mExternalListener.onDestroyed(ifaceName);
271             }
272         }
273     }
274 
275     /**
276      * Create a STA iface using {@link HalDeviceManager}.
277      *
278      * @param destroyedListener Listener to be invoked when the interface is destroyed.
279      * @param requestorWs Requestor worksource.
280      * @param concreteClientModeManager ConcreteClientModeManager requesting the interface.
281      * @return iface name on success, null otherwise.
282      */
createStaIface(@ullable InterfaceDestroyedListener destroyedListener, @NonNull WorkSource requestorWs, @NonNull ConcreteClientModeManager concreteClientModeManager)283     public String createStaIface(@Nullable InterfaceDestroyedListener destroyedListener,
284             @NonNull WorkSource requestorWs,
285             @NonNull ConcreteClientModeManager concreteClientModeManager) {
286         synchronized (sLock) {
287             WifiStaIface iface = mHalDeviceManager.createStaIface(
288                     new StaInterfaceDestroyedListenerInternal(destroyedListener), mHalEventHandler,
289                     requestorWs, concreteClientModeManager);
290             if (iface == null) {
291                 mLog.err("Failed to create STA iface").flush();
292                 return null;
293             }
294             String ifaceName = iface.getName();
295             if (TextUtils.isEmpty(ifaceName)) {
296                 mLog.err("Failed to get iface name").flush();
297                 return null;
298             }
299             if (!registerStaIfaceCallback(iface)) {
300                 mLog.err("Failed to register STA iface callback").flush();
301                 return null;
302             }
303             if (!retrieveWifiChip(iface)) {
304                 mLog.err("Failed to get wifi chip").flush();
305                 return null;
306             }
307             mWifiStaIfaces.put(ifaceName, iface);
308             return ifaceName;
309         }
310     }
311 
312     /**
313      * Replace the requestor worksource info for a STA iface using {@link HalDeviceManager}.
314      *
315      * @param ifaceName Name of the interface being removed.
316      * @param requestorWs Requestor worksource.
317      * @return true on success, false otherwise.
318      */
replaceStaIfaceRequestorWs(@onNull String ifaceName, @NonNull WorkSource requestorWs)319     public boolean replaceStaIfaceRequestorWs(@NonNull String ifaceName,
320             @NonNull WorkSource requestorWs) {
321         synchronized (sLock) {
322             WifiStaIface iface = getStaIface(ifaceName);
323             if (iface == null) return false;
324 
325             if (!mHalDeviceManager.replaceRequestorWs(iface, requestorWs)) {
326                 mLog.err("Failed to replace requestor worksource for STA iface").flush();
327                 return false;
328             }
329             return true;
330         }
331     }
332 
333     /**
334      * Remove a STA iface using {@link HalDeviceManager}.
335      *
336      * @param ifaceName Name of the interface being removed.
337      * @return true on success, false otherwise.
338      */
removeStaIface(@onNull String ifaceName)339     public boolean removeStaIface(@NonNull String ifaceName) {
340         synchronized (sLock) {
341             WifiStaIface iface = getStaIface(ifaceName);
342             if (iface == null) return false;
343             if (!mHalDeviceManager.removeIface(iface)) {
344                 mLog.err("Failed to remove STA iface").flush();
345                 return false;
346             }
347             mWifiStaIfaces.remove(ifaceName);
348             return true;
349         }
350     }
351 
352     /** Helper method to lookup the corresponding AP iface object using iface name. */
getApIface(@onNull String ifaceName)353     private WifiApIface getApIface(@NonNull String ifaceName) {
354         synchronized (sLock) {
355             return mWifiApIfaces.get(ifaceName);
356         }
357     }
358 
359     private class ApInterfaceDestroyedListenerInternal implements InterfaceDestroyedListener {
360         private final InterfaceDestroyedListener mExternalListener;
361 
ApInterfaceDestroyedListenerInternal(InterfaceDestroyedListener externalListener)362         ApInterfaceDestroyedListenerInternal(InterfaceDestroyedListener externalListener) {
363             mExternalListener = externalListener;
364         }
365 
366         @Override
onDestroyed(@onNull String ifaceName)367         public void onDestroyed(@NonNull String ifaceName) {
368             synchronized (sLock) {
369                 mWifiApIfaces.remove(ifaceName);
370             }
371             if (mExternalListener != null) {
372                 mExternalListener.onDestroyed(ifaceName);
373             }
374         }
375     }
376 
getNecessaryCapabilitiesForSoftApMode(@oftApConfiguration.BandType int band)377     private long getNecessaryCapabilitiesForSoftApMode(@SoftApConfiguration.BandType int band) {
378         long caps = HalDeviceManager.CHIP_CAPABILITY_ANY;
379         if ((band & SoftApConfiguration.BAND_60GHZ) != 0) {
380             caps |= WifiManager.WIFI_FEATURE_INFRA_60G;
381         }
382         return caps;
383     }
384 
385     /**
386      * Create an AP iface using {@link HalDeviceManager}.
387      *
388      * @param destroyedListener Listener to be invoked when the interface is destroyed.
389      * @param requestorWs Requestor worksource.
390      * @param band The requesting band for this AP interface.
391      * @param isBridged Whether or not AP interface is a bridge interface.
392      * @param softApManager SoftApManager of the request.
393      * @return iface name on success, null otherwise.
394      */
createApIface(@ullable InterfaceDestroyedListener destroyedListener, @NonNull WorkSource requestorWs, @SoftApConfiguration.BandType int band, boolean isBridged, @NonNull SoftApManager softApManager)395     public String createApIface(@Nullable InterfaceDestroyedListener destroyedListener,
396             @NonNull WorkSource requestorWs,
397             @SoftApConfiguration.BandType int band,
398             boolean isBridged,
399             @NonNull SoftApManager softApManager) {
400         synchronized (sLock) {
401             WifiApIface iface = mHalDeviceManager.createApIface(
402                     getNecessaryCapabilitiesForSoftApMode(band),
403                     new ApInterfaceDestroyedListenerInternal(destroyedListener), mHalEventHandler,
404                     requestorWs, isBridged, softApManager);
405             if (iface == null) {
406                 mLog.err("Failed to create AP iface").flush();
407                 return null;
408             }
409             String ifaceName = iface.getName();
410             if (TextUtils.isEmpty(ifaceName)) {
411                 mLog.err("Failed to get iface name").flush();
412                 return null;
413             }
414             if (!retrieveWifiChip(iface)) {
415                 mLog.err("Failed to get wifi chip").flush();
416                 return null;
417             }
418             mWifiApIfaces.put(ifaceName, iface);
419             return ifaceName;
420         }
421     }
422 
423     /**
424      * Replace the requestor worksource info for an AP iface using {@link HalDeviceManager}.
425      *
426      * @param ifaceName Name of the interface being removed.
427      * @param requestorWs Requestor worksource.
428      * @return true on success, false otherwise.
429      */
replaceApIfaceRequestorWs(@onNull String ifaceName, @NonNull WorkSource requestorWs)430     public boolean replaceApIfaceRequestorWs(@NonNull String ifaceName,
431             @NonNull WorkSource requestorWs) {
432         synchronized (sLock) {
433             WifiApIface iface = getApIface(ifaceName);
434             if (iface == null) return false;
435 
436             if (!mHalDeviceManager.replaceRequestorWs(iface, requestorWs)) {
437                 mLog.err("Failed to replace requestor worksource for AP iface").flush();
438                 return false;
439             }
440             return true;
441         }
442     }
443 
444     /**
445      * Remove an AP iface using {@link HalDeviceManager}.
446      *
447      * @param ifaceName Name of the interface being removed.
448      * @return true on success, false otherwise.
449      */
removeApIface(@onNull String ifaceName)450     public boolean removeApIface(@NonNull String ifaceName) {
451         synchronized (sLock) {
452             WifiApIface iface = getApIface(ifaceName);
453             if (iface == null) return false;
454 
455             if (!mHalDeviceManager.removeIface(iface)) {
456                 mLog.err("Failed to remove AP iface").flush();
457                 return false;
458             }
459             mWifiApIfaces.remove(ifaceName);
460             return true;
461         }
462     }
463 
464     /**
465      * Helper function to remove specific instance in bridged AP iface.
466      *
467      * @param ifaceName Name of the iface.
468      * @param apIfaceInstance The identity of the ap instance.
469      * @return true if the operation succeeded, false if there is an error in Hal.
470      */
removeIfaceInstanceFromBridgedApIface(@onNull String ifaceName, @NonNull String apIfaceInstance)471     public boolean removeIfaceInstanceFromBridgedApIface(@NonNull String ifaceName,
472             @NonNull String apIfaceInstance) {
473         if (mWifiChip == null) return false;
474         return mWifiChip.removeIfaceInstanceFromBridgedApIface(ifaceName, apIfaceInstance);
475     }
476 
477     /**
478      * Set the current coex unsafe channels to avoid and their restrictions.
479      * @param unsafeChannels List of {@link android.net.wifi.CoexUnsafeChannel} to avoid.
480      * @param restrictions int containing a bitwise-OR combination of
481      *                     {@link WifiManager.CoexRestriction}.
482      * @return true if the operation succeeded, false if there is an error in Hal.
483      */
setCoexUnsafeChannels( @onNull List<android.net.wifi.CoexUnsafeChannel> unsafeChannels, int restrictions)484     public boolean setCoexUnsafeChannels(
485             @NonNull List<android.net.wifi.CoexUnsafeChannel> unsafeChannels, int restrictions) {
486         if (mWifiChip == null) return false;
487         return mWifiChip.setCoexUnsafeChannels(unsafeChannels, restrictions);
488     }
489 
retrieveWifiChip(WifiHal.WifiInterface iface)490     private boolean retrieveWifiChip(WifiHal.WifiInterface iface) {
491         synchronized (sLock) {
492             boolean registrationNeeded = mWifiChip == null;
493             mWifiChip = mHalDeviceManager.getChip(iface);
494             if (mWifiChip == null) {
495                 mLog.err("Failed to get the chip created for the Iface").flush();
496                 return false;
497             }
498             if (!registrationNeeded) {
499                 return true;
500             }
501             if (!registerChipCallback()) {
502                 mLog.err("Failed to register chip callback").flush();
503                 mWifiChip = null;
504                 return false;
505             }
506             cacheWifiChipInfo(mWifiChip);
507             return true;
508         }
509     }
510 
511     /**
512      * Registers the sta iface callback.
513      */
registerStaIfaceCallback(WifiStaIface iface)514     private boolean registerStaIfaceCallback(WifiStaIface iface) {
515         synchronized (sLock) {
516             if (iface == null) return false;
517             if (mWifiStaIfaceEventCallback == null) return false;
518             return iface.registerFrameworkCallback(mWifiStaIfaceEventCallback);
519         }
520     }
521 
522     /**
523      * Registers the sta iface callback.
524      */
registerChipCallback()525     private boolean registerChipCallback() {
526         synchronized (sLock) {
527             if (mWifiChip == null) return false;
528             return mWifiChip.registerCallback(mWifiChipEventCallback);
529         }
530     }
531 
532     /**
533      * Stops the HAL
534      */
stopVendorHal()535     public void stopVendorHal() {
536         synchronized (sLock) {
537             mHalDeviceManager.stop();
538             clearState();
539             mLog.info("Vendor Hal stopped").flush();
540         }
541     }
542 
543     /**
544      * Clears the state associated with a started Iface
545      *
546      * Caller should hold the lock.
547      */
clearState()548     private void clearState() {
549         mWifiChip = null;
550         mWifiStaIfaces.clear();
551         mWifiApIfaces.clear();
552         mDriverDescription = null;
553         mFirmwareDescription = null;
554     }
555 
556     /**
557      * Tests whether the HAL is started and at least one iface is up.
558      */
isHalStarted()559     public boolean isHalStarted() {
560         // For external use only. Methods in this class should test for null directly.
561         synchronized (sLock) {
562             return (!mWifiStaIfaces.isEmpty() || !mWifiApIfaces.isEmpty());
563         }
564     }
565 
566     /**
567      * Gets the scan capabilities
568      *
569      * @param ifaceName Name of the interface.
570      * @param capabilities object to be filled in
571      * @return true for success, false for failure
572      */
getBgScanCapabilities( @onNull String ifaceName, WifiNative.ScanCapabilities capabilities)573     public boolean getBgScanCapabilities(
574             @NonNull String ifaceName, WifiNative.ScanCapabilities capabilities) {
575         synchronized (sLock) {
576             WifiStaIface iface = getStaIface(ifaceName);
577             if (iface == null) return false;
578 
579             WifiNative.ScanCapabilities result = iface.getBackgroundScanCapabilities();
580             if (result != null) {
581                 capabilities.max_scan_cache_size = result.max_scan_cache_size;
582                 capabilities.max_ap_cache_per_scan = result.max_ap_cache_per_scan;
583                 capabilities.max_scan_buckets = result.max_scan_buckets;
584                 capabilities.max_rssi_sample_size = result.max_rssi_sample_size;
585                 capabilities.max_scan_reporting_threshold = result.max_scan_reporting_threshold;
586             }
587             return result != null;
588         }
589     }
590 
591     /**
592      * Holds the current background scan state, to implement pause and restart
593      */
594     @VisibleForTesting
595     class CurrentBackgroundScan {
596         public int cmdId;
597         public WifiStaIface.StaBackgroundScanParameters param;
598         public WifiNative.ScanEventHandler eventHandler = null;
599         public boolean paused = false;
600         public WifiScanner.ScanData[] latestScanResults = null;
601 
CurrentBackgroundScan(int id, WifiNative.ScanSettings settings)602         CurrentBackgroundScan(int id, WifiNative.ScanSettings settings) {
603             cmdId = id;
604             List<WifiNative.BucketSettings> buckets = new ArrayList<>();
605             if (settings.buckets != null) {
606                 buckets = Arrays.asList(settings.buckets);
607             }
608             param = new WifiStaIface.StaBackgroundScanParameters(settings.base_period_ms,
609                     settings.max_ap_per_scan, settings.report_threshold_percent,
610                     settings.report_threshold_num_scans, buckets);
611         }
612     }
613 
614     private int mLastScanCmdId; // For assigning cmdIds to scans
615 
616     @VisibleForTesting
617     CurrentBackgroundScan mScan = null;
618 
619     /**
620      * Starts a background scan
621      *
622      * Any ongoing scan will be stopped first
623      *
624      * @param ifaceName    Name of the interface.
625      * @param settings     to control the scan
626      * @param eventHandler to call with the results
627      * @return true for success
628      */
startBgScan(@onNull String ifaceName, WifiNative.ScanSettings settings, WifiNative.ScanEventHandler eventHandler)629     public boolean startBgScan(@NonNull String ifaceName,
630                                WifiNative.ScanSettings settings,
631                                WifiNative.ScanEventHandler eventHandler) {
632         if (eventHandler == null) return false;
633         synchronized (sLock) {
634             WifiStaIface iface = getStaIface(ifaceName);
635             if (iface == null) return false;
636             if (mScan != null && !mScan.paused) {
637                 iface.stopBackgroundScan(mScan.cmdId);
638                 mScan = null;
639             }
640 
641             mLastScanCmdId = (mLastScanCmdId % 9) + 1; // cycle through non-zero single digits
642             CurrentBackgroundScan scan = new CurrentBackgroundScan(mLastScanCmdId, settings);
643             boolean success = iface.startBackgroundScan(scan.cmdId, scan.param);
644             if (!success) return false;
645 
646             scan.eventHandler = eventHandler;
647             mScan = scan;
648             return true;
649         }
650     }
651 
652 
653     /**
654      * Stops any ongoing background scan
655      *
656      * @param ifaceName Name of the interface.
657      */
stopBgScan(@onNull String ifaceName)658     public void stopBgScan(@NonNull String ifaceName) {
659         synchronized (sLock) {
660             WifiStaIface iface = getStaIface(ifaceName);
661             if (iface == null) return;
662             if (mScan != null) {
663                 iface.stopBackgroundScan(mScan.cmdId);
664                 mScan = null;
665             }
666         }
667     }
668 
669     /**
670      * Pauses an ongoing background scan
671      *
672      * @param ifaceName Name of the interface.
673      */
pauseBgScan(@onNull String ifaceName)674     public void pauseBgScan(@NonNull String ifaceName) {
675         synchronized (sLock) {
676             WifiStaIface iface = getStaIface(ifaceName);
677             if (iface == null) return;
678             if (mScan != null && !mScan.paused) {
679                 if (!iface.stopBackgroundScan(mScan.cmdId)) return;
680                 mScan.paused = true;
681             }
682         }
683     }
684 
685     /**
686      * Restarts a paused background scan
687      *
688      * @param ifaceName Name of the interface.
689      */
restartBgScan(@onNull String ifaceName)690     public void restartBgScan(@NonNull String ifaceName) {
691         synchronized (sLock) {
692             WifiStaIface iface = getStaIface(ifaceName);
693             if (iface == null) return;
694             if (mScan != null && mScan.paused) {
695                 if (!iface.startBackgroundScan(mScan.cmdId, mScan.param)) return;
696                 mScan.paused = false;
697             }
698         }
699     }
700 
701     /**
702      * Gets the latest scan results received from the HAL interface callback.
703      *
704      * @param ifaceName Name of the interface.
705      */
getBgScanResults(@onNull String ifaceName)706     public WifiScanner.ScanData[] getBgScanResults(@NonNull String ifaceName) {
707         synchronized (sLock) {
708             WifiStaIface iface = getStaIface(ifaceName);
709             if (iface == null) return null;
710             if (mScan == null) return null;
711             return mScan.latestScanResults;
712         }
713     }
714 
715     /**
716      * Get the link layer statistics
717      *
718      * Note - we always enable link layer stats on a STA interface.
719      *
720      * @param ifaceName Name of the interface.
721      * @return the statistics, or null if unable to do so
722      */
getWifiLinkLayerStats(@onNull String ifaceName)723     public WifiLinkLayerStats getWifiLinkLayerStats(@NonNull String ifaceName) {
724         synchronized (sLock) {
725             WifiStaIface iface = getStaIface(ifaceName);
726             if (iface == null) return null;
727             return iface.getLinkLayerStats();
728         }
729     }
730 
731     @VisibleForTesting
732     boolean mLinkLayerStatsDebug = false;  // Passed to Hal
733 
734     /**
735      * Enables the linkLayerStats in the Hal.
736      *
737      * This is called unconditionally whenever we create a STA interface.
738      *
739      * @param ifaceName Name of the interface.
740      */
enableLinkLayerStats(@onNull String ifaceName)741     public void enableLinkLayerStats(@NonNull String ifaceName) {
742         synchronized (sLock) {
743             WifiStaIface iface = getStaIface(ifaceName);
744             if (iface == null) {
745                 mLog.err("STA iface object is NULL - Failed to enable link layer stats")
746                         .flush();
747                 return;
748             }
749             if (!iface.enableLinkLayerStatsCollection(mLinkLayerStatsDebug)) {
750                 mLog.err("unable to enable link layer stats collection").flush();
751             }
752         }
753     }
754 
755     /**
756      * Translation table used by getSupportedFeatureSetFromPackageManager
757      * for translating System caps
758      */
759     private static final Pair[] sSystemFeatureCapabilityTranslation = new Pair[] {
760             Pair.create(WifiManager.WIFI_FEATURE_INFRA, PackageManager.FEATURE_WIFI),
761             Pair.create(WifiManager.WIFI_FEATURE_P2P, PackageManager.FEATURE_WIFI_DIRECT),
762             Pair.create(WifiManager.WIFI_FEATURE_AWARE, PackageManager.FEATURE_WIFI_AWARE),
763     };
764 
765     /**
766      * If VendorHal is not supported, reading PackageManager
767      * system features to return basic capabilities.
768      *
769      * @return bitmask defined by WifiManager.WIFI_FEATURE_*
770      */
getSupportedFeatureSetFromPackageManager()771     private long getSupportedFeatureSetFromPackageManager() {
772         long featureSet = 0;
773         final PackageManager pm = sContext.getPackageManager();
774         for (Pair pair: sSystemFeatureCapabilityTranslation) {
775             if (pm.hasSystemFeature((String) pair.second)) {
776                 featureSet |= (long) pair.first;
777             }
778         }
779         enter("System feature set: %").c(featureSet).flush();
780         return featureSet;
781     }
782 
783     /**
784      * Get maximum number of links supported by the chip for MLO association.
785      *
786      * @param ifaceName Name of the interface.
787      * @return maximum number of association links or -1 if error or not available.
788      */
getMaxMloAssociationLinkCount(String ifaceName)789     public int getMaxMloAssociationLinkCount(String ifaceName) {
790         WifiChipInfo wifiChipInfo = getCachedWifiChipInfo(
791                 ifaceName);
792         if (wifiChipInfo == null || wifiChipInfo.capabilities == null) return -1;
793         return wifiChipInfo.capabilities.maxMloAssociationLinkCount;
794     }
795 
796     /**
797      * Get the maximum number of STR links used in Multi-Link Operation.
798      *
799      * @param ifaceName Name of the interface.
800      * @return maximum number of MLO STR links or -1 if error or not available.
801      */
getMaxMloStrLinkCount(String ifaceName)802     public int getMaxMloStrLinkCount(String ifaceName) {
803         WifiChipInfo wifiChipInfo = getCachedWifiChipInfo(
804                 ifaceName);
805         if (wifiChipInfo == null || wifiChipInfo.capabilities == null) return -1;
806         return wifiChipInfo.capabilities.maxMloStrLinkCount;
807     }
808 
809     /**
810      * Get the maximum number of concurrent TDLS sessions supported by the device.
811      *
812      * @param ifaceName Name of the interface.
813      * @return maximum number of concurrent TDLS sessions or -1 if error or not available.
814      */
getMaxSupportedConcurrentTdlsSessions(String ifaceName)815     public int getMaxSupportedConcurrentTdlsSessions(String ifaceName) {
816         WifiChipInfo wifiChipInfo = getCachedWifiChipInfo(
817                 ifaceName);
818         if (wifiChipInfo == null || wifiChipInfo.capabilities == null) return -1;
819         return wifiChipInfo.capabilities.maxConcurrentTdlsSessionCount;
820     }
821 
822     /**
823      * Get Chip specific cached info.
824      *
825      * @param ifaceName Name of the interface
826      * @return the cached information.
827      */
getCachedWifiChipInfo(String ifaceName)828     private WifiChipInfo getCachedWifiChipInfo(String ifaceName) {
829         WifiStaIface iface = getStaIface(ifaceName);
830         if (iface == null) return null;
831 
832         WifiChip chip = mHalDeviceManager.getChip(iface);
833         if (chip == null) return null;
834 
835         return mCachedWifiChipInfos.get(chip.getId());
836     }
837 
838     /**
839      * Cache chip specific info.
840      *
841      * @param chip Wi-Fi chip
842      */
cacheWifiChipInfo(@onNull WifiChip chip)843     private void cacheWifiChipInfo(@NonNull WifiChip chip) {
844         if (mCachedWifiChipInfos.contains(chip.getId())) return;
845         WifiChipInfo wifiChipInfo = new WifiChipInfo();
846         wifiChipInfo.capabilities = chip.getWifiChipCapabilities();
847         mCachedWifiChipInfos.put(chip.getId(), wifiChipInfo);
848     }
849 
850     /**
851      * Get the supported features
852      *
853      * The result may differ depending on the mode (STA or AP)
854      *
855      * @param ifaceName Name of the interface.
856      * @return bitmask defined by WifiManager.WIFI_FEATURE_*
857      */
getSupportedFeatureSet(@onNull String ifaceName)858     public long getSupportedFeatureSet(@NonNull String ifaceName) {
859         long featureSet = 0L;
860         if (!mHalDeviceManager.isStarted() || !mHalDeviceManager.isSupported()) {
861             return getSupportedFeatureSetFromPackageManager();
862         }
863 
864         synchronized (sLock) {
865             if (mWifiChip != null) {
866                 WifiChip.Response<Long> capsResp = mWifiChip.getCapabilitiesAfterIfacesExist();
867                 if (capsResp.getStatusCode() == WifiHal.WIFI_STATUS_SUCCESS) {
868                     featureSet = capsResp.getValue();
869                 } else if (capsResp.getStatusCode() == WifiHal.WIFI_STATUS_ERROR_REMOTE_EXCEPTION) {
870                     return 0;
871                 }
872             }
873 
874             WifiStaIface iface = getStaIface(ifaceName);
875             if (iface != null) {
876                 featureSet |= iface.getCapabilities();
877                 if (mHalDeviceManager.is24g5gDbsSupported(iface)
878                         || mHalDeviceManager.is5g6gDbsSupported(iface)) {
879                     featureSet |= WifiManager.WIFI_FEATURE_DUAL_BAND_SIMULTANEOUS;
880                 }
881             }
882         }
883 
884         if (mWifiGlobals.isWpa3SaeH2eSupported()) {
885             featureSet |= WifiManager.WIFI_FEATURE_SAE_H2E;
886         }
887 
888         Set<Integer> supportedIfaceTypes = mHalDeviceManager.getSupportedIfaceTypes();
889         if (supportedIfaceTypes.contains(WifiChip.IFACE_TYPE_STA)) {
890             featureSet |= WifiManager.WIFI_FEATURE_INFRA;
891         }
892         if (supportedIfaceTypes.contains(WifiChip.IFACE_TYPE_AP)) {
893             featureSet |= WifiManager.WIFI_FEATURE_MOBILE_HOTSPOT;
894         }
895         if (supportedIfaceTypes.contains(WifiChip.IFACE_TYPE_P2P)) {
896             featureSet |= WifiManager.WIFI_FEATURE_P2P;
897         }
898         if (supportedIfaceTypes.contains(WifiChip.IFACE_TYPE_NAN)) {
899             featureSet |= WifiManager.WIFI_FEATURE_AWARE;
900         }
901 
902         return featureSet;
903     }
904 
905     /**
906      * Set Mac address on the given interface
907      *
908      * @param ifaceName Name of the interface
909      * @param mac MAC address to change into
910      * @return true for success
911      */
setStaMacAddress(@onNull String ifaceName, @NonNull MacAddress mac)912     public boolean setStaMacAddress(@NonNull String ifaceName, @NonNull MacAddress mac) {
913         synchronized (sLock) {
914             WifiStaIface iface = getStaIface(ifaceName);
915             if (iface == null) return false;
916             return iface.setMacAddress(mac);
917         }
918     }
919 
920     /**
921      * Reset MAC address to factory MAC address on the given interface
922      *
923      * @param ifaceName Name of the interface
924      * @return true for success
925      */
resetApMacToFactoryMacAddress(@onNull String ifaceName)926     public boolean resetApMacToFactoryMacAddress(@NonNull String ifaceName) {
927         synchronized (sLock) {
928             WifiApIface iface = getApIface(ifaceName);
929             if (iface == null) return false;
930             return iface.resetToFactoryMacAddress();
931         }
932     }
933 
934     /**
935      * Set Mac address on the given interface
936      *
937      * @param ifaceName Name of the interface
938      * @param mac MAC address to change into
939      * @return true for success
940      */
setApMacAddress(@onNull String ifaceName, @NonNull MacAddress mac)941     public boolean setApMacAddress(@NonNull String ifaceName, @NonNull MacAddress mac) {
942         synchronized (sLock) {
943             WifiApIface iface = getApIface(ifaceName);
944             if (iface == null) return false;
945             return iface.setMacAddress(mac);
946         }
947     }
948 
949     /**
950      * Returns true if Hal version supports setMacAddress, otherwise false.
951      *
952      * @param ifaceName Name of the interface
953      */
isApSetMacAddressSupported(@onNull String ifaceName)954     public boolean isApSetMacAddressSupported(@NonNull String ifaceName) {
955         synchronized (sLock) {
956             WifiApIface iface = getApIface(ifaceName);
957             if (iface == null) return false;
958             return iface.isSetMacAddressSupported();
959         }
960     }
961 
962     /**
963      * Get factory MAC address of the given interface
964      *
965      * @param ifaceName Name of the interface
966      * @return factory MAC address of the interface or null.
967      */
getStaFactoryMacAddress(@onNull String ifaceName)968     public MacAddress getStaFactoryMacAddress(@NonNull String ifaceName) {
969         synchronized (sLock) {
970             WifiStaIface iface = getStaIface(ifaceName);
971             if (iface == null) return null;
972             return iface.getFactoryMacAddress();
973         }
974     }
975 
976     /**
977      * Get factory MAC address of the given interface
978      *
979      * @param ifaceName Name of the interface
980      * @return factory MAC address of the interface or null.
981      */
getApFactoryMacAddress(@onNull String ifaceName)982     public MacAddress getApFactoryMacAddress(@NonNull String ifaceName) {
983         synchronized (sLock) {
984             WifiApIface iface = getApIface(ifaceName);
985             if (iface == null) return null;
986             return iface.getFactoryMacAddress();
987         }
988     }
989 
990     /**
991      * Get the APF (Android Packet Filter) capabilities of the device
992      *
993      * @param ifaceName Name of the interface.
994      * @return APF capabilities object.
995      */
getApfCapabilities(@onNull String ifaceName)996     public ApfCapabilities getApfCapabilities(@NonNull String ifaceName) {
997         synchronized (sLock) {
998             WifiStaIface iface = getStaIface(ifaceName);
999             if (iface == null) return sNoApfCapabilities;
1000             return iface.getApfPacketFilterCapabilities();
1001         }
1002     }
1003 
1004     private static final ApfCapabilities sNoApfCapabilities = new ApfCapabilities(0, 0, 0);
1005 
1006     /**
1007      * Installs an APF program on this iface, replacing any existing program.
1008      *
1009      * @param ifaceName Name of the interface.
1010      * @param filter is the android packet filter program
1011      * @return true for success
1012      */
installPacketFilter(@onNull String ifaceName, byte[] filter)1013     public boolean installPacketFilter(@NonNull String ifaceName, byte[] filter) {
1014         if (filter == null) return false;
1015         enter("filter length %").c(filter.length).flush();
1016         synchronized (sLock) {
1017             WifiStaIface iface = getStaIface(ifaceName);
1018             if (iface == null) return false;
1019             return iface.installApfPacketFilter(filter);
1020         }
1021     }
1022 
1023     /**
1024      * Reads the APF program and data buffer on this iface.
1025      *
1026      * @param ifaceName Name of the interface
1027      * @return the buffer returned by the driver, or null in case of an error
1028      */
readPacketFilter(@onNull String ifaceName)1029     public byte[] readPacketFilter(@NonNull String ifaceName) {
1030         enter("").flush();
1031         // TODO: Must also take the wakelock here to prevent going to sleep with APF disabled.
1032         synchronized (sLock) {
1033             WifiStaIface iface = getStaIface(ifaceName);
1034             if (iface == null) return null;
1035             return iface.readApfPacketFilterData();
1036         }
1037     }
1038 
1039     /**
1040      * Set country code for this Wifi chip
1041      *
1042      * @param countryCode - two-letter country code (as ISO 3166)
1043      * @return true for success
1044      */
setChipCountryCode(String countryCode)1045     public boolean setChipCountryCode(String countryCode) {
1046         synchronized (sLock) {
1047             if (mWifiChip == null) return false;
1048             return mWifiChip.setCountryCode(countryCode);
1049         }
1050     }
1051 
1052     /**
1053      * Get the names of the bridged AP instances.
1054      *
1055      * @param ifaceName Name of the bridged interface.
1056      * @return A list which contains the names of the bridged AP instances.
1057      */
1058     @Nullable
getBridgedApInstances(@onNull String ifaceName)1059     public List<String> getBridgedApInstances(@NonNull String ifaceName) {
1060         synchronized (sLock) {
1061             WifiApIface iface = getApIface(ifaceName);
1062             if (iface == null) return null;
1063             return iface.getBridgedInstances();
1064         }
1065     }
1066 
1067     /**
1068      * Set country code for this AP iface.
1069      *
1070      * @param ifaceName Name of the interface.
1071      * @param countryCode - two-letter country code (as ISO 3166)
1072      * @return true for success
1073      */
setApCountryCode(@onNull String ifaceName, String countryCode)1074     public boolean setApCountryCode(@NonNull String ifaceName, String countryCode) {
1075         synchronized (sLock) {
1076             WifiApIface iface = getApIface(ifaceName);
1077             if (iface == null) return false;
1078             return iface.setCountryCode(countryCode);
1079         }
1080     }
1081 
1082     private WifiNative.WifiLoggerEventHandler mLogEventHandler = null;
1083 
1084     /**
1085      * Registers the logger callback and enables alerts.
1086      * Ring buffer data collection is only triggered when |startLoggingRingBuffer| is invoked.
1087      */
setLoggingEventHandler(WifiNative.WifiLoggerEventHandler handler)1088     public boolean setLoggingEventHandler(WifiNative.WifiLoggerEventHandler handler) {
1089         if (handler == null) return false;
1090         synchronized (sLock) {
1091             if (mWifiChip == null) return false;
1092             if (mLogEventHandler != null) return false;
1093             if (!mWifiChip.enableDebugErrorAlerts(true)) {
1094                 return false;
1095             }
1096             mLogEventHandler = handler;
1097             return true;
1098         }
1099     }
1100 
1101     /**
1102      * Stops all logging and resets the logger callback.
1103      * This stops both the alerts and ring buffer data collection.
1104      * Existing log handler is cleared.
1105      */
resetLogHandler()1106     public boolean resetLogHandler() {
1107         synchronized (sLock) {
1108             mLogEventHandler = null;
1109             if (mWifiChip == null) return false;
1110             if (!mWifiChip.enableDebugErrorAlerts(false)) {
1111                 return false;
1112             }
1113             return mWifiChip.stopLoggingToDebugRingBuffer();
1114         }
1115     }
1116 
1117     /**
1118      * Control debug data collection
1119      *
1120      * @param verboseLevel       0 to 3, inclusive. 0 stops logging.
1121      * @param flags              Ignored.
1122      * @param maxIntervalInSec   Maximum interval between reports; ignore if 0.
1123      * @param minDataSizeInBytes Minimum data size in buffer for report; ignore if 0.
1124      * @param ringName           Name of the ring for which data collection is to start.
1125      * @return true for success
1126      */
startLoggingRingBuffer(int verboseLevel, int flags, int maxIntervalInSec, int minDataSizeInBytes, String ringName)1127     public boolean startLoggingRingBuffer(int verboseLevel, int flags, int maxIntervalInSec,
1128                                           int minDataSizeInBytes, String ringName) {
1129         enter("verboseLevel=%, flags=%, maxIntervalInSec=%, minDataSizeInBytes=%, ringName=%")
1130                 .c(verboseLevel).c(flags).c(maxIntervalInSec).c(minDataSizeInBytes).c(ringName)
1131                 .flush();
1132         synchronized (sLock) {
1133             if (mWifiChip == null) return false;
1134             return mWifiChip.startLoggingToDebugRingBuffer(
1135                     ringName,
1136                     verboseLevel,
1137                     maxIntervalInSec,
1138                     minDataSizeInBytes
1139             );
1140         }
1141     }
1142 
1143     /**
1144      * Pointlessly fail
1145      *
1146      * @return -1
1147      */
getSupportedLoggerFeatureSet()1148     public int getSupportedLoggerFeatureSet() {
1149         return -1;
1150     }
1151 
1152     private String mDriverDescription; // Cached value filled by requestChipDebugInfo()
1153 
1154     /**
1155      * Vendor-provided wifi driver version string
1156      */
getDriverVersion()1157     public String getDriverVersion() {
1158         synchronized (sLock) {
1159             if (mDriverDescription == null) requestChipDebugInfo();
1160             return mDriverDescription;
1161         }
1162     }
1163 
1164     private String mFirmwareDescription; // Cached value filled by requestChipDebugInfo()
1165 
1166     /**
1167      * Vendor-provided wifi firmware version string
1168      */
getFirmwareVersion()1169     public String getFirmwareVersion() {
1170         synchronized (sLock) {
1171             if (mFirmwareDescription == null) requestChipDebugInfo();
1172             return mFirmwareDescription;
1173         }
1174     }
1175 
1176     /**
1177      * Refreshes our idea of the driver and firmware versions
1178      */
requestChipDebugInfo()1179     private void requestChipDebugInfo() {
1180         mDriverDescription = null;
1181         mFirmwareDescription = null;
1182         synchronized (sLock) {
1183             if (mWifiChip == null) return;
1184             WifiChip.ChipDebugInfo info = mWifiChip.requestChipDebugInfo();
1185             if (info == null) return;
1186             mDriverDescription = info.driverDescription;
1187             mFirmwareDescription = info.firmwareDescription;
1188         }
1189         mLog.info("Driver: % Firmware: %")
1190                 .c(mDriverDescription)
1191                 .c(mFirmwareDescription)
1192                 .flush();
1193     }
1194 
1195     /**
1196      * API to get the status of all ring buffers supported by driver
1197      */
getRingBufferStatus()1198     public WifiNative.RingBufferStatus[] getRingBufferStatus() {
1199         synchronized (sLock) {
1200             if (mWifiChip == null) return null;
1201             List<WifiNative.RingBufferStatus> statusList = mWifiChip.getDebugRingBuffersStatus();
1202             if (statusList == null) return null;
1203 
1204             WifiNative.RingBufferStatus[] statusArray =
1205                     new WifiNative.RingBufferStatus[statusList.size()];
1206             statusList.toArray(statusArray);
1207             return statusArray;
1208         }
1209     }
1210 
1211     /**
1212      * Indicates to driver that all the data has to be uploaded urgently
1213      */
getRingBufferData(String ringName)1214     public boolean getRingBufferData(String ringName) {
1215         enter("ringName %").c(ringName).flush();
1216         synchronized (sLock) {
1217             if (mWifiChip == null) return false;
1218             return mWifiChip.forceDumpToDebugRingBuffer(ringName);
1219         }
1220     }
1221 
1222     /**
1223      * request hal to flush ring buffers to files
1224      */
flushRingBufferData()1225     public boolean flushRingBufferData() {
1226         synchronized (sLock) {
1227             if (mWifiChip == null) return false;
1228             return mWifiChip.flushRingBufferToFile();
1229         }
1230     }
1231 
1232     /**
1233      * Request vendor debug info from the firmware
1234      */
getFwMemoryDump()1235     public byte[] getFwMemoryDump() {
1236         synchronized (sLock) {
1237             if (mWifiChip == null) return null;
1238             return mWifiChip.requestFirmwareDebugDump();
1239         }
1240     }
1241 
1242     /**
1243      * Request vendor debug info from the driver
1244      */
getDriverStateDump()1245     public byte[] getDriverStateDump() {
1246         synchronized (sLock) {
1247             if (mWifiChip == null) return (null);
1248             return mWifiChip.requestDriverDebugDump();
1249         }
1250     }
1251 
1252     /**
1253      * Start packet fate monitoring
1254      * <p>
1255      * Once started, monitoring remains active until HAL is unloaded.
1256      *
1257      * @param ifaceName Name of the interface.
1258      * @return true for success
1259      */
startPktFateMonitoring(@onNull String ifaceName)1260     public boolean startPktFateMonitoring(@NonNull String ifaceName) {
1261         synchronized (sLock) {
1262             WifiStaIface iface = getStaIface(ifaceName);
1263             if (iface == null) return false;
1264             return iface.startDebugPacketFateMonitoring();
1265         }
1266     }
1267 
1268     /**
1269      * Retrieve fates of outbound packets
1270      * <p>
1271      * Reports the outbound frames for the most recent association (space allowing).
1272      *
1273      * @param ifaceName Name of the interface.
1274      * @return list of TxFateReports up to size {@link WifiLoggerHal#MAX_FATE_LOG_LEN}, or empty
1275      * list on failure.
1276      */
getTxPktFates(@onNull String ifaceName)1277     public List<TxFateReport> getTxPktFates(@NonNull String ifaceName) {
1278         synchronized (sLock) {
1279             WifiStaIface iface = getStaIface(ifaceName);
1280             if (iface == null) return new ArrayList<>();
1281             return iface.getDebugTxPacketFates();
1282         }
1283     }
1284 
1285     /**
1286      * Retrieve fates of inbound packets
1287      * <p>
1288      * Reports the inbound frames for the most recent association (space allowing).
1289      *
1290      * @param ifaceName Name of the interface.
1291      * @return list of RxFateReports up to size {@link WifiLoggerHal#MAX_FATE_LOG_LEN}, or empty
1292      * list on failure.
1293      */
getRxPktFates(@onNull String ifaceName)1294     public List<RxFateReport> getRxPktFates(@NonNull String ifaceName) {
1295         synchronized (sLock) {
1296             WifiStaIface iface = getStaIface(ifaceName);
1297             if (iface == null) return new ArrayList<>();
1298             return iface.getDebugRxPacketFates();
1299         }
1300     }
1301 
1302     /**
1303      * Start sending the specified keep alive packets periodically.
1304      *
1305      * @param ifaceName Name of the interface.
1306      * @param slot Command ID to use for this invocation.
1307      * @param srcAddr Source MAC address of the packet.
1308      * @param dstAddr Destination MAC address of the packet.
1309      * @param packet IP packet contents to be transmitted.
1310      * @param protocol Ether type to be set in the ethernet frame transmitted.
1311      * @param periodInMs Interval at which this packet must be transmitted.
1312      * @return 0 for success, -1 for error
1313      */
startSendingOffloadedPacket( @onNull String ifaceName, int slot, byte[] srcAddr, byte[] dstAddr, byte[] packet, int protocol, int periodInMs)1314     public int startSendingOffloadedPacket(
1315             @NonNull String ifaceName, int slot, byte[] srcAddr, byte[] dstAddr,
1316             byte[] packet, int protocol, int periodInMs) {
1317         enter("slot=% periodInMs=%").c(slot).c(periodInMs).flush();
1318         synchronized (sLock) {
1319             WifiStaIface iface = getStaIface(ifaceName);
1320             if (iface == null) return -1;
1321             MacAddress srcMac, dstMac;
1322             try {
1323                 srcMac = MacAddress.fromBytes(srcAddr);
1324                 dstMac = MacAddress.fromBytes(dstAddr);
1325             } catch (IllegalArgumentException e) {
1326                 mLog.info("Invalid MacAddress in startSendingOffloadedPacket").flush();
1327                 return -1;
1328             }
1329             boolean success = iface.startSendingKeepAlivePackets(
1330                         slot,
1331                         packet,
1332                         (short) protocol,
1333                         srcMac,
1334                         dstMac,
1335                         periodInMs);
1336             return success ? 0 : -1;
1337         }
1338     }
1339 
1340     /**
1341      * Stop sending the specified keep alive packets.
1342      *
1343      * @param ifaceName Name of the interface.
1344      * @param slot id - same as startSendingOffloadedPacket call.
1345      * @return 0 for success, -1 for error
1346      */
stopSendingOffloadedPacket(@onNull String ifaceName, int slot)1347     public int stopSendingOffloadedPacket(@NonNull String ifaceName, int slot) {
1348         enter("slot=%").c(slot).flush();
1349 
1350         synchronized (sLock) {
1351             WifiStaIface iface = getStaIface(ifaceName);
1352             if (iface == null) return -1;
1353             boolean success = iface.stopSendingKeepAlivePackets(slot);
1354             return success ? 0 : -1;
1355         }
1356     }
1357 
1358     /**
1359      * A fixed cmdId for our RssiMonitoring (we only do one at a time)
1360      */
1361     @VisibleForTesting
1362     static final int sRssiMonCmdId = 7551;
1363 
1364     /**
1365      * Our client's handler
1366      */
1367     private WifiNative.WifiRssiEventHandler mWifiRssiEventHandler;
1368 
1369     /**
1370      * Start RSSI monitoring on the currently connected access point.
1371      *
1372      * @param ifaceName        Name of the interface.
1373      * @param maxRssi          Maximum RSSI threshold.
1374      * @param minRssi          Minimum RSSI threshold.
1375      * @param rssiEventHandler Called when RSSI goes above maxRssi or below minRssi
1376      * @return 0 for success, -1 for failure
1377      */
startRssiMonitoring(@onNull String ifaceName, byte maxRssi, byte minRssi, WifiNative.WifiRssiEventHandler rssiEventHandler)1378     public int startRssiMonitoring(@NonNull String ifaceName, byte maxRssi, byte minRssi,
1379                                    WifiNative.WifiRssiEventHandler rssiEventHandler) {
1380         enter("maxRssi=% minRssi=%").c(maxRssi).c(minRssi).flush();
1381         if (maxRssi <= minRssi) return -1;
1382         if (rssiEventHandler == null) return -1;
1383         synchronized (sLock) {
1384             WifiStaIface iface = getStaIface(ifaceName);
1385             if (iface == null) return -1;
1386             iface.stopRssiMonitoring(sRssiMonCmdId);
1387             if (!iface.startRssiMonitoring(sRssiMonCmdId, maxRssi, minRssi)) return -1;
1388             mWifiRssiEventHandler = rssiEventHandler;
1389             return 0;
1390         }
1391     }
1392 
1393     /**
1394      * Stop RSSI monitoring
1395      *
1396      * @param ifaceName Name of the interface.
1397      * @return 0 for success, -1 for failure
1398      */
stopRssiMonitoring(@onNull String ifaceName)1399     public int stopRssiMonitoring(@NonNull String ifaceName) {
1400         synchronized (sLock) {
1401             mWifiRssiEventHandler = null;
1402             WifiStaIface iface = getStaIface(ifaceName);
1403             if (iface == null) return -1;
1404             boolean success = iface.stopRssiMonitoring(sRssiMonCmdId);
1405             return success ? 0 : -1;
1406         }
1407     }
1408 
1409     /**
1410      * Fetch the host wakeup reasons stats from wlan driver.
1411      *
1412      * @return the |WlanWakeReasonAndCounts| from the wlan driver, or null on failure.
1413      */
getWlanWakeReasonCount()1414     public WlanWakeReasonAndCounts getWlanWakeReasonCount() {
1415         synchronized (sLock) {
1416             if (mWifiChip == null) return null;
1417             return mWifiChip.getDebugHostWakeReasonStats();
1418         }
1419     }
1420 
1421     /**
1422      * Enable/Disable Neighbour discovery offload functionality in the firmware.
1423      *
1424      * @param ifaceName Name of the interface.
1425      * @param enabled true to enable, false to disable.
1426      * @return true for success, false for failure
1427      */
configureNeighborDiscoveryOffload(@onNull String ifaceName, boolean enabled)1428     public boolean configureNeighborDiscoveryOffload(@NonNull String ifaceName, boolean enabled) {
1429         enter("enabled=%").c(enabled).flush();
1430         synchronized (sLock) {
1431             WifiStaIface iface = getStaIface(ifaceName);
1432             if (iface == null) return false;
1433             return iface.enableNdOffload(enabled);
1434         }
1435     }
1436 
1437     // Firmware roaming control.
1438 
1439     /**
1440      * Query the firmware roaming capabilities.
1441      *
1442      * @param ifaceName Name of the interface.
1443      * @return capabilities object on success, null otherwise.
1444      */
1445     @Nullable
getRoamingCapabilities(@onNull String ifaceName)1446     public WifiNative.RoamingCapabilities getRoamingCapabilities(@NonNull String ifaceName) {
1447         synchronized (sLock) {
1448             WifiStaIface iface = getStaIface(ifaceName);
1449             if (iface == null) return null;
1450             return iface.getRoamingCapabilities();
1451         }
1452     }
1453 
1454     /**
1455      * Enable/disable firmware roaming.
1456      *
1457      * @param ifaceName Name of the interface.
1458      * @param state the intended roaming state
1459      * @return SET_FIRMWARE_ROAMING_SUCCESS, SET_FIRMWARE_ROAMING_FAILURE,
1460      *         or SET_FIRMWARE_ROAMING_BUSY
1461      */
enableFirmwareRoaming(@onNull String ifaceName, @WifiNative.RoamingEnableState int state)1462     public @WifiNative.RoamingEnableStatus int enableFirmwareRoaming(@NonNull String ifaceName,
1463             @WifiNative.RoamingEnableState int state) {
1464         synchronized (sLock) {
1465             WifiStaIface iface = getStaIface(ifaceName);
1466             if (iface == null) return WifiNative.SET_FIRMWARE_ROAMING_FAILURE;
1467             return iface.setRoamingState(state);
1468         }
1469     }
1470 
1471     /**
1472      * Set firmware roaming configurations.
1473      *
1474      * @param ifaceName Name of the interface.
1475      * @param config new roaming configuration object
1476      * @return true for success; false for failure
1477      */
configureRoaming(@onNull String ifaceName, WifiNative.RoamingConfig config)1478     public boolean configureRoaming(@NonNull String ifaceName, WifiNative.RoamingConfig config) {
1479         synchronized (sLock) {
1480             WifiStaIface iface = getStaIface(ifaceName);
1481             if (iface == null) return false;
1482             try {
1483                 // parse the blocklist BSSIDs if any
1484                 List<MacAddress> bssidBlocklist = new ArrayList<>();
1485                 if (config.blocklistBssids != null) {
1486                     for (String bssid : config.blocklistBssids) {
1487                         bssidBlocklist.add(MacAddress.fromString(bssid));
1488                     }
1489                 }
1490 
1491                 // parse the allowlist SSIDs if any
1492                 List<byte[]> ssidAllowlist = new ArrayList<>();
1493                 if (config.allowlistSsids != null) {
1494                     for (String ssidStr : config.allowlistSsids) {
1495                         for (WifiSsid originalSsid : mSsidTranslator.getAllPossibleOriginalSsids(
1496                                 WifiSsid.fromString(ssidStr))) {
1497                             // HIDL code is throwing InvalidArgumentException when ssidWhitelist has
1498                             // SSIDs with less than 32 byte length this is due to HAL definition of
1499                             // SSID declared it as 32-byte fixed length array. Thus pad additional
1500                             // bytes with 0's to pass SSIDs as byte arrays of 32 length
1501                             ssidAllowlist.add(
1502                                     Arrays.copyOf(originalSsid.getBytes(), 32));
1503                         }
1504                     }
1505                 }
1506 
1507                 return iface.configureRoaming(bssidBlocklist, ssidAllowlist);
1508             } catch (IllegalArgumentException e) {
1509                 mLog.err("Illegal argument for roaming configuration").c(e.toString()).flush();
1510                 return false;
1511             }
1512         }
1513     }
1514 
1515     /**
1516      * Select one of the pre-configured TX power level scenarios or reset it back to normal.
1517      * Primarily used for meeting SAR requirements during voice calls.
1518      *
1519      * Note: If it was found out that the scenario to be reported is the same as last reported one,
1520      *       then exit with success.
1521      *       This is to handle the case when some HAL versions deal with different inputs equally,
1522      *       in that case, we should not call the hal unless there is a change in scenario.
1523      * Note: It is assumed that this method is only called if SAR is enabled. The logic of whether
1524      *       to call it or not resides in SarManager class.
1525      *
1526      * @param sarInfo The collection of inputs to select the SAR scenario.
1527      * @return true for success; false for failure or if the HAL version does not support this API.
1528      */
selectTxPowerScenario(SarInfo sarInfo)1529     public boolean selectTxPowerScenario(SarInfo sarInfo) {
1530         synchronized (sLock) {
1531             if (mWifiChip == null) return false;
1532             return mWifiChip.selectTxPowerScenario(sarInfo);
1533         }
1534     }
1535 
1536     /**
1537      * Enable/Disable low-latency mode
1538      *
1539      * @param enabled true to enable low-latency mode, false to disable it
1540      */
setLowLatencyMode(boolean enabled)1541     public boolean setLowLatencyMode(boolean enabled) {
1542         synchronized (sLock) {
1543             if (mWifiChip == null) return false;
1544             return mWifiChip.setLowLatencyMode(enabled);
1545         }
1546     }
1547 
1548     /**
1549      * Returns whether the given HdmIfaceTypeForCreation combo is supported or not.
1550      */
canDeviceSupportCreateTypeCombo(SparseArray<Integer> combo)1551     public boolean canDeviceSupportCreateTypeCombo(SparseArray<Integer> combo) {
1552         synchronized (sLock) {
1553             return mHalDeviceManager.canDeviceSupportCreateTypeCombo(combo);
1554         }
1555     }
1556 
1557     /**
1558      * Returns whether a new iface can be created without tearing down any existing ifaces.
1559      */
canDeviceSupportAdditionalIface( @alDeviceManager.HdmIfaceTypeForCreation int createIfaceType, @NonNull WorkSource requestorWs)1560     public boolean canDeviceSupportAdditionalIface(
1561             @HalDeviceManager.HdmIfaceTypeForCreation int createIfaceType,
1562             @NonNull WorkSource requestorWs) {
1563         synchronized (sLock) {
1564             List<Pair<Integer, WorkSource>> creationImpact =
1565                     mHalDeviceManager.reportImpactToCreateIface(createIfaceType, true, requestorWs);
1566             return creationImpact != null && creationImpact.isEmpty();
1567         }
1568     }
1569 
1570     /**
1571      * Returns whether STA + AP concurrency is supported or not.
1572      */
isStaApConcurrencySupported()1573     public boolean isStaApConcurrencySupported() {
1574         synchronized (sLock) {
1575             return mHalDeviceManager.canDeviceSupportCreateTypeCombo(new SparseArray<Integer>() {{
1576                     put(HDM_CREATE_IFACE_STA, 1);
1577                     put(HDM_CREATE_IFACE_AP, 1);
1578                 }});
1579         }
1580     }
1581 
1582     /**
1583      * Returns whether STA + STA concurrency is supported or not.
1584      */
1585     public boolean isStaStaConcurrencySupported() {
1586         synchronized (sLock) {
1587             return mHalDeviceManager.canDeviceSupportCreateTypeCombo(new SparseArray<Integer>() {{
1588                     put(HDM_CREATE_IFACE_STA, 2);
1589                 }});
1590         }
1591     }
1592 
1593     /**
1594      * Returns whether a new AP iface can be created or not.
1595      */
1596     public boolean isItPossibleToCreateApIface(@NonNull WorkSource requestorWs) {
1597         synchronized (sLock) {
1598             return mHalDeviceManager.isItPossibleToCreateIface(HDM_CREATE_IFACE_AP, requestorWs);
1599         }
1600     }
1601 
1602     /**
1603      * Returns whether a new AP iface can be created or not.
1604      */
1605     public boolean isItPossibleToCreateBridgedApIface(@NonNull WorkSource requestorWs) {
1606         synchronized (sLock) {
1607             return mHalDeviceManager.isItPossibleToCreateIface(
1608                     HDM_CREATE_IFACE_AP_BRIDGE, requestorWs);
1609         }
1610     }
1611 
1612     /**
1613      * Returns whether a new STA iface can be created or not.
1614      */
1615     public boolean isItPossibleToCreateStaIface(@NonNull WorkSource requestorWs) {
1616         synchronized (sLock) {
1617             return mHalDeviceManager.isItPossibleToCreateIface(HDM_CREATE_IFACE_STA, requestorWs);
1618         }
1619 
1620     }
1621     /**
1622      * Set primary connection when multiple STA ifaces are active.
1623      *
1624      * @param ifaceName Name of the interface.
1625      * @return true for success
1626      */
1627     public boolean setMultiStaPrimaryConnection(@NonNull String ifaceName) {
1628         if (TextUtils.isEmpty(ifaceName)) return false;
1629         synchronized (sLock) {
1630             if (mWifiChip == null) return false;
1631             return mWifiChip.setMultiStaPrimaryConnection(ifaceName);
1632         }
1633     }
1634 
1635     /**
1636      * Set use-case when multiple STA ifaces are active.
1637      *
1638      * @param useCase one of the use cases.
1639      * @return true for success
1640      */
1641     public boolean setMultiStaUseCase(@WifiNative.MultiStaUseCase int useCase) {
1642         synchronized (sLock) {
1643             if (mWifiChip == null) return false;
1644             return mWifiChip.setMultiStaUseCase(useCase);
1645         }
1646     }
1647 
1648     /**
1649      * Notify scan mode state to driver to save power in scan-only mode.
1650      *
1651      * @param ifaceName Name of the interface.
1652      * @param enable whether is in scan-only mode
1653      * @return true for success
1654      */
1655     public boolean setScanMode(@NonNull String ifaceName, boolean enable) {
1656         synchronized (sLock) {
1657             WifiStaIface iface = getStaIface(ifaceName);
1658             if (iface == null) return false;
1659             return iface.setScanMode(enable);
1660         }
1661     }
1662 
1663     /**
1664      * Callback for events on the STA interface.
1665      */
1666     private class StaIfaceEventCallback implements WifiStaIface.Callback {
1667         @Override
1668         public void onBackgroundScanFailure(int cmdId) {
1669             mVerboseLog.d("onBackgroundScanFailure " + cmdId);
1670             WifiNative.ScanEventHandler eventHandler;
1671             synchronized (sLock) {
1672                 if (mScan == null || cmdId != mScan.cmdId) return;
1673                 eventHandler = mScan.eventHandler;
1674             }
1675             eventHandler.onScanStatus(WifiNative.WIFI_SCAN_FAILED);
1676         }
1677 
1678         @Override
1679         public void onBackgroundFullScanResult(
1680                 int cmdId, int bucketsScanned, ScanResult result) {
1681             mVerboseLog.d("onBackgroundFullScanResult " + cmdId);
1682             WifiNative.ScanEventHandler eventHandler;
1683             synchronized (sLock) {
1684                 if (mScan == null || cmdId != mScan.cmdId) return;
1685                 eventHandler = mScan.eventHandler;
1686             }
1687             eventHandler.onFullScanResult(result, bucketsScanned);
1688         }
1689 
1690         @Override
1691         public void onBackgroundScanResults(int cmdId, WifiScanner.ScanData[] scanDatas) {
1692             mVerboseLog.d("onBackgroundScanResults " + cmdId);
1693             WifiNative.ScanEventHandler eventHandler;
1694             // WifiScanner currently uses the results callback to fetch the scan results.
1695             // So, simulate that by sending out the notification and then caching the results
1696             // locally. This will then be returned to WifiScanner via getScanResults.
1697             synchronized (sLock) {
1698                 if (mScan == null || cmdId != mScan.cmdId) return;
1699                 eventHandler = mScan.eventHandler;
1700                 mScan.latestScanResults = scanDatas;
1701             }
1702             eventHandler.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE);
1703         }
1704 
1705         @Override
1706         public void onRssiThresholdBreached(int cmdId, byte[/* 6 */] currBssid, int currRssi) {
1707             mVerboseLog.d("onRssiThresholdBreached " + cmdId + "currRssi " + currRssi);
1708             WifiNative.WifiRssiEventHandler eventHandler;
1709             synchronized (sLock) {
1710                 if (mWifiRssiEventHandler == null || cmdId != sRssiMonCmdId) return;
1711                 eventHandler = mWifiRssiEventHandler;
1712             }
1713             eventHandler.onRssiThresholdBreached((byte) currRssi);
1714         }
1715     }
1716 
1717     /**
1718      * Callback for events on the chip.
1719      */
1720     private class ChipEventCallback implements WifiChip.Callback {
1721         @Override
1722         public void onChipReconfigured(int modeId) {
1723             mVerboseLog.d("onChipReconfigured " + modeId);
1724         }
1725 
1726         @Override
1727         public void onChipReconfigureFailure(int status) {
1728             mVerboseLog.d("onChipReconfigureFailure " + status);
1729         }
1730 
1731         public void onIfaceAdded(int type, String name) {
1732             mVerboseLog.d("onIfaceAdded " + type + ", name: " + name);
1733         }
1734 
1735         @Override
1736         public void onIfaceRemoved(int type, String name) {
1737             mVerboseLog.d("onIfaceRemoved " + type + ", name: " + name);
1738         }
1739 
1740         @Override
1741         public void onDebugRingBufferDataAvailable(
1742                 WifiNative.RingBufferStatus status, byte[] data) {
1743             mHalEventHandler.post(() -> {
1744                 WifiNative.WifiLoggerEventHandler eventHandler;
1745                 synchronized (sLock) {
1746                     if (mLogEventHandler == null || status == null || data == null) return;
1747                     eventHandler = mLogEventHandler;
1748                 }
1749                 // Because |sLock| has been released, there is a chance that we'll execute
1750                 // a spurious callback (after someone has called resetLogHandler()).
1751                 //
1752                 // However, the alternative risks deadlock. Consider:
1753                 // [T1.1] WifiDiagnostics.captureBugReport()
1754                 // [T1.2] -- acquire WifiDiagnostics object's intrinsic lock
1755                 // [T1.3]    -> WifiVendorHal.getRingBufferData()
1756                 // [T1.4]       -- acquire WifiVendorHal.sLock
1757                 // [T2.1] <lambda>()
1758                 // [T2.2] -- acquire WifiVendorHal.sLock
1759                 // [T2.3]    -> WifiDiagnostics.onRingBufferData()
1760                 // [T2.4]       -- acquire WifiDiagnostics object's intrinsic lock
1761                 //
1762                 // The problem here is that the two threads acquire the locks in opposite order.
1763                 // If, for example, T2.2 executes between T1.2 and 1.4, then T1 and T2
1764                 // will be deadlocked.
1765                 int sizeBefore = data.length;
1766                 boolean conversionFailure = false;
1767                 try {
1768                     eventHandler.onRingBufferData(status, data);
1769                     int sizeAfter = data.length;
1770                     if (sizeAfter != sizeBefore) {
1771                         conversionFailure = true;
1772                     }
1773                 } catch (ArrayIndexOutOfBoundsException e) {
1774                     conversionFailure = true;
1775                 }
1776                 if (conversionFailure) {
1777                     Log.wtf("WifiVendorHal", "Conversion failure detected in "
1778                             + "onDebugRingBufferDataAvailable. "
1779                             + "The input ArrayList |data| is potentially corrupted. "
1780                             + "Starting size=" + sizeBefore + ", "
1781                             + "final size=" + data.length);
1782                 }
1783             });
1784         }
1785 
1786         @Override
1787         public void onDebugErrorAlert(int errorCode, byte[] debugData) {
1788             mLog.w("onDebugErrorAlert " + errorCode);
1789             mHalEventHandler.post(() -> {
1790                 WifiNative.WifiLoggerEventHandler eventHandler;
1791                 synchronized (sLock) {
1792                     if (mLogEventHandler == null || debugData == null) return;
1793                     eventHandler = mLogEventHandler;
1794                 }
1795                 // See comment in onDebugRingBufferDataAvailable(), for an explanation
1796                 // of why this callback is invoked without |sLock| held.
1797                 eventHandler.onWifiAlert(errorCode, debugData);
1798             });
1799         }
1800 
1801         @Override
1802         public void onRadioModeChange(List<WifiChip.RadioModeInfo> radioModeInfoList) {
1803             mVerboseLog.d("onRadioModeChange " + radioModeInfoList);
1804             WifiNative.VendorHalRadioModeChangeEventHandler handler;
1805             synchronized (sLock) {
1806                 if (mRadioModeChangeEventHandler == null || radioModeInfoList == null) return;
1807                 handler = mRadioModeChangeEventHandler;
1808             }
1809             // Should only contain 1 or 2 radio infos.
1810             if (radioModeInfoList.size() == 0 || radioModeInfoList.size() > 2) {
1811                 mLog.e("Unexpected number of radio info in list " + radioModeInfoList.size());
1812                 return;
1813             }
1814             WifiChip.RadioModeInfo radioModeInfo0 = radioModeInfoList.get(0);
1815             WifiChip.RadioModeInfo radioModeInfo1 =
1816                     radioModeInfoList.size() == 2 ? radioModeInfoList.get(1) : null;
1817             // Number of ifaces on each radio should be equal.
1818             if (radioModeInfo1 != null
1819                     && radioModeInfo0.ifaceInfos.size() != radioModeInfo1.ifaceInfos.size()) {
1820                 mLog.e("Unexpected number of iface info in list "
1821                         + radioModeInfo0.ifaceInfos.size() + ", "
1822                         + radioModeInfo1.ifaceInfos.size());
1823                 return;
1824             }
1825             int numIfacesOnEachRadio = radioModeInfo0.ifaceInfos.size();
1826             // Only 1 or 2 ifaces should be present on each radio.
1827             if (numIfacesOnEachRadio == 0 || numIfacesOnEachRadio > 2) {
1828                 mLog.e("Unexpected number of iface info in list " + numIfacesOnEachRadio);
1829                 return;
1830             }
1831             Runnable runnable = null;
1832             // 2 ifaces simultaneous on 2 radios.
1833             if (radioModeInfoList.size() == 2 && numIfacesOnEachRadio == 1) {
1834                 // Iface on radio0 should be different from the iface on radio1 for DBS & SBS.
1835                 if (areSameIfaceNames(radioModeInfo0.ifaceInfos, radioModeInfo1.ifaceInfos)) {
1836                     mLog.e("Unexpected for both radio infos to have same iface");
1837                     return;
1838                 }
1839                 if (radioModeInfo0.bandInfo != radioModeInfo1.bandInfo) {
1840                     runnable = () -> {
1841                         handler.onDbs();
1842                     };
1843                 } else {
1844                     runnable = () -> {
1845                         handler.onSbs(radioModeInfo0.bandInfo);
1846                     };
1847                 }
1848             // 2 ifaces time sharing on 1 radio.
1849             } else if (radioModeInfoList.size() == 1 && numIfacesOnEachRadio == 2) {
1850                 WifiChip.IfaceInfo ifaceInfo0 = radioModeInfo0.ifaceInfos.get(0);
1851                 WifiChip.IfaceInfo ifaceInfo1 = radioModeInfo0.ifaceInfos.get(1);
1852                 if (ifaceInfo0.channel != ifaceInfo1.channel) {
1853                     runnable = () -> {
1854                         handler.onMcc(radioModeInfo0.bandInfo);
1855                     };
1856                 } else {
1857                     runnable = () -> {
1858                         handler.onScc(radioModeInfo0.bandInfo);
1859                     };
1860                 }
1861             } else {
1862                 // Not concurrency scenario, uninteresting...
1863             }
1864             if (runnable != null) mHalEventHandler.post(runnable);
1865         }
1866     }
1867 
1868     private boolean areSameIfaceNames(List<WifiChip.IfaceInfo> ifaceList1,
1869             List<WifiChip.IfaceInfo> ifaceList2) {
1870         List<String> ifaceNamesList1 = ifaceList1
1871                 .stream()
1872                 .map(i -> i.name)
1873                 .collect(Collectors.toList());
1874         List<String> ifaceNamesList2 = ifaceList2
1875                 .stream()
1876                 .map(i -> i.name)
1877                 .collect(Collectors.toList());
1878         return ifaceNamesList1.containsAll(ifaceNamesList2);
1879     }
1880 
1881     /**
1882      * Hal Device Manager callbacks.
1883      */
1884     public class HalDeviceManagerStatusListener implements HalDeviceManager.ManagerStatusListener {
1885         @Override
1886         public void onStatusChanged() {
1887             boolean isReady = mHalDeviceManager.isReady();
1888             boolean isStarted = mHalDeviceManager.isStarted();
1889 
1890             mVerboseLog.i("Device Manager onStatusChanged. isReady(): " + isReady
1891                     + ", isStarted(): " + isStarted);
1892             if (!isReady) {
1893                 // Probably something unpleasant, e.g. the server died
1894                 WifiNative.VendorHalDeathEventHandler handler;
1895                 synchronized (sLock) {
1896                     clearState();
1897                     handler = mDeathEventHandler;
1898                 }
1899                 if (handler != null) {
1900                     handler.onDeath();
1901                 }
1902             }
1903         }
1904     }
1905 
1906     /**
1907      * Trigger subsystem restart in vendor side
1908      */
1909     public boolean startSubsystemRestart() {
1910         synchronized (sLock) {
1911             if (mWifiChip == null) return false;
1912             return mWifiChip.triggerSubsystemRestart();
1913         }
1914     }
1915 
1916     /**
1917      * Retrieve the list of usable Wifi channels.
1918      */
1919     public List<WifiAvailableChannel> getUsableChannels(
1920             @WifiScanner.WifiBand int band,
1921             @WifiAvailableChannel.OpMode int mode,
1922             @WifiAvailableChannel.Filter int filter) {
1923         synchronized (sLock) {
1924             if (mWifiChip == null) return null;
1925             return mWifiChip.getUsableChannels(band, mode, filter);
1926         }
1927     }
1928 
1929     /**
1930      * Set maximum acceptable DTIM multiplier to hardware driver. Any multiplier larger than the
1931      * maximum value must not be accepted, it will cause packet loss higher than what the system
1932      * can accept, which will cause unexpected behavior for apps, and may interrupt the network
1933      * connection.
1934      *
1935      * @param ifaceName Name of the interface.
1936      * @param multiplier integer maximum DTIM multiplier value to set.
1937      * @return true for success
1938      */
1939     public boolean setDtimMultiplier(@NonNull String ifaceName, int multiplier) {
1940         synchronized (sLock) {
1941             WifiStaIface iface = getStaIface(ifaceName);
1942             if (iface == null) return false;
1943             return iface.setDtimMultiplier(multiplier);
1944         }
1945     }
1946 
1947     /**
1948      * Set the Multi-Link Operation mode.
1949      *
1950      * @param mode Multi-Link operation mode {@link android.net.wifi.WifiManager.MloMode}.
1951      * @return {@code true} if success, otherwise {@code false}.
1952      */
1953     public @WifiStatusCode int setMloMode(@WifiManager.MloMode int mode) {
1954         synchronized (sLock) {
1955             if (mWifiChip == null) return WifiStatusCode.ERROR_WIFI_CHIP_INVALID;
1956             return mWifiChip.setMloMode(mode);
1957         }
1958     }
1959 
1960     /**
1961      * Enable/disable the feature of allowing current STA-connected channel for WFA GO, SAP and
1962      * Aware when the regulatory allows.
1963      *
1964      * @param enableIndoorChannel enable or disable indoor channel.
1965      * @param enableDfsChannel    enable or disable DFS channel.
1966      * @return true if the operation succeeded, false if there is an error in Hal.
1967      */
1968     public boolean enableStaChannelForPeerNetwork(boolean enableIndoorChannel,
1969             boolean enableDfsChannel) {
1970         synchronized (sLock) {
1971             if (mWifiChip == null) return false;
1972             return mWifiChip.enableStaChannelForPeerNetwork(enableIndoorChannel, enableDfsChannel);
1973         }
1974     }
1975 
1976     /**
1977      * See {@link WifiNative#isBandCombinationSupported(String, List)}.
1978      */
1979     public boolean isBandCombinationSupported(@NonNull String ifaceName,
1980             @NonNull List<Integer> bands) {
1981         synchronized (sLock) {
1982             WifiStaIface iface = getStaIface(ifaceName);
1983             if (iface == null) return false;
1984             return mHalDeviceManager.isBandCombinationSupported(iface, bands);
1985         }
1986     }
1987 
1988     /**
1989      * See {@link WifiNative#getSupportedBandCombinations(String)}.
1990      */
1991     public Set<List<Integer>> getSupportedBandCombinations(String ifaceName) {
1992         synchronized (sLock) {
1993             WifiStaIface iface = getStaIface(ifaceName);
1994             if (iface == null) return null;
1995             return mHalDeviceManager.getSupportedBandCombinations(iface);
1996         }
1997     }
1998 }
1999