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