/* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.wifi.hal; import static android.net.wifi.WifiUsabilityStatsEntry.LINK_STATE_UNKNOWN; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.hardware.wifi.V1_0.IWifiStaIfaceEventCallback; import android.hardware.wifi.V1_0.StaBackgroundScanBucketEventReportSchemeMask; import android.hardware.wifi.V1_0.StaBackgroundScanBucketParameters; import android.hardware.wifi.V1_0.StaBackgroundScanParameters; import android.hardware.wifi.V1_0.StaLinkLayerIfaceStats; import android.hardware.wifi.V1_0.StaLinkLayerRadioStats; import android.hardware.wifi.V1_0.StaLinkLayerStats; import android.hardware.wifi.V1_0.StaRoamingConfig; import android.hardware.wifi.V1_0.StaRoamingState; import android.hardware.wifi.V1_0.StaScanData; import android.hardware.wifi.V1_0.StaScanDataFlagMask; import android.hardware.wifi.V1_0.StaScanResult; import android.hardware.wifi.V1_0.WifiDebugPacketFateFrameType; import android.hardware.wifi.V1_0.WifiDebugRxPacketFate; import android.hardware.wifi.V1_0.WifiDebugRxPacketFateReport; import android.hardware.wifi.V1_0.WifiDebugTxPacketFate; import android.hardware.wifi.V1_0.WifiDebugTxPacketFateReport; import android.hardware.wifi.V1_0.WifiStatus; import android.hardware.wifi.V1_0.WifiStatusCode; import android.hardware.wifi.V1_5.WifiBand; import android.net.MacAddress; import android.net.apf.ApfCapabilities; import android.net.wifi.ScanResult; import android.net.wifi.WifiManager; import android.net.wifi.WifiScanner; import android.net.wifi.WifiSsid; import android.os.RemoteException; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.server.wifi.SsidTranslator; import com.android.server.wifi.WifiLinkLayerStats; import com.android.server.wifi.WifiLoggerHal; import com.android.server.wifi.WifiNative; import com.android.server.wifi.util.BitMask; import com.android.server.wifi.util.GeneralUtil; import com.android.server.wifi.util.NativeUtil; import com.android.wifi.resources.R; import java.util.ArrayList; import java.util.List; import java.util.function.Supplier; /** * HIDL implementation of the IWifiStaIface interface. */ public class WifiStaIfaceHidlImpl implements IWifiStaIface { private static final String TAG = "WifiStaIfaceHidlImpl"; private android.hardware.wifi.V1_0.IWifiStaIface mWifiStaIface; private String mIfaceName; private IWifiStaIfaceEventCallback mHalCallback; private WifiStaIface.Callback mFrameworkCallback; private Context mContext; private SsidTranslator mSsidTranslator; private static final int DEFAULT_LINK = 0; private static final int NUM_OF_LINKS = 1; private final boolean mWifiLinkLayerAllRadiosStatsAggregationEnabled; public WifiStaIfaceHidlImpl(@NonNull android.hardware.wifi.V1_0.IWifiStaIface staIface, @NonNull Context context, @NonNull SsidTranslator ssidTranslator) { mWifiStaIface = staIface; mContext = context; mSsidTranslator = ssidTranslator; mHalCallback = new StaIfaceEventCallback(); mWifiLinkLayerAllRadiosStatsAggregationEnabled = mContext.getResources() .getBoolean(R.bool.config_wifiLinkLayerAllRadiosStatsAggregationEnabled); } /** * See comments for {@link IWifiStaIface#registerFrameworkCallback(WifiStaIface.Callback)} */ public boolean registerFrameworkCallback(WifiStaIface.Callback callback) { final String methodStr = "registerFrameworkCallback"; return validateAndCall(methodStr, false, () -> registerFrameworkCallbackInternal(methodStr, callback)); } /** * See comments for {@link IWifiStaIface#getName()} */ @Nullable public String getName() { final String methodStr = "getName"; return validateAndCall(methodStr, null, () -> getNameInternal(methodStr)); } /** * See comments for {@link IWifiStaIface#configureRoaming(List, List)} */ public boolean configureRoaming(List bssidBlocklist, List ssidAllowlist) { final String methodStr = "configureRoaming"; return validateAndCall(methodStr, false, () -> configureRoamingInternal(methodStr, bssidBlocklist, ssidAllowlist)); } /** * See comments for {@link IWifiStaIface#enableLinkLayerStatsCollection(boolean)} */ public boolean enableLinkLayerStatsCollection(boolean debug) { final String methodStr = "enableLinkLayerStatsCollection"; return validateAndCall(methodStr, false, () -> enableLinkLayerStatsCollectionInternal(methodStr, debug)); } /** * See comments for {@link IWifiStaIface#enableNdOffload(boolean)} */ public boolean enableNdOffload(boolean enable) { final String methodStr = "enableNdOffload"; return validateAndCall(methodStr, false, () -> enableNdOffloadInternal(methodStr, enable)); } /** * See comments for {@link IWifiStaIface#getApfPacketFilterCapabilities()} */ public ApfCapabilities getApfPacketFilterCapabilities() { final String methodStr = "getApfPacketFilterCapabilities"; return validateAndCall(methodStr, new ApfCapabilities(0, 0, 0), () -> getApfPacketFilterCapabilitiesInternal(methodStr)); } /** * See comments for {@link IWifiStaIface#getBackgroundScanCapabilities()} */ @Nullable public WifiNative.ScanCapabilities getBackgroundScanCapabilities() { final String methodStr = "getBackgroundScanCapabilities"; return validateAndCall(methodStr, null, () -> getBackgroundScanCapabilitiesInternal(methodStr)); } /** * See comments for {@link IWifiStaIface#getCapabilities()} */ public long getCapabilities() { final String methodStr = "getCapabilities"; return validateAndCall(methodStr, 0L, () -> getCapabilitiesInternal(methodStr)); } /** * See comments for {@link IWifiStaIface#getDebugRxPacketFates()} */ public List getDebugRxPacketFates() { final String methodStr = "getDebugRxPacketFates"; return validateAndCall(methodStr, new ArrayList<>(), () -> getDebugRxPacketFatesInternal(methodStr)); } /** * See comments for {@link IWifiStaIface#getDebugTxPacketFates()} */ public List getDebugTxPacketFates() { final String methodStr = "getDebugTxPacketFates"; return validateAndCall(methodStr, new ArrayList<>(), () -> getDebugTxPacketFatesInternal(methodStr)); } /** * See comments for {@link IWifiStaIface#getFactoryMacAddress()} */ @Nullable public MacAddress getFactoryMacAddress() { final String methodStr = "getFactoryMacAddress"; return validateAndCall(methodStr, null, () -> getFactoryMacAddressInternal(methodStr)); } /** * See comments for {@link IWifiStaIface#getCachedScanData()} */ @Nullable public WifiScanner.ScanData getCachedScanData() { Log.d(TAG, "getCachedScanData is not implemented by HIDL"); return null; } /** * See comments for {@link IWifiStaIface#getLinkLayerStats()} */ @Nullable public WifiLinkLayerStats getLinkLayerStats() { final String methodStr = "getLinkLayerStats"; return validateAndCall(methodStr, null, () -> getLinkLayerStatsInternal(methodStr)); } /** * See comments for {@link IWifiStaIface#getRoamingCapabilities()} */ @Nullable public WifiNative.RoamingCapabilities getRoamingCapabilities() { final String methodStr = "getRoamingCapabilities"; return validateAndCall(methodStr, null, () -> getRoamingCapabilitiesInternal(methodStr)); } /** * See comments for {@link IWifiStaIface#installApfPacketFilter(byte[])} */ public boolean installApfPacketFilter(byte[] program) { final String methodStr = "installApfPacketFilter"; return validateAndCall(methodStr, false, () -> installApfPacketFilterInternal(methodStr, program)); } /** * See comments for {@link IWifiStaIface#readApfPacketFilterData()} */ @Nullable public byte[] readApfPacketFilterData() { final String methodStr = "readApfPacketFilterData"; return validateAndCall(methodStr, null, () -> readApfPacketFilterDataInternal(methodStr)); } /** * See comments for {@link IWifiStaIface#setMacAddress(MacAddress)} */ public boolean setMacAddress(MacAddress mac) { final String methodStr = "setMacAddress"; return validateAndCall(methodStr, false, () -> setMacAddressInternal(methodStr, mac)); } /** * See comments for {@link IWifiStaIface#setRoamingState(int)} */ public @WifiNative.RoamingEnableStatus int setRoamingState( @WifiNative.RoamingEnableState int state) { final String methodStr = "setRoamingState"; return validateAndCall(methodStr, WifiNative.SET_FIRMWARE_ROAMING_FAILURE, () -> setRoamingStateInternal(methodStr, state)); } /** * See comments for {@link IWifiStaIface#setScanMode(boolean)} */ public boolean setScanMode(boolean enable) { final String methodStr = "setScanMode"; return validateAndCall(methodStr, false, () -> setScanModeInternal(methodStr, enable)); } /** * See comments for * {@link IWifiStaIface#startBackgroundScan(int, WifiStaIface.StaBackgroundScanParameters)} */ public boolean startBackgroundScan(int cmdId, WifiStaIface.StaBackgroundScanParameters params) { final String methodStr = "startBackgroundScan"; return validateAndCall(methodStr, false, () -> startBackgroundScanInternal(methodStr, cmdId, params)); } /** * See comments for {@link IWifiStaIface#startDebugPacketFateMonitoring()} */ public boolean startDebugPacketFateMonitoring() { final String methodStr = "startDebugPacketFateMonitoring"; return validateAndCall(methodStr, false, () -> startDebugPacketFateMonitoringInternal(methodStr)); } /** * See comments for * {@link IWifiStaIface#startRssiMonitoring(int, int, int)} */ public boolean startRssiMonitoring(int cmdId, int maxRssi, int minRssi) { final String methodStr = "startRssiMonitoring"; return validateAndCall(methodStr, false, () -> startRssiMonitoringInternal(methodStr, cmdId, maxRssi, minRssi)); } /** * See comments for {@link IWifiStaIface#startSendingKeepAlivePackets(int, byte[], int, * MacAddress, MacAddress, int)} */ public boolean startSendingKeepAlivePackets(int cmdId, byte[] ipPacketData, int etherType, MacAddress srcAddress, MacAddress dstAddress, int periodInMs) { final String methodStr = "startSendingKeepAlivePackets"; return validateAndCall(methodStr, false, () -> startSendingKeepAlivePacketsInternal(methodStr, cmdId, ipPacketData, etherType, srcAddress, dstAddress, periodInMs)); } /** * See comments for {@link IWifiStaIface#stopBackgroundScan(int)} */ public boolean stopBackgroundScan(int cmdId) { final String methodStr = "stopBackgroundScan"; return validateAndCall(methodStr, false, () -> stopBackgroundScanInternal(methodStr, cmdId)); } /** * See comments for {@link IWifiStaIface#stopRssiMonitoring(int)} */ public boolean stopRssiMonitoring(int cmdId) { final String methodStr = "stopRssiMonitoring"; return validateAndCall(methodStr, false, () -> stopRssiMonitoringInternal(methodStr, cmdId)); } /** * See comments for {@link IWifiStaIface#stopSendingKeepAlivePackets(int)} */ public boolean stopSendingKeepAlivePackets(int cmdId) { final String methodStr = "stopSendingKeepAlivePackets"; return validateAndCall(methodStr, false, () -> stopSendingKeepAlivePacketsInternal(methodStr, cmdId)); } /** * See comments for {@link IWifiStaIface#setDtimMultiplier(int)} */ public boolean setDtimMultiplier(int multiplier) { Log.d(TAG, "setDtimMultiplier is not implemented by HIDL"); return false; } /** * See comments for {@link IWifiStaIface#setRoamingMode(int)} */ public int setRoamingMode(int roamingMode) { Log.d(TAG, "setRoamingMode is not implemented by HIDL"); return 0; } // Internal Implementations private boolean registerFrameworkCallbackInternal(String methodStr, WifiStaIface.Callback callback) { if (mFrameworkCallback != null) { Log.e(TAG, "Framework callback is already registered"); return false; } else if (callback == null) { Log.e(TAG, "Cannot register a null callback"); return false; } try { WifiStatus status = mWifiStaIface.registerEventCallback(mHalCallback); if (!isOk(status, methodStr)) return false; mFrameworkCallback = callback; return true; } catch (RemoteException e) { handleRemoteException(e, methodStr); return false; } } private String getNameInternal(String methodStr) { if (mIfaceName != null) return mIfaceName; GeneralUtil.Mutable nameResp = new GeneralUtil.Mutable<>(); try { mWifiStaIface.getName((WifiStatus status, String name) -> { if (isOk(status, methodStr)) { nameResp.value = name; mIfaceName = name; } }); } catch (RemoteException e) { handleRemoteException(e, methodStr); } return nameResp.value; } private boolean configureRoamingInternal(String methodStr, List bssidBlocklist, List ssidAllowlist) { StaRoamingConfig config = new StaRoamingConfig(); config.ssidWhitelist = new ArrayList<>(ssidAllowlist); config.bssidBlacklist = new ArrayList<>(); for (MacAddress bssid : bssidBlocklist) { config.bssidBlacklist.add(bssid.toByteArray()); } try { WifiStatus status = mWifiStaIface.configureRoaming(config); return isOk(status, methodStr); } catch (RemoteException e) { handleRemoteException(e, methodStr); return false; } } private boolean enableLinkLayerStatsCollectionInternal(String methodStr, boolean debug) { try { WifiStatus status = mWifiStaIface.enableLinkLayerStatsCollection(debug); return isOk(status, methodStr); } catch (RemoteException e) { handleRemoteException(e, methodStr); return false; } } private boolean enableNdOffloadInternal(String methodStr, boolean enable) { try { WifiStatus status = mWifiStaIface.enableNdOffload(enable); return isOk(status, methodStr); } catch (RemoteException e) { handleRemoteException(e, methodStr); return false; } } private ApfCapabilities getApfPacketFilterCapabilitiesInternal(String methodStr) { GeneralUtil.Mutable apfResp = new GeneralUtil.Mutable<>(new ApfCapabilities(0, 0, 0)); try { mWifiStaIface.getApfPacketFilterCapabilities((status, caps) -> { if (isOk(status, methodStr)) { apfResp.value = new ApfCapabilities( /* apfVersionSupported */ caps.version, /* maximumApfProgramSize */ caps.maxLength, /* apfPacketFormat */ android.system.OsConstants.ARPHRD_ETHER); } }); } catch (RemoteException e) { handleRemoteException(e, methodStr); } return apfResp.value; } private WifiNative.ScanCapabilities getBackgroundScanCapabilitiesInternal(String methodStr) { GeneralUtil.Mutable scanResp = new GeneralUtil.Mutable<>(); try { mWifiStaIface.getBackgroundScanCapabilities((status, caps) -> { if (isOk(status, methodStr)) { WifiNative.ScanCapabilities out = new WifiNative.ScanCapabilities(); out.max_scan_cache_size = caps.maxCacheSize; out.max_ap_cache_per_scan = caps.maxApCachePerScan; out.max_scan_buckets = caps.maxBuckets; out.max_rssi_sample_size = 0; out.max_scan_reporting_threshold = caps.maxReportingThreshold; scanResp.value = out; } }); } catch (RemoteException e) { handleRemoteException(e, methodStr); } return scanResp.value; } private long getCapabilitiesInternal(String methodStr) { GeneralUtil.Mutable capsResp = new GeneralUtil.Mutable<>(0L); try { mWifiStaIface.getCapabilities((status, caps) -> { if (isOk(status, methodStr)) { capsResp.value = halToFrameworkStaIfaceCapability(caps); } }); } catch (RemoteException e) { handleRemoteException(e, methodStr); } return capsResp.value; } private List getDebugRxPacketFatesInternal(String methodStr) { try { List reportBufs = new ArrayList<>(); mWifiStaIface.getDebugRxPacketFates((status, fates) -> { if (!isOk(status, methodStr)) return; for (WifiDebugRxPacketFateReport fate : fates) { if (reportBufs.size() >= WifiLoggerHal.MAX_FATE_LOG_LEN) break; byte code = halToFrameworkRxPktFate(fate.fate); long us = fate.frameInfo.driverTimestampUsec; byte type = halToFrameworkPktFateFrameType(fate.frameInfo.frameType); byte[] frame = NativeUtil.byteArrayFromArrayList( fate.frameInfo.frameContent); reportBufs.add(new WifiNative.RxFateReport(code, us, type, frame)); } }); return reportBufs; } catch (RemoteException e) { handleRemoteException(e, methodStr); return new ArrayList<>(); } } private List getDebugTxPacketFatesInternal(String methodStr) { try { List reportBufs = new ArrayList<>(); mWifiStaIface.getDebugTxPacketFates((status, fates) -> { if (!isOk(status, methodStr)) return; for (WifiDebugTxPacketFateReport fate : fates) { if (reportBufs.size() >= WifiLoggerHal.MAX_FATE_LOG_LEN) break; byte code = halToFrameworkTxPktFate(fate.fate); long us = fate.frameInfo.driverTimestampUsec; byte type = halToFrameworkPktFateFrameType(fate.frameInfo.frameType); byte[] frame = NativeUtil.byteArrayFromArrayList( fate.frameInfo.frameContent); reportBufs.add(new WifiNative.TxFateReport(code, us, type, frame)); } }); return reportBufs; } catch (RemoteException e) { handleRemoteException(e, methodStr); return new ArrayList<>(); } } private MacAddress getFactoryMacAddressInternal(String methodStr) { GeneralUtil.Mutable macResp = new GeneralUtil.Mutable<>(); try { android.hardware.wifi.V1_3.IWifiStaIface sta13 = getWifiStaIfaceV1_3Mockable(); if (sta13 == null) return null; sta13.getFactoryMacAddress((status, macBytes) -> { if (isOk(status, methodStr)) { macResp.value = MacAddress.fromBytes(macBytes); } }); } catch (RemoteException e) { handleRemoteException(e, methodStr); } return macResp.value; } private WifiLinkLayerStats getLinkLayerStatsInternal(String methodStr) { if (getWifiStaIfaceV1_6Mockable() != null) { return getLinkLayerStatsV1_6Internal(methodStr); } else if (getWifiStaIfaceV1_5Mockable() != null) { return getLinkLayerStatsV1_5Internal(methodStr); } else if (getWifiStaIfaceV1_3Mockable() != null) { return getLinkLayerStatsV1_3Internal(methodStr); } else { return getLinkLayerStatsV1_0Internal(methodStr); } } private WifiLinkLayerStats getLinkLayerStatsV1_0Internal(String methodStr) { final String methodStrV1_0 = methodStr + "V1_0"; GeneralUtil.Mutable statsResp = new GeneralUtil.Mutable<>(); try { mWifiStaIface.getLinkLayerStats((status, stats) -> { if (isOk(status, methodStrV1_0)) { statsResp.value = stats; } }); } catch (RemoteException e) { handleRemoteException(e, methodStrV1_0); return null; } return frameworkFromHalLinkLayerStats(statsResp.value); } private WifiLinkLayerStats getLinkLayerStatsV1_3Internal(String methodStr) { final String methodStrV1_3 = methodStr + "V1_3"; GeneralUtil.Mutable statsResp = new GeneralUtil.Mutable<>(); try { android.hardware.wifi.V1_3.IWifiStaIface iface13 = getWifiStaIfaceV1_3Mockable(); if (iface13 == null) return null; iface13.getLinkLayerStats_1_3((status, stats) -> { if (isOk(status, methodStrV1_3)) { statsResp.value = stats; } }); } catch (RemoteException e) { handleRemoteException(e, methodStrV1_3); return null; } return frameworkFromHalLinkLayerStats_1_3(statsResp.value); } private WifiLinkLayerStats getLinkLayerStatsV1_5Internal(String methodStr) { final String methodStrV1_5 = methodStr + "V1_5"; GeneralUtil.Mutable statsResp = new GeneralUtil.Mutable<>(); try { android.hardware.wifi.V1_5.IWifiStaIface iface15 = getWifiStaIfaceV1_5Mockable(); if (iface15 == null) return null; iface15.getLinkLayerStats_1_5((status, stats) -> { if (isOk(status, methodStrV1_5)) { statsResp.value = stats; } }); } catch (RemoteException e) { handleRemoteException(e, methodStrV1_5); return null; } return frameworkFromHalLinkLayerStats_1_5(statsResp.value); } private WifiLinkLayerStats getLinkLayerStatsV1_6Internal(String methodStr) { final String methodStrV1_6 = methodStr + "V1_6"; GeneralUtil.Mutable statsResp = new GeneralUtil.Mutable<>(); try { android.hardware.wifi.V1_6.IWifiStaIface iface16 = getWifiStaIfaceV1_6Mockable(); if (iface16 == null) return null; iface16.getLinkLayerStats_1_6((status, stats) -> { if (isOk(status, methodStrV1_6)) { statsResp.value = stats; } }); } catch (RemoteException e) { handleRemoteException(e, methodStrV1_6); return null; } catch (Exception e) { handleException(e, methodStrV1_6); return null; } return frameworkFromHalLinkLayerStats_1_6(statsResp.value); } private WifiNative.RoamingCapabilities getRoamingCapabilitiesInternal(String methodStr) { GeneralUtil.Mutable capsResp = new GeneralUtil.Mutable<>(); try { mWifiStaIface.getRoamingCapabilities((status, caps) -> { if (isOk(status, methodStr)) { WifiNative.RoamingCapabilities out = new WifiNative.RoamingCapabilities(); out.maxBlocklistSize = caps.maxBlacklistSize; out.maxAllowlistSize = caps.maxWhitelistSize; capsResp.value = out; } }); } catch (RemoteException e) { handleRemoteException(e, methodStr); } return capsResp.value; } private boolean installApfPacketFilterInternal(String methodStr, byte[] program) { try { int cmdId = 0; // We only aspire to support one program at a time. ArrayList filter = NativeUtil.byteArrayToArrayList(program); WifiStatus status = mWifiStaIface.installApfPacketFilter(cmdId, filter); return isOk(status, methodStr); } catch (RemoteException e) { handleRemoteException(e, methodStr); return false; } } private byte[] readApfPacketFilterDataInternal(String methodStr) { GeneralUtil.Mutable dataResp = new GeneralUtil.Mutable<>(); try { android.hardware.wifi.V1_2.IWifiStaIface iface12 = getWifiStaIfaceV1_2Mockable(); if (iface12 == null) return null; iface12.readApfPacketFilterData((status, data) -> { if (isOk(status, methodStr)) { dataResp.value = NativeUtil.byteArrayFromArrayList(data); } }); } catch (RemoteException e) { handleRemoteException(e, methodStr); } return dataResp.value; } private boolean setMacAddressInternal(String methodStr, MacAddress mac) { try { android.hardware.wifi.V1_2.IWifiStaIface iface12 = getWifiStaIfaceV1_2Mockable(); if (iface12 == null) return false; byte[] macBytes = mac.toByteArray(); WifiStatus status = iface12.setMacAddress(macBytes); return isOk(status, methodStr); } catch (RemoteException e) { handleRemoteException(e, methodStr); return false; } } private @WifiNative.RoamingEnableStatus int setRoamingStateInternal(String methodStr, @WifiNative.RoamingEnableState int state) { byte halState = frameworkToHalStaRoamingState(state); if (halState == -1) { return WifiStaIface.SET_ROAMING_STATE_FAILURE_CODE; } try { WifiStatus status = mWifiStaIface.setRoamingState(halState); if (isOk(status, methodStr)) { return WifiNative.SET_FIRMWARE_ROAMING_SUCCESS; } else if (status.code == WifiStatusCode.ERROR_BUSY) { return WifiNative.SET_FIRMWARE_ROAMING_BUSY; } else { return WifiStaIface.SET_ROAMING_STATE_FAILURE_CODE; } } catch (RemoteException e) { handleRemoteException(e, methodStr); return WifiStaIface.SET_ROAMING_STATE_FAILURE_CODE; } } private boolean setScanModeInternal(String methodStr, boolean enable) { try { android.hardware.wifi.V1_5.IWifiStaIface iface15 = getWifiStaIfaceV1_5Mockable(); if (iface15 == null) return false; WifiStatus status = iface15.setScanMode(enable); return isOk(status, methodStr); } catch (RemoteException e) { handleRemoteException(e, methodStr); return false; } } private boolean startBackgroundScanInternal(String methodStr, int cmdId, WifiStaIface.StaBackgroundScanParameters params) { try { StaBackgroundScanParameters halParams = frameworkToHalBackgroundScanParams(params); WifiStatus status = mWifiStaIface.startBackgroundScan(cmdId, halParams); return isOk(status, methodStr); } catch (RemoteException e) { handleRemoteException(e, methodStr); return false; } } private boolean startDebugPacketFateMonitoringInternal(String methodStr) { try { WifiStatus status = mWifiStaIface.startDebugPacketFateMonitoring(); return isOk(status, methodStr); } catch (RemoteException e) { handleRemoteException(e, methodStr); return false; } } private boolean startRssiMonitoringInternal(String methodStr, int cmdId, int maxRssi, int minRssi) { try { WifiStatus status = mWifiStaIface.startRssiMonitoring(cmdId, maxRssi, minRssi); return isOk(status, methodStr); } catch (RemoteException e) { handleRemoteException(e, methodStr); return false; } } private boolean startSendingKeepAlivePacketsInternal(String methodStr, int cmdId, byte[] ipPacketData, int etherType, MacAddress srcAddress, MacAddress dstAddress, int periodInMs) { try { ArrayList data = NativeUtil.byteArrayToArrayList(ipPacketData); byte[] srcMac = srcAddress.toByteArray(); byte[] dstMac = dstAddress.toByteArray(); WifiStatus status = mWifiStaIface.startSendingKeepAlivePackets( cmdId, data, (short) etherType, srcMac, dstMac, periodInMs); return isOk(status, methodStr); } catch (RemoteException e) { handleRemoteException(e, methodStr); return false; } } private boolean stopBackgroundScanInternal(String methodStr, int cmdId) { try { WifiStatus status = mWifiStaIface.stopBackgroundScan(cmdId); return isOk(status, methodStr); } catch (RemoteException e) { handleRemoteException(e, methodStr); return false; } } private boolean stopRssiMonitoringInternal(String methodStr, int cmdId) { try { WifiStatus status = mWifiStaIface.stopRssiMonitoring(cmdId); return isOk(status, methodStr); } catch (RemoteException e) { handleRemoteException(e, methodStr); return false; } } private boolean stopSendingKeepAlivePacketsInternal(String methodStr, int cmdId) { try { WifiStatus status = mWifiStaIface.stopSendingKeepAlivePackets(cmdId); return isOk(status, methodStr); } catch (RemoteException e) { handleRemoteException(e, methodStr); return false; } } // Helper Functions private static byte frameworkToHalStaRoamingState(@WifiNative.RoamingEnableState int state) { switch (state) { case WifiNative.DISABLE_FIRMWARE_ROAMING: return StaRoamingState.DISABLED; case WifiNative.ENABLE_FIRMWARE_ROAMING: return StaRoamingState.ENABLED; default: Log.e(TAG, "Invalid firmware roaming state enum: " + state); return -1; } } private static byte halToFrameworkPktFateFrameType(int type) { switch (type) { case WifiDebugPacketFateFrameType.UNKNOWN: return WifiLoggerHal.FRAME_TYPE_UNKNOWN; case WifiDebugPacketFateFrameType.ETHERNET_II: return WifiLoggerHal.FRAME_TYPE_ETHERNET_II; case WifiDebugPacketFateFrameType.MGMT_80211: return WifiLoggerHal.FRAME_TYPE_80211_MGMT; default: throw new IllegalArgumentException("bad " + type); } } private static byte halToFrameworkRxPktFate(int type) { switch (type) { case WifiDebugRxPacketFate.SUCCESS: return WifiLoggerHal.RX_PKT_FATE_SUCCESS; case WifiDebugRxPacketFate.FW_QUEUED: return WifiLoggerHal.RX_PKT_FATE_FW_QUEUED; case WifiDebugRxPacketFate.FW_DROP_FILTER: return WifiLoggerHal.RX_PKT_FATE_FW_DROP_FILTER; case WifiDebugRxPacketFate.FW_DROP_INVALID: return WifiLoggerHal.RX_PKT_FATE_FW_DROP_INVALID; case WifiDebugRxPacketFate.FW_DROP_NOBUFS: return WifiLoggerHal.RX_PKT_FATE_FW_DROP_NOBUFS; case WifiDebugRxPacketFate.FW_DROP_OTHER: return WifiLoggerHal.RX_PKT_FATE_FW_DROP_OTHER; case WifiDebugRxPacketFate.DRV_QUEUED: return WifiLoggerHal.RX_PKT_FATE_DRV_QUEUED; case WifiDebugRxPacketFate.DRV_DROP_FILTER: return WifiLoggerHal.RX_PKT_FATE_DRV_DROP_FILTER; case WifiDebugRxPacketFate.DRV_DROP_INVALID: return WifiLoggerHal.RX_PKT_FATE_DRV_DROP_INVALID; case WifiDebugRxPacketFate.DRV_DROP_NOBUFS: return WifiLoggerHal.RX_PKT_FATE_DRV_DROP_NOBUFS; case WifiDebugRxPacketFate.DRV_DROP_OTHER: return WifiLoggerHal.RX_PKT_FATE_DRV_DROP_OTHER; default: throw new IllegalArgumentException("bad " + type); } } private static byte halToFrameworkTxPktFate(int type) { switch (type) { case WifiDebugTxPacketFate.ACKED: return WifiLoggerHal.TX_PKT_FATE_ACKED; case WifiDebugTxPacketFate.SENT: return WifiLoggerHal.TX_PKT_FATE_SENT; case WifiDebugTxPacketFate.FW_QUEUED: return WifiLoggerHal.TX_PKT_FATE_FW_QUEUED; case WifiDebugTxPacketFate.FW_DROP_INVALID: return WifiLoggerHal.TX_PKT_FATE_FW_DROP_INVALID; case WifiDebugTxPacketFate.FW_DROP_NOBUFS: return WifiLoggerHal.TX_PKT_FATE_FW_DROP_NOBUFS; case WifiDebugTxPacketFate.FW_DROP_OTHER: return WifiLoggerHal.TX_PKT_FATE_FW_DROP_OTHER; case WifiDebugTxPacketFate.DRV_QUEUED: return WifiLoggerHal.TX_PKT_FATE_DRV_QUEUED; case WifiDebugTxPacketFate.DRV_DROP_INVALID: return WifiLoggerHal.TX_PKT_FATE_DRV_DROP_INVALID; case WifiDebugTxPacketFate.DRV_DROP_NOBUFS: return WifiLoggerHal.TX_PKT_FATE_DRV_DROP_NOBUFS; case WifiDebugTxPacketFate.DRV_DROP_OTHER: return WifiLoggerHal.TX_PKT_FATE_DRV_DROP_OTHER; default: throw new IllegalArgumentException("bad " + type); } } private static boolean hasCapability(long capabilities, long desiredCapability) { return (capabilities & desiredCapability) != 0; } @VisibleForTesting long halToFrameworkStaIfaceCapability(int caps) { long features = 0; if (hasCapability(caps, android.hardware.wifi.V1_0.IWifiStaIface.StaIfaceCapabilityMask.HOTSPOT)) { features |= WifiManager.WIFI_FEATURE_PASSPOINT; } if (hasCapability(caps, android.hardware.wifi.V1_0.IWifiStaIface.StaIfaceCapabilityMask.BACKGROUND_SCAN)) { features |= WifiManager.WIFI_FEATURE_SCANNER; } if (hasCapability(caps, android.hardware.wifi.V1_0.IWifiStaIface.StaIfaceCapabilityMask.PNO)) { features |= WifiManager.WIFI_FEATURE_PNO; } if (hasCapability(caps, android.hardware.wifi.V1_0.IWifiStaIface.StaIfaceCapabilityMask.TDLS)) { features |= WifiManager.WIFI_FEATURE_TDLS; } if (hasCapability(caps, android.hardware.wifi.V1_0.IWifiStaIface.StaIfaceCapabilityMask.TDLS_OFFCHANNEL)) { features |= WifiManager.WIFI_FEATURE_TDLS_OFFCHANNEL; } if (hasCapability(caps, android.hardware.wifi.V1_0.IWifiStaIface.StaIfaceCapabilityMask.LINK_LAYER_STATS)) { features |= WifiManager.WIFI_FEATURE_LINK_LAYER_STATS; } if (hasCapability(caps, android.hardware.wifi.V1_0.IWifiStaIface.StaIfaceCapabilityMask.RSSI_MONITOR)) { features |= WifiManager.WIFI_FEATURE_RSSI_MONITOR; } if (hasCapability(caps, android.hardware.wifi.V1_0.IWifiStaIface.StaIfaceCapabilityMask.KEEP_ALIVE)) { features |= WifiManager.WIFI_FEATURE_MKEEP_ALIVE; } if (hasCapability(caps, android.hardware.wifi.V1_0.IWifiStaIface.StaIfaceCapabilityMask.ND_OFFLOAD)) { features |= WifiManager.WIFI_FEATURE_CONFIG_NDO; } if (hasCapability(caps, android.hardware.wifi.V1_0.IWifiStaIface.StaIfaceCapabilityMask.CONTROL_ROAMING)) { features |= WifiManager.WIFI_FEATURE_CONTROL_ROAMING; } if (hasCapability(caps, android.hardware.wifi.V1_0.IWifiStaIface.StaIfaceCapabilityMask .PROBE_IE_WHITELIST)) { features |= WifiManager.WIFI_FEATURE_IE_WHITELIST; } if (hasCapability(caps, android.hardware.wifi.V1_0.IWifiStaIface.StaIfaceCapabilityMask.SCAN_RAND)) { features |= WifiManager.WIFI_FEATURE_SCAN_RAND; } return features; } @VisibleForTesting WifiLinkLayerStats frameworkFromHalLinkLayerStats(StaLinkLayerStats stats) { if (stats == null) return null; WifiLinkLayerStats out = new WifiLinkLayerStats(); setIfaceStats(out, stats.iface); setRadioStats(out, stats.radios); out.timeStampInMs = stats.timeStampInMs; out.version = WifiLinkLayerStats.V1_0; return out; } /** * Makes the framework version of link layer stats from the hal version. */ @VisibleForTesting WifiLinkLayerStats frameworkFromHalLinkLayerStats_1_3( android.hardware.wifi.V1_3.StaLinkLayerStats stats) { if (stats == null) return null; WifiLinkLayerStats out = new WifiLinkLayerStats(); setIfaceStats(out, stats.iface); setRadioStats_1_3(out, stats.radios); out.timeStampInMs = stats.timeStampInMs; out.version = WifiLinkLayerStats.V1_3; return out; } /** * Makes the framework version of link layer stats from the hal version. */ @VisibleForTesting WifiLinkLayerStats frameworkFromHalLinkLayerStats_1_5( android.hardware.wifi.V1_5.StaLinkLayerStats stats) { if (stats == null) return null; WifiLinkLayerStats out = new WifiLinkLayerStats(); setIfaceStats_1_5(out, stats.iface); setRadioStats_1_5(out, stats.radios); out.timeStampInMs = stats.timeStampInMs; out.version = WifiLinkLayerStats.V1_5; return out; } /** * Makes the framework version of link layer stats from the hal version. */ @VisibleForTesting WifiLinkLayerStats frameworkFromHalLinkLayerStats_1_6( android.hardware.wifi.V1_6.StaLinkLayerStats stats) { if (stats == null) return null; WifiLinkLayerStats out = new WifiLinkLayerStats(); setIfaceStats_1_6(out, stats.iface); setRadioStats_1_6(out, stats.radios); out.timeStampInMs = stats.timeStampInMs; out.version = WifiLinkLayerStats.V1_5; return out; } private static void setIfaceStats(WifiLinkLayerStats stats, StaLinkLayerIfaceStats iface) { if (iface == null) return; stats.links = new WifiLinkLayerStats.LinkSpecificStats[NUM_OF_LINKS]; stats.links[DEFAULT_LINK] = new WifiLinkLayerStats.LinkSpecificStats(); stats.links[DEFAULT_LINK].link_id = 0; stats.links[DEFAULT_LINK].state = LINK_STATE_UNKNOWN; stats.links[DEFAULT_LINK].beacon_rx = iface.beaconRx; /* HIDL is using legacy single link layer stats. */ stats.links[DEFAULT_LINK].radio_id = 0; stats.links[DEFAULT_LINK].frequencyMhz = 0; stats.links[DEFAULT_LINK].rssi_mgmt = iface.avgRssiMgmt; // Statistics are broken out by Wireless Multimedia Extensions categories // WME Best Effort Access Category stats.links[DEFAULT_LINK].rxmpdu_be = iface.wmeBePktStats.rxMpdu; stats.links[DEFAULT_LINK].txmpdu_be = iface.wmeBePktStats.txMpdu; stats.links[DEFAULT_LINK].lostmpdu_be = iface.wmeBePktStats.lostMpdu; stats.links[DEFAULT_LINK].retries_be = iface.wmeBePktStats.retries; // WME Background Access Category stats.links[DEFAULT_LINK].rxmpdu_bk = iface.wmeBkPktStats.rxMpdu; stats.links[DEFAULT_LINK].txmpdu_bk = iface.wmeBkPktStats.txMpdu; stats.links[DEFAULT_LINK].lostmpdu_bk = iface.wmeBkPktStats.lostMpdu; stats.links[DEFAULT_LINK].retries_bk = iface.wmeBkPktStats.retries; // WME Video Access Category stats.links[DEFAULT_LINK].rxmpdu_vi = iface.wmeViPktStats.rxMpdu; stats.links[DEFAULT_LINK].txmpdu_vi = iface.wmeViPktStats.txMpdu; stats.links[DEFAULT_LINK].lostmpdu_vi = iface.wmeViPktStats.lostMpdu; stats.links[DEFAULT_LINK].retries_vi = iface.wmeViPktStats.retries; // WME Voice Access Category stats.links[DEFAULT_LINK].rxmpdu_vo = iface.wmeVoPktStats.rxMpdu; stats.links[DEFAULT_LINK].txmpdu_vo = iface.wmeVoPktStats.txMpdu; stats.links[DEFAULT_LINK].lostmpdu_vo = iface.wmeVoPktStats.lostMpdu; stats.links[DEFAULT_LINK].retries_vo = iface.wmeVoPktStats.retries; } private static void setIfaceStats_1_5(WifiLinkLayerStats stats, android.hardware.wifi.V1_5.StaLinkLayerIfaceStats iface) { if (iface == null) return; setIfaceStats(stats, iface.V1_0); stats.links[DEFAULT_LINK].timeSliceDutyCycleInPercent = iface.timeSliceDutyCycleInPercent; // WME Best Effort Access Category stats.links[DEFAULT_LINK].contentionTimeMinBeInUsec = iface.wmeBeContentionTimeStats.contentionTimeMinInUsec; stats.links[DEFAULT_LINK].contentionTimeMaxBeInUsec = iface.wmeBeContentionTimeStats.contentionTimeMaxInUsec; stats.links[DEFAULT_LINK].contentionTimeAvgBeInUsec = iface.wmeBeContentionTimeStats.contentionTimeAvgInUsec; stats.links[DEFAULT_LINK].contentionNumSamplesBe = iface.wmeBeContentionTimeStats.contentionNumSamples; // WME Background Access Category stats.links[DEFAULT_LINK].contentionTimeMinBkInUsec = iface.wmeBkContentionTimeStats.contentionTimeMinInUsec; stats.links[DEFAULT_LINK].contentionTimeMaxBkInUsec = iface.wmeBkContentionTimeStats.contentionTimeMaxInUsec; stats.links[DEFAULT_LINK].contentionTimeAvgBkInUsec = iface.wmeBkContentionTimeStats.contentionTimeAvgInUsec; stats.links[DEFAULT_LINK].contentionNumSamplesBk = iface.wmeBkContentionTimeStats.contentionNumSamples; // WME Video Access Category stats.links[DEFAULT_LINK].contentionTimeMinViInUsec = iface.wmeViContentionTimeStats.contentionTimeMinInUsec; stats.links[DEFAULT_LINK].contentionTimeMaxViInUsec = iface.wmeViContentionTimeStats.contentionTimeMaxInUsec; stats.links[DEFAULT_LINK].contentionTimeAvgViInUsec = iface.wmeViContentionTimeStats.contentionTimeAvgInUsec; stats.links[DEFAULT_LINK].contentionNumSamplesVi = iface.wmeViContentionTimeStats.contentionNumSamples; // WME Voice Access Category stats.links[DEFAULT_LINK].contentionTimeMinVoInUsec = iface.wmeVoContentionTimeStats.contentionTimeMinInUsec; stats.links[DEFAULT_LINK].contentionTimeMaxVoInUsec = iface.wmeVoContentionTimeStats.contentionTimeMaxInUsec; stats.links[DEFAULT_LINK].contentionTimeAvgVoInUsec = iface.wmeVoContentionTimeStats.contentionTimeAvgInUsec; stats.links[DEFAULT_LINK].contentionNumSamplesVo = iface.wmeVoContentionTimeStats.contentionNumSamples; // Peer information statistics stats.links[DEFAULT_LINK].peerInfo = new WifiLinkLayerStats.PeerInfo[iface.peers.size()]; for (int i = 0; i < stats.links[DEFAULT_LINK].peerInfo.length; i++) { WifiLinkLayerStats.PeerInfo peer = new WifiLinkLayerStats.PeerInfo(); android.hardware.wifi.V1_5.StaPeerInfo staPeerInfo = iface.peers.get(i); peer.staCount = staPeerInfo.staCount; peer.chanUtil = staPeerInfo.chanUtil; WifiLinkLayerStats.RateStat[] rateStats = new WifiLinkLayerStats.RateStat[staPeerInfo.rateStats.size()]; for (int j = 0; j < staPeerInfo.rateStats.size(); j++) { rateStats[j] = new WifiLinkLayerStats.RateStat(); android.hardware.wifi.V1_5.StaRateStat staRateStat = staPeerInfo.rateStats.get(j); rateStats[j].preamble = staRateStat.rateInfo.preamble; rateStats[j].nss = staRateStat.rateInfo.nss; rateStats[j].bw = staRateStat.rateInfo.bw; rateStats[j].rateMcsIdx = staRateStat.rateInfo.rateMcsIdx; rateStats[j].bitRateInKbps = staRateStat.rateInfo.bitRateInKbps; rateStats[j].txMpdu = staRateStat.txMpdu; rateStats[j].rxMpdu = staRateStat.rxMpdu; rateStats[j].mpduLost = staRateStat.mpduLost; rateStats[j].retries = staRateStat.retries; } peer.rateStats = rateStats; stats.links[DEFAULT_LINK].peerInfo[i] = peer; } } private static void setIfaceStats_1_6(WifiLinkLayerStats stats, android.hardware.wifi.V1_6.StaLinkLayerIfaceStats iface) { if (iface == null) return; setIfaceStats(stats, iface.V1_0); stats.links[DEFAULT_LINK].timeSliceDutyCycleInPercent = iface.timeSliceDutyCycleInPercent; // WME Best Effort Access Category stats.links[DEFAULT_LINK].contentionTimeMinBeInUsec = iface.wmeBeContentionTimeStats.contentionTimeMinInUsec; stats.links[DEFAULT_LINK].contentionTimeMaxBeInUsec = iface.wmeBeContentionTimeStats.contentionTimeMaxInUsec; stats.links[DEFAULT_LINK].contentionTimeAvgBeInUsec = iface.wmeBeContentionTimeStats.contentionTimeAvgInUsec; stats.links[DEFAULT_LINK].contentionNumSamplesBe = iface.wmeBeContentionTimeStats.contentionNumSamples; // WME Background Access Category stats.links[DEFAULT_LINK].contentionTimeMinBkInUsec = iface.wmeBkContentionTimeStats.contentionTimeMinInUsec; stats.links[DEFAULT_LINK].contentionTimeMaxBkInUsec = iface.wmeBkContentionTimeStats.contentionTimeMaxInUsec; stats.links[DEFAULT_LINK].contentionTimeAvgBkInUsec = iface.wmeBkContentionTimeStats.contentionTimeAvgInUsec; stats.links[DEFAULT_LINK].contentionNumSamplesBk = iface.wmeBkContentionTimeStats.contentionNumSamples; // WME Video Access Category stats.links[DEFAULT_LINK].contentionTimeMinViInUsec = iface.wmeViContentionTimeStats.contentionTimeMinInUsec; stats.links[DEFAULT_LINK].contentionTimeMaxViInUsec = iface.wmeViContentionTimeStats.contentionTimeMaxInUsec; stats.links[DEFAULT_LINK].contentionTimeAvgViInUsec = iface.wmeViContentionTimeStats.contentionTimeAvgInUsec; stats.links[DEFAULT_LINK].contentionNumSamplesVi = iface.wmeViContentionTimeStats.contentionNumSamples; // WME Voice Access Category stats.links[DEFAULT_LINK].contentionTimeMinVoInUsec = iface.wmeVoContentionTimeStats.contentionTimeMinInUsec; stats.links[DEFAULT_LINK].contentionTimeMaxVoInUsec = iface.wmeVoContentionTimeStats.contentionTimeMaxInUsec; stats.links[DEFAULT_LINK].contentionTimeAvgVoInUsec = iface.wmeVoContentionTimeStats.contentionTimeAvgInUsec; stats.links[DEFAULT_LINK].contentionNumSamplesVo = iface.wmeVoContentionTimeStats.contentionNumSamples; // Peer information statistics stats.links[DEFAULT_LINK].peerInfo = new WifiLinkLayerStats.PeerInfo[iface.peers.size()]; for (int i = 0; i < stats.links[DEFAULT_LINK].peerInfo.length; i++) { WifiLinkLayerStats.PeerInfo peer = new WifiLinkLayerStats.PeerInfo(); android.hardware.wifi.V1_6.StaPeerInfo staPeerInfo = iface.peers.get(i); peer.staCount = staPeerInfo.staCount; peer.chanUtil = staPeerInfo.chanUtil; WifiLinkLayerStats.RateStat[] rateStats = new WifiLinkLayerStats.RateStat[staPeerInfo.rateStats.size()]; for (int j = 0; j < staPeerInfo.rateStats.size(); j++) { rateStats[j] = new WifiLinkLayerStats.RateStat(); android.hardware.wifi.V1_6.StaRateStat staRateStat = staPeerInfo.rateStats.get(j); rateStats[j].preamble = staRateStat.rateInfo.preamble; rateStats[j].nss = staRateStat.rateInfo.nss; rateStats[j].bw = staRateStat.rateInfo.bw; rateStats[j].rateMcsIdx = staRateStat.rateInfo.rateMcsIdx; rateStats[j].bitRateInKbps = staRateStat.rateInfo.bitRateInKbps; rateStats[j].txMpdu = staRateStat.txMpdu; rateStats[j].rxMpdu = staRateStat.rxMpdu; rateStats[j].mpduLost = staRateStat.mpduLost; rateStats[j].retries = staRateStat.retries; } peer.rateStats = rateStats; stats.links[DEFAULT_LINK].peerInfo[i] = peer; } } private static void setRadioStats(WifiLinkLayerStats stats, List radios) { if (radios == null) return; // Do not coalesce this info for multi radio devices with older HALs. if (radios.size() > 0) { StaLinkLayerRadioStats radioStats = radios.get(0); stats.on_time = radioStats.onTimeInMs; stats.tx_time = radioStats.txTimeInMs; stats.tx_time_per_level = new int[radioStats.txTimeInMsPerLevel.size()]; for (int i = 0; i < stats.tx_time_per_level.length; i++) { stats.tx_time_per_level[i] = radioStats.txTimeInMsPerLevel.get(i); } stats.rx_time = radioStats.rxTimeInMs; stats.on_time_scan = radioStats.onTimeInMsForScan; stats.numRadios = 1; } } private void setRadioStats_1_3(WifiLinkLayerStats stats, List radios) { if (radios == null) return; int radioIndex = 0; for (android.hardware.wifi.V1_3.StaLinkLayerRadioStats radioStats : radios) { aggregateFrameworkRadioStatsFromHidl_1_3(radioIndex, stats, radioStats); radioIndex++; } } private void setRadioStats_1_5(WifiLinkLayerStats stats, List radios) { if (radios == null) return; int radioIndex = 0; stats.radioStats = new WifiLinkLayerStats.RadioStat[radios.size()]; for (android.hardware.wifi.V1_5.StaLinkLayerRadioStats radioStats : radios) { WifiLinkLayerStats.RadioStat radio = new WifiLinkLayerStats.RadioStat(); setFrameworkPerRadioStatsFromHidl_1_3(radioStats.radioId, radio, radioStats.V1_3); stats.radioStats[radioIndex] = radio; aggregateFrameworkRadioStatsFromHidl_1_3(radioIndex, stats, radioStats.V1_3); radioIndex++; } } private void setRadioStats_1_6(WifiLinkLayerStats stats, List radios) { if (radios == null) return; int radioIndex = 0; stats.radioStats = new WifiLinkLayerStats.RadioStat[radios.size()]; for (android.hardware.wifi.V1_6.StaLinkLayerRadioStats radioStats : radios) { WifiLinkLayerStats.RadioStat radio = new WifiLinkLayerStats.RadioStat(); setFrameworkPerRadioStatsFromHidl_1_6(radio, radioStats); stats.radioStats[radioIndex] = radio; aggregateFrameworkRadioStatsFromHidl_1_6(radioIndex, stats, radioStats); radioIndex++; } } /** * Set individual radio stats from the hal radio stats for V1_3 */ private void setFrameworkPerRadioStatsFromHidl_1_3(int radioId, WifiLinkLayerStats.RadioStat radio, android.hardware.wifi.V1_3.StaLinkLayerRadioStats hidlRadioStats) { radio.radio_id = radioId; radio.on_time = hidlRadioStats.V1_0.onTimeInMs; radio.tx_time = hidlRadioStats.V1_0.txTimeInMs; radio.rx_time = hidlRadioStats.V1_0.rxTimeInMs; radio.on_time_scan = hidlRadioStats.V1_0.onTimeInMsForScan; radio.on_time_nan_scan = hidlRadioStats.onTimeInMsForNanScan; radio.on_time_background_scan = hidlRadioStats.onTimeInMsForBgScan; radio.on_time_roam_scan = hidlRadioStats.onTimeInMsForRoamScan; radio.on_time_pno_scan = hidlRadioStats.onTimeInMsForPnoScan; radio.on_time_hs20_scan = hidlRadioStats.onTimeInMsForHs20Scan; /* Copy list of channel stats */ for (android.hardware.wifi.V1_3.WifiChannelStats channelStats : hidlRadioStats.channelStats) { WifiLinkLayerStats.ChannelStats channelStatsEntry = new WifiLinkLayerStats.ChannelStats(); channelStatsEntry.frequency = channelStats.channel.centerFreq; channelStatsEntry.radioOnTimeMs = channelStats.onTimeInMs; channelStatsEntry.ccaBusyTimeMs = channelStats.ccaBusyTimeInMs; radio.channelStatsMap.put(channelStats.channel.centerFreq, channelStatsEntry); } } /** * Set individual radio stats from the hal radio stats for V1_6 */ private void setFrameworkPerRadioStatsFromHidl_1_6(WifiLinkLayerStats.RadioStat radio, android.hardware.wifi.V1_6.StaLinkLayerRadioStats hidlRadioStats) { radio.radio_id = hidlRadioStats.radioId; radio.on_time = hidlRadioStats.V1_0.onTimeInMs; radio.tx_time = hidlRadioStats.V1_0.txTimeInMs; radio.rx_time = hidlRadioStats.V1_0.rxTimeInMs; radio.on_time_scan = hidlRadioStats.V1_0.onTimeInMsForScan; radio.on_time_nan_scan = hidlRadioStats.onTimeInMsForNanScan; radio.on_time_background_scan = hidlRadioStats.onTimeInMsForBgScan; radio.on_time_roam_scan = hidlRadioStats.onTimeInMsForRoamScan; radio.on_time_pno_scan = hidlRadioStats.onTimeInMsForPnoScan; radio.on_time_hs20_scan = hidlRadioStats.onTimeInMsForHs20Scan; /* Copy list of channel stats */ for (android.hardware.wifi.V1_6.WifiChannelStats channelStats : hidlRadioStats.channelStats) { WifiLinkLayerStats.ChannelStats channelStatsEntry = new WifiLinkLayerStats.ChannelStats(); channelStatsEntry.frequency = channelStats.channel.centerFreq; channelStatsEntry.radioOnTimeMs = channelStats.onTimeInMs; channelStatsEntry.ccaBusyTimeMs = channelStats.ccaBusyTimeInMs; radio.channelStatsMap.put(channelStats.channel.centerFreq, channelStatsEntry); } } /** * If config_wifiLinkLayerAllRadiosStatsAggregationEnabled is set to true, aggregate * the radio stats from all the radios else process the stats from Radio 0 only. * This method is for V1_3 */ private void aggregateFrameworkRadioStatsFromHidl_1_3(int radioIndex, WifiLinkLayerStats stats, android.hardware.wifi.V1_3.StaLinkLayerRadioStats hidlRadioStats) { if (!mWifiLinkLayerAllRadiosStatsAggregationEnabled && radioIndex > 0) { return; } // Aggregate the radio stats from all the radios stats.on_time += hidlRadioStats.V1_0.onTimeInMs; stats.tx_time += hidlRadioStats.V1_0.txTimeInMs; // Aggregate tx_time_per_level based on the assumption that the length of // txTimeInMsPerLevel is the same across all radios. So txTimeInMsPerLevel on other // radios at array indices greater than the length of first radio will be dropped. if (stats.tx_time_per_level == null) { stats.tx_time_per_level = new int[hidlRadioStats.V1_0.txTimeInMsPerLevel.size()]; } for (int i = 0; i < hidlRadioStats.V1_0.txTimeInMsPerLevel.size() && i < stats.tx_time_per_level.length; i++) { stats.tx_time_per_level[i] += hidlRadioStats.V1_0.txTimeInMsPerLevel.get(i); } stats.rx_time += hidlRadioStats.V1_0.rxTimeInMs; stats.on_time_scan += hidlRadioStats.V1_0.onTimeInMsForScan; stats.on_time_nan_scan += hidlRadioStats.onTimeInMsForNanScan; stats.on_time_background_scan += hidlRadioStats.onTimeInMsForBgScan; stats.on_time_roam_scan += hidlRadioStats.onTimeInMsForRoamScan; stats.on_time_pno_scan += hidlRadioStats.onTimeInMsForPnoScan; stats.on_time_hs20_scan += hidlRadioStats.onTimeInMsForHs20Scan; /* Copy list of channel stats */ for (android.hardware.wifi.V1_3.WifiChannelStats channelStats : hidlRadioStats.channelStats) { WifiLinkLayerStats.ChannelStats channelStatsEntry = stats.channelStatsMap.get(channelStats.channel.centerFreq); if (channelStatsEntry == null) { channelStatsEntry = new WifiLinkLayerStats.ChannelStats(); channelStatsEntry.frequency = channelStats.channel.centerFreq; stats.channelStatsMap.put(channelStats.channel.centerFreq, channelStatsEntry); } channelStatsEntry.radioOnTimeMs += channelStats.onTimeInMs; channelStatsEntry.ccaBusyTimeMs += channelStats.ccaBusyTimeInMs; } stats.numRadios++; } /** * If config_wifiLinkLayerAllRadiosStatsAggregationEnabled is set to true, aggregate * the radio stats from all the radios else process the stats from Radio 0 only. * This method is for V1_6 */ private void aggregateFrameworkRadioStatsFromHidl_1_6(int radioIndex, WifiLinkLayerStats stats, android.hardware.wifi.V1_6.StaLinkLayerRadioStats hidlRadioStats) { if (!mWifiLinkLayerAllRadiosStatsAggregationEnabled && radioIndex > 0) { return; } // Aggregate the radio stats from all the radios stats.on_time += hidlRadioStats.V1_0.onTimeInMs; stats.tx_time += hidlRadioStats.V1_0.txTimeInMs; // Aggregate tx_time_per_level based on the assumption that the length of // txTimeInMsPerLevel is the same across all radios. So txTimeInMsPerLevel on other // radios at array indices greater than the length of first radio will be dropped. if (stats.tx_time_per_level == null) { stats.tx_time_per_level = new int[hidlRadioStats.V1_0.txTimeInMsPerLevel.size()]; } for (int i = 0; i < hidlRadioStats.V1_0.txTimeInMsPerLevel.size() && i < stats.tx_time_per_level.length; i++) { stats.tx_time_per_level[i] += hidlRadioStats.V1_0.txTimeInMsPerLevel.get(i); } stats.rx_time += hidlRadioStats.V1_0.rxTimeInMs; stats.on_time_scan += hidlRadioStats.V1_0.onTimeInMsForScan; stats.on_time_nan_scan += hidlRadioStats.onTimeInMsForNanScan; stats.on_time_background_scan += hidlRadioStats.onTimeInMsForBgScan; stats.on_time_roam_scan += hidlRadioStats.onTimeInMsForRoamScan; stats.on_time_pno_scan += hidlRadioStats.onTimeInMsForPnoScan; stats.on_time_hs20_scan += hidlRadioStats.onTimeInMsForHs20Scan; /* Copy list of channel stats */ for (android.hardware.wifi.V1_6.WifiChannelStats channelStats : hidlRadioStats.channelStats) { WifiLinkLayerStats.ChannelStats channelStatsEntry = stats.channelStatsMap.get(channelStats.channel.centerFreq); if (channelStatsEntry == null) { channelStatsEntry = new WifiLinkLayerStats.ChannelStats(); channelStatsEntry.frequency = channelStats.channel.centerFreq; stats.channelStatsMap.put(channelStats.channel.centerFreq, channelStatsEntry); } channelStatsEntry.radioOnTimeMs += channelStats.onTimeInMs; channelStatsEntry.ccaBusyTimeMs += channelStats.ccaBusyTimeInMs; } stats.numRadios++; } private static StaBackgroundScanParameters frameworkToHalBackgroundScanParams( WifiStaIface.StaBackgroundScanParameters frameworkParams) { StaBackgroundScanParameters halParams = new StaBackgroundScanParameters(); halParams.basePeriodInMs = frameworkParams.basePeriodInMs; halParams.maxApPerScan = frameworkParams.maxApPerScan; halParams.reportThresholdPercent = frameworkParams.reportThresholdPercent; halParams.reportThresholdNumScans = frameworkParams.reportThresholdNumScans; if (frameworkParams.buckets != null) { for (WifiNative.BucketSettings bucket : frameworkParams.buckets) { halParams.buckets.add(frameworkToHalBucketParams(bucket)); } } return halParams; } private static StaBackgroundScanBucketParameters frameworkToHalBucketParams( WifiNative.BucketSettings frameworkBucket) { StaBackgroundScanBucketParameters halBucket = new StaBackgroundScanBucketParameters(); halBucket.bucketIdx = frameworkBucket.bucket; halBucket.band = frameworkToHalWifiBand(frameworkBucket.band); if (frameworkBucket.channels != null) { for (WifiNative.ChannelSettings cs : frameworkBucket.channels) { halBucket.frequencies.add(cs.frequency); } } halBucket.periodInMs = frameworkBucket.period_ms; halBucket.eventReportScheme = frameworkToHalReportSchemeMask(frameworkBucket.report_events); halBucket.exponentialMaxPeriodInMs = frameworkBucket.max_period_ms; // Although HAL API allows configurable base value for the truncated // exponential back off scan. Native API and above support only // truncated binary exponential back off scan. // Hard code value of base to 2 here. halBucket.exponentialBase = 2; halBucket.exponentialStepCount = frameworkBucket.step_count; return halBucket; } /** * Convert WifiBand from framework to HAL. * * Note: This method is only used by background scan which does not * support 6GHz, hence band combinations including 6GHz are considered invalid * * @param frameworkBand one of WifiScanner.WIFI_BAND_* * @return A WifiBand value * @throws IllegalArgumentException if frameworkBand is not recognized */ private static int frameworkToHalWifiBand(int frameworkBand) { switch (frameworkBand) { case WifiScanner.WIFI_BAND_UNSPECIFIED: return WifiBand.BAND_UNSPECIFIED; case WifiScanner.WIFI_BAND_24_GHZ: return WifiBand.BAND_24GHZ; case WifiScanner.WIFI_BAND_5_GHZ: return WifiBand.BAND_5GHZ; case WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY: return WifiBand.BAND_5GHZ_DFS; case WifiScanner.WIFI_BAND_5_GHZ_WITH_DFS: return WifiBand.BAND_5GHZ_WITH_DFS; case WifiScanner.WIFI_BAND_BOTH: return WifiBand.BAND_24GHZ_5GHZ; case WifiScanner.WIFI_BAND_BOTH_WITH_DFS: return WifiBand.BAND_24GHZ_5GHZ_WITH_DFS; case WifiScanner.WIFI_BAND_6_GHZ: return WifiBand.BAND_6GHZ; case WifiScanner.WIFI_BAND_24_5_6_GHZ: return WifiBand.BAND_24GHZ_5GHZ_6GHZ; case WifiScanner.WIFI_BAND_24_5_WITH_DFS_6_GHZ: return WifiBand.BAND_24GHZ_5GHZ_WITH_DFS_6GHZ; case WifiScanner.WIFI_BAND_60_GHZ: return WifiBand.BAND_60GHZ; case WifiScanner.WIFI_BAND_24_5_6_60_GHZ: return WifiBand.BAND_24GHZ_5GHZ_6GHZ_60GHZ; case WifiScanner.WIFI_BAND_24_5_WITH_DFS_6_60_GHZ: return WifiBand.BAND_24GHZ_5GHZ_WITH_DFS_6GHZ_60GHZ; case WifiScanner.WIFI_BAND_24_GHZ_WITH_5GHZ_DFS: default: throw new IllegalArgumentException("bad band " + frameworkBand); } } /** * Convert the report scheme mask from framework to hal. * * @param reportUnderscoreEvents is logical OR of WifiScanner.REPORT_EVENT_* values * @return Corresponding StaBackgroundScanBucketEventReportSchemeMask value * @throws IllegalArgumentException if a mask bit is not recognized */ private static int frameworkToHalReportSchemeMask(int reportUnderscoreEvents) { int ans = 0; BitMask in = new BitMask(reportUnderscoreEvents); if (in.testAndClear(WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)) { ans |= StaBackgroundScanBucketEventReportSchemeMask.EACH_SCAN; } if (in.testAndClear(WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT)) { ans |= StaBackgroundScanBucketEventReportSchemeMask.FULL_RESULTS; } if (in.testAndClear(WifiScanner.REPORT_EVENT_NO_BATCH)) { ans |= StaBackgroundScanBucketEventReportSchemeMask.NO_BATCH; } if (in.value != 0) throw new IllegalArgumentException("bad " + reportUnderscoreEvents); return ans; } // Only sets the fields of ScanResult used by Gscan clients. private ScanResult hidlToFrameworkScanResult(StaScanResult scanResult) { if (scanResult == null) return null; WifiSsid originalSsid = WifiSsid.fromBytes( NativeUtil.byteArrayFromArrayList(scanResult.ssid)); MacAddress bssid; try { bssid = MacAddress.fromString(NativeUtil.macAddressFromByteArray(scanResult.bssid)); } catch (IllegalArgumentException e) { Log.e(TAG, "Failed to get BSSID of scan result: " + e); return null; } ScanResult frameworkScanResult = new ScanResult(); frameworkScanResult.setWifiSsid(mSsidTranslator.getTranslatedSsidAndRecordBssidCharset( originalSsid, bssid)); frameworkScanResult.BSSID = bssid.toString(); frameworkScanResult.level = scanResult.rssi; frameworkScanResult.frequency = scanResult.frequency; frameworkScanResult.timestamp = scanResult.timeStampInUs; return frameworkScanResult; } private ScanResult[] hidlToFrameworkScanResults(ArrayList scanResults) { if (scanResults == null || scanResults.isEmpty()) return new ScanResult[0]; ScanResult[] frameworkScanResults = new ScanResult[scanResults.size()]; int i = 0; for (StaScanResult scanResult : scanResults) { ScanResult frameworkScanResult = hidlToFrameworkScanResult(scanResult); if (frameworkScanResult == null) { Log.e(TAG, "hidlToFrameworkScanResults: unable to convert hidl to framework " + "scan result!"); continue; } frameworkScanResults[i++] = frameworkScanResult; } return frameworkScanResults; } private static int hidlToFrameworkScanDataFlags(int flag) { if (flag == StaScanDataFlagMask.INTERRUPTED) { return 1; } else { return 0; } } private WifiScanner.ScanData[] hidlToFrameworkScanDatas( int cmdId, ArrayList scanDatas) { if (scanDatas == null || scanDatas.isEmpty()) return new WifiScanner.ScanData[0]; WifiScanner.ScanData[] frameworkScanDatas = new WifiScanner.ScanData[scanDatas.size()]; int i = 0; for (StaScanData scanData : scanDatas) { int flags = hidlToFrameworkScanDataFlags(scanData.flags); ScanResult[] frameworkScanResults = hidlToFrameworkScanResults(scanData.results); frameworkScanDatas[i++] = new WifiScanner.ScanData(cmdId, flags, scanData.bucketsScanned, WifiScanner.WIFI_BAND_UNSPECIFIED, frameworkScanResults); } return frameworkScanDatas; } protected android.hardware.wifi.V1_2.IWifiStaIface getWifiStaIfaceV1_2Mockable() { return android.hardware.wifi.V1_2.IWifiStaIface.castFrom(mWifiStaIface); } protected android.hardware.wifi.V1_3.IWifiStaIface getWifiStaIfaceV1_3Mockable() { return android.hardware.wifi.V1_3.IWifiStaIface.castFrom(mWifiStaIface); } protected android.hardware.wifi.V1_5.IWifiStaIface getWifiStaIfaceV1_5Mockable() { return android.hardware.wifi.V1_5.IWifiStaIface.castFrom(mWifiStaIface); } protected android.hardware.wifi.V1_6.IWifiStaIface getWifiStaIfaceV1_6Mockable() { return android.hardware.wifi.V1_6.IWifiStaIface.castFrom(mWifiStaIface); } private boolean isOk(WifiStatus status, String methodStr) { if (status.code == WifiStatusCode.SUCCESS) return true; Log.e(TAG, methodStr + " failed with status: " + status); return false; } private void handleRemoteException(RemoteException e, String methodStr) { Log.e(TAG, methodStr + " failed with remote exception: " + e); mWifiStaIface = null; } private void handleException(Exception e, String methodStr) { Log.e(TAG, methodStr + " failed with exception: " + e); } private T validateAndCall(String methodStr, T defaultVal, @NonNull Supplier supplier) { if (mWifiStaIface == null) { Log.wtf(TAG, "Cannot call " + methodStr + " because mWifiStaIface is null"); return defaultVal; } return supplier.get(); } // HAL Callback private class StaIfaceEventCallback extends IWifiStaIfaceEventCallback.Stub { @Override public void onBackgroundScanFailure(int cmdId) { if (mFrameworkCallback == null) return; mFrameworkCallback.onBackgroundScanFailure(cmdId); } @Override public void onBackgroundFullScanResult(int cmdId, int bucketsScanned, StaScanResult result) { if (mFrameworkCallback == null) return; ScanResult frameworkScanResult = hidlToFrameworkScanResult(result); if (frameworkScanResult == null) { Log.e(TAG, "Unable to convert scan result from HAL to framework"); return; } mFrameworkCallback.onBackgroundFullScanResult(cmdId, bucketsScanned, frameworkScanResult); } @Override public void onBackgroundScanResults(int cmdId, ArrayList scanDatas) { if (mFrameworkCallback == null) return; WifiScanner.ScanData[] frameworkScanDatas = hidlToFrameworkScanDatas(cmdId, scanDatas); mFrameworkCallback.onBackgroundScanResults(cmdId, frameworkScanDatas); } @Override public void onRssiThresholdBreached(int cmdId, byte[/* 6 */] currBssid, int currRssi) { if (mFrameworkCallback == null) return; mFrameworkCallback.onRssiThresholdBreached(cmdId, currBssid, currRssi); } } }