Android WiFi 扫描流程

发布时间 2023-08-24 13:54:14作者: xiaowang_lj

1.WiFiManager

packages\modules\Wifi\framework\java\android\net\wifi\WifiManager.java  startScan

@Deprecated
    public boolean startScan() {
        return startScan(null);
    }

    /** @hide */
    @SystemApi
    @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
    public boolean startScan(WorkSource workSource) {
        try {
            String packageName = mContext.getOpPackageName();
            String attributionTag = mContext.getAttributionTag();
            return mService.startScan(packageName, attributionTag);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

2.IWifiManager

packages\modules\Wifi\framework\java\android\net\wifi\WifiManager.aidl

@UnsupportedAppUsage
    IWifiManager mService;

3.BaseWifiService实现WifiManager接口

public class BaseWifiService extends IWifiManager.Stub {

packages\modules\Wifi\service\java\com\android\server\wifi\BaseWifiService.java

4.WifiServiceImpl实现具体逻辑

packages\modules\Wifi\service\java\com\android\server\wifi\WifiServiceImpl.java

    @Override
    public boolean startScan(String packageName, String featureId) {
        if (enforceChangePermission(packageName) != MODE_ALLOWED) {
            return false;
        }

        int callingUid = Binder.getCallingUid();
        long ident = Binder.clearCallingIdentity();
        mLog.info("startScan uid=%").c(callingUid).flush();
        synchronized (this) {
            if (mInIdleMode) {
                // Need to send an immediate scan result broadcast in case the
                // caller is waiting for a result ..

                // TODO: investigate if the logic to cancel scans when idle can move to
                // WifiScanningServiceImpl.  This will 1 - clean up WifiServiceImpl and 2 -
                // avoid plumbing an awkward path to report a cancelled/failed scan.  This will
                // be sent directly until b/31398592 is fixed.
                sendFailedScanBroadcast();
                mScanPending = true;
                return false;
            }
        }
        try {
            mWifiPermissionsUtil.enforceCanAccessScanResults(packageName, featureId, callingUid,
                    null);
            Boolean scanSuccess = mWifiThreadRunner.call(() ->
                    mScanRequestProxy.startScan(callingUid, packageName), null);
            if (scanSuccess == null) {
                sendFailedScanBroadcast();
                return false;
            }
            if (!scanSuccess) {
                Log.e(TAG, "Failed to start scan");
                return false;
            }
        } catch (SecurityException e) {
            Log.w(TAG, "Permission violation - startScan not allowed for"
                    + " uid=" + callingUid + ", packageName=" + packageName + ", reason=" + e);
            return false;
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
        return true;
    }

enforceChangePermission会核查是否获取了android.Manifest.permission.CHANGE_WIFI_STATE权限

    @CheckResult
    private int enforceChangePermission(String callingPackage) {
        mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
        if (checkNetworkSettingsPermission(Binder.getCallingPid(), Binder.getCallingUid())) {
            return MODE_ALLOWED;
        }
        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_WIFI_STATE,
                "WifiService");

        return mAppOps.noteOp(
                AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Binder.getCallingUid(), callingPackage);
    }

5.WifiPermissionsUtil的enforceCanAccessScanResults会继续检查权限

packages\modules\Wifi\service\java\com\android\server\wifi\util\WifiPermissionsUtil.java

    public void enforceCanAccessScanResults(String pkgName, @Nullable String featureId, int uid,
            @Nullable String message)
            throws SecurityException {
        checkPackage(uid, pkgName);

        // Apps with NETWORK_SETTINGS, NETWORK_SETUP_WIZARD, NETWORK_MANAGED_PROVISIONING,
        // NETWORK_STACK & MAINLINE_NETWORK_STACK, RADIO_SCAN_WITHOUT_LOCATION are granted a bypass.
        if (checkNetworkSettingsPermission(uid) || checkNetworkSetupWizardPermission(uid)
                || checkNetworkManagedProvisioningPermission(uid)
                || checkNetworkStackPermission(uid) || checkMainlineNetworkStackPermission(uid)
                || checkScanWithoutLocationPermission(uid)) {
            return;
        }

        // Location mode must be enabled
        if (!isLocationModeEnabled()) {
            if (mVerboseLoggingEnabled) {
                Log.v(TAG, "enforceCanAccessScanResults(pkg=" + pkgName + ", uid=" + uid + "): "
                        + "location is disabled");
            }
            // Location mode is disabled, scan results cannot be returned
            throw new SecurityException("Location mode is disabled for the device");
        }

        // Check if the calling Uid has CAN_READ_PEER_MAC_ADDRESS permission.
        boolean canCallingUidAccessLocation = checkCallerHasPeersMacAddressPermission(uid);
        // LocationAccess by App: caller must have Coarse/Fine Location permission to have access to
        // location information.
        boolean canAppPackageUseLocation = checkCallersLocationPermission(pkgName, featureId,
                uid, /* coarseForTargetSdkLessThanQ */ true, message);

        // If neither caller or app has location access, there is no need to check
        // any other permissions. Deny access to scan results.
        if (!canCallingUidAccessLocation && !canAppPackageUseLocation) {
            if (mVerboseLoggingEnabled) {
                Log.v(TAG, "enforceCanAccessScanResults(pkg=" + pkgName + ", uid=" + uid + "): "
                        + "canCallingUidAccessLocation=" + canCallingUidAccessLocation
                        + ", canAppPackageUseLocation=" + canAppPackageUseLocation);
            }
            throw new SecurityException("UID " + uid + " has no location permission");
        }
        // Check if Wifi Scan request is an operation allowed for this App.
        if (!isScanAllowedbyApps(pkgName, featureId, uid)) {
            if (mVerboseLoggingEnabled) {
                Log.v(TAG, "enforceCanAccessScanResults(pkg=" + pkgName + ", uid=" + uid + "): "
                        + "doesn't have app-op " + AppOpsManager.OPSTR_WIFI_SCAN);
            }
            throw new SecurityException("UID " + uid + " has no wifi scan permission");
        }
        // If the User or profile is current, permission is granted
        // Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission.
        boolean isCurrentProfile = isCurrentProfile(uid);
        if (!isCurrentProfile && !checkInteractAcrossUsersFull(uid)) {
            if (mVerboseLoggingEnabled) {
                Log.v(TAG, "enforceCanAccessScanResults(pkg=" + pkgName + ", uid=" + uid + "): "
                        + "isCurrentProfile=" + isCurrentProfile
                        + ", checkInteractAcrossUsersFull=" + checkInteractAcrossUsersFull(uid));
            }
            throw new SecurityException("UID " + uid + " profile not permitted");
        }
    }

6.调用 \packages\modules\Wifi\service\java\com\android\server\wifi\ScanRequestProxy.java 的startScan

    /**
     * Initiate a wifi scan.
     *
     * @param callingUid The uid initiating the wifi scan. Blame will be given to this uid.
     * @return true if the scan request was placed or a scan is already ongoing, false otherwise.
     */
    public boolean startScan(int callingUid, String packageName) {
        if (!mScanningEnabled || !retrieveWifiScannerIfNecessary()) {
            Log.e(TAG, "Failed to retrieve wifiscanner");
            sendScanResultFailureBroadcastToPackage(packageName);
            return false;
        }
        boolean fromSettingsOrSetupWizard =
                mWifiPermissionsUtil.checkNetworkSettingsPermission(callingUid)
                        || mWifiPermissionsUtil.checkNetworkSetupWizardPermission(callingUid);
        // Check and throttle scan request unless,
        // a) App has either NETWORK_SETTINGS or NETWORK_SETUP_WIZARD permission.
        // b) Throttling has been disabled by user.
        int packageImportance = getPackageImportance(callingUid, packageName);
        if (!fromSettingsOrSetupWizard && mThrottleEnabled
                && shouldScanRequestBeThrottledForApp(callingUid, packageName, packageImportance)) {
            Log.i(TAG, "Scan request from " + packageName + " throttled");
            sendScanResultFailureBroadcastToPackage(packageName);
            return false;
        }
        // Create a worksource using the caller's UID.
        WorkSource workSource = new WorkSource(callingUid, packageName);
        mWifiMetrics.getScanMetrics().setWorkSource(workSource);
        mWifiMetrics.getScanMetrics().setImportance(packageImportance);

        // Create the scan settings.
        WifiScanner.ScanSettings settings = new WifiScanner.ScanSettings();
        // Scan requests from apps with network settings will be of high accuracy type.
        if (fromSettingsOrSetupWizard) {
            settings.type = WifiScanner.SCAN_TYPE_HIGH_ACCURACY;
        } else {
            if (SdkLevel.isAtLeastS()) {
                // since the scan request is from a normal app, do not scan all 6Ghz channels.
                settings.set6GhzPscOnlyEnabled(true);
            }
        }
        settings.band = WifiScanner.WIFI_BAND_ALL;
        settings.reportEvents = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN
                | WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT;
        if (mScanningForHiddenNetworksEnabled) {
            settings.hiddenNetworks.clear();
            // retrieve the list of hidden network SSIDs from saved network to scan for, if enabled.
            settings.hiddenNetworks.addAll(mWifiConfigManager.retrieveHiddenNetworkList());
            // retrieve the list of hidden network SSIDs from Network suggestion to scan for.
            settings.hiddenNetworks.addAll(
                    mWifiInjector.getWifiNetworkSuggestionsManager().retrieveHiddenNetworkList());
        }
        mWifiScanner.startScan(settings, new HandlerExecutor(mHandler),
                new ScanRequestProxyScanListener(), workSource);
        return true;
    }

7.调用WifiScanner的startScan

packages\modules\Wifi\framework\java\android\net\wifi\WifiScanner.java

    @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
    public void startScan(ScanSettings settings, @Nullable @CallbackExecutor Executor executor,
            ScanListener listener, WorkSource workSource) {
        Objects.requireNonNull(listener, "listener cannot be null");
        int key = addListener(listener, executor);
        if (key == INVALID_KEY) return;
        validateChannel();
        Bundle scanParams = new Bundle();
        scanParams.putParcelable(SCAN_PARAMS_SCAN_SETTINGS_KEY, settings);
        scanParams.putParcelable(SCAN_PARAMS_WORK_SOURCE_KEY, workSource);
        scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName());
        scanParams.putString(REQUEST_FEATURE_ID_KEY, mContext.getAttributionTag());
        mAsyncChannel.sendMessage(CMD_START_SINGLE_SCAN, 0, key, scanParams);
    }

8.mAsyncChannel是AsyncChannel类型的变量,内部通过Messager实现进程间通信

frameworks\base\core\java\com\android\internal\util\AsyncChannel.java

 @UnsupportedAppUsage
    public void sendMessage(Message msg) {
        msg.replyTo = mSrcMessenger;
        try {
            mDstMessenger.send(msg);
        } catch (RemoteException e) {
            replyDisconnected(STATUS_SEND_UNSUCCESSFUL);
        }
    }
/** Messenger for destination */
    private Messenger mDstMessenger;

9.WifiScanningServiceImpl实现进程间通信,处理响应的message

packages\modules\Wifi\service\java\com\android\server\wifi\scanner\WifiScanningServiceImpl.java

@Override
    public Messenger getMessenger() {
        if (mClientHandler != null) {
            mLog.trace("getMessenger() uid=%").c(Binder.getCallingUid()).flush();
            return new Messenger(mClientHandler);
        }
        loge("WifiScanningServiceImpl trying to get messenger w/o initialization");
        return null;
    }

消息处理的handler是ClientHandler mClientHandler

ClientHandler 是WifiScanningServiceImpl的内部类

 case WifiScanner.CMD_START_SINGLE_SCAN:
                case WifiScanner.CMD_STOP_SINGLE_SCAN:
                    mSingleScanStateMachine.sendMessage(Message.obtain(msg));
                    break;

然后转入WifiSingleScanStateMachine类中,WifiSingleScanStateMachine是WifiScanningServiceImpl的内部类

WifiSingleScanStateMachine有DefaultState ,用来处理消息

private final DefaultState mDefaultState = new DefaultState();
 case WifiScanner.CMD_START_SINGLE_SCAN:
                        handleScanStartMessage(ci, msg);
        /**
         * Helper method to handle the scan start message.
         */
        private void handleScanStartMessage(ClientInfo ci, Message msg) {
            int handler = msg.arg2;
            Bundle scanParams = (Bundle) msg.obj;
            if (scanParams == null) {
                logCallback("singleScanInvalidRequest",  ci, handler, "null params");
                replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "params null");
                return;
            }
            ScanSettings scanSettings = null;
            WorkSource workSource = null;
            try {
                scanSettings =
                        scanParams.getParcelable(
                                WifiScanner.SCAN_PARAMS_SCAN_SETTINGS_KEY);
                workSource =
                        scanParams.getParcelable(
                                WifiScanner.SCAN_PARAMS_WORK_SOURCE_KEY);
            } catch (BadParcelableException e) {
                Log.wtf(TAG, "Failed to get parcelable params", e);
                logCallback("singleScanInvalidRequest",  ci, handler,
                        "bad parcel params");
                replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST,
                        "bad parcel params");
                return;
            }
            if (validateScanRequest(ci, handler, scanSettings)) {
                if (getCurrentState() == mDefaultState && !scanSettings.ignoreLocationSettings) {
                    // Reject regular scan requests if scanning is disabled.
                    replyFailed(msg, WifiScanner.REASON_UNSPECIFIED, "not available");
                    return;
                }
                mWifiMetrics.incrementOneshotScanCount();
                if ((scanSettings.band & WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY) != 0) {
                    mWifiMetrics.incrementOneshotScanWithDfsCount();
                }
                logScanRequest("addSingleScanRequest", ci, handler, workSource,
                        scanSettings, null);
                replySucceeded(msg);

                if (scanSettings.ignoreLocationSettings) {
                    // Inform wifi manager that an emergency scan is in progress (regardless of
                    // whether scanning is currently enabled or not). This ensures that
                    // the wifi chip remains on for the duration of this scan.
                    mWifiManager.setEmergencyScanRequestInProgress(true);
                }

                if (getCurrentState() == mScanningState) {
                    // If there is an active scan that will fulfill the scan request then
                    // mark this request as an active scan, otherwise mark it pending.
                    if (activeScanSatisfies(scanSettings)) {
                        mActiveScans.addRequest(ci, handler, workSource, scanSettings);
                    } else {
                        mPendingScans.addRequest(ci, handler, workSource, scanSettings);
                    }
                } else if (getCurrentState() == mIdleState) {
                    // If were not currently scanning then try to start a scan. Otherwise
                    // this scan will be scheduled when transitioning back to IdleState
                    // after finishing the current scan.
                    mPendingScans.addRequest(ci, handler, workSource, scanSettings);
                    tryToStartNewScan();
                } else if (getCurrentState() == mDefaultState) {
                    // If scanning is disabled and the request is for emergency purposes
                    // (checked above), add to pending list. this scan will be scheduled when
                    // transitioning to IdleState when wifi manager enables scanning as a part of
                    // processing WifiManager.setEmergencyScanRequestInProgress(true)
                    mPendingScans.addRequest(ci, handler, workSource, scanSettings);
                }
            } else {
                logCallback("singleScanInvalidRequest",  ci, handler, "bad request");
                replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request");
                mWifiMetrics.incrementScanReturnEntry(
                        WifiMetricsProto.WifiLog.SCAN_FAILURE_INVALID_CONFIGURATION, 1);
            }
        }
        void tryToStartNewScan() {
            if (mPendingScans.size() == 0) { // no pending requests
                return;
            }
            mChannelHelper.updateChannels();
            // TODO move merging logic to a scheduler
            WifiNative.ScanSettings settings = new WifiNative.ScanSettings();
            settings.num_buckets = 1;
            WifiNative.BucketSettings bucketSettings = new WifiNative.BucketSettings();
            bucketSettings.bucket = 0;
            bucketSettings.period_ms = 0;
            bucketSettings.report_events = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN;

            ChannelCollection channels = mChannelHelper.createChannelCollection();
            List<WifiNative.HiddenNetwork> hiddenNetworkList = new ArrayList<>();
            for (RequestInfo<ScanSettings> entry : mPendingScans) {
                settings.scanType = mergeScanTypes(settings.scanType, entry.settings.type);
                settings.enable6GhzRnr = mergeRnrSetting(settings.enable6GhzRnr, entry.settings);
                channels.addChannels(entry.settings);
                for (ScanSettings.HiddenNetwork srcNetwork : entry.settings.hiddenNetworks) {
                    WifiNative.HiddenNetwork hiddenNetwork = new WifiNative.HiddenNetwork();
                    hiddenNetwork.ssid = srcNetwork.ssid;
                    hiddenNetworkList.add(hiddenNetwork);
                }
                if ((entry.settings.reportEvents & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT)
                        != 0) {
                    bucketSettings.report_events |= WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT;
                }

                if (entry.clientInfo != null) {
                    mWifiMetrics.getScanMetrics().setClientUid(entry.clientInfo.mUid);
                }
                mWifiMetrics.getScanMetrics().setWorkSource(entry.workSource);
            }

            if (hiddenNetworkList.size() > 0) {
                settings.hiddenNetworks = new WifiNative.HiddenNetwork[hiddenNetworkList.size()];
                int numHiddenNetworks = 0;
                for (WifiNative.HiddenNetwork hiddenNetwork : hiddenNetworkList) {
                    settings.hiddenNetworks[numHiddenNetworks++] = hiddenNetwork;
                }
            }

            channels.fillBucketSettings(bucketSettings, Integer.MAX_VALUE);
            settings.buckets = new WifiNative.BucketSettings[] {bucketSettings};

            if (mScannerImplsTracker.startSingleScan(settings)) {
                mWifiMetrics.getScanMetrics().logScanStarted(
                        WifiMetrics.ScanMetrics.SCAN_TYPE_SINGLE);

                // store the active scan settings
                mActiveScanSettings = settings;
                // swap pending and active scan requests
                RequestList<ScanSettings> tmp = mActiveScans;
                mActiveScans = mPendingScans;
                mPendingScans = tmp;
                // make sure that the pending list is clear
                mPendingScans.clear();
                transitionTo(mScanningState);
            } else {
                mWifiMetrics.incrementScanReturnEntry(
                        WifiMetricsProto.WifiLog.SCAN_UNKNOWN, mPendingScans.size());
                mWifiMetrics.getScanMetrics().logScanFailedToStart(
                        WifiMetrics.ScanMetrics.SCAN_TYPE_SINGLE);

                // notify and cancel failed scans
                sendOpFailedToAllAndClear(mPendingScans, WifiScanner.REASON_UNSPECIFIED,
                        "Failed to start single scan");
            }
        }
调用ScannerImplsTracker的startSingleScan方法 ,ScannerImplsTracker是WifiScanningServiceImpl的内部类
/**
             * Triggers a new scan on all the available scanner impls.
             * @return true if the scan succeeded on any of the impl, false otherwise.
             */
            public boolean startSingleScan(WifiNative.ScanSettings scanSettings) {
                mStatusPerImpl.clear();
                boolean anySuccess = false;
                for (Map.Entry<String, WifiScannerImpl> entry : mScannerImpls.entrySet()) {
                    String ifaceName = entry.getKey();
                    WifiScannerImpl impl = entry.getValue();
                    boolean success = impl.startSingleScan(
                            scanSettings, new ScanEventHandler(ifaceName));
                    if (!success) {
                        Log.e(TAG, "Failed to start single scan on " + ifaceName);
                        mStatusPerImpl.put(ifaceName, STATUS_FAILED);
                        continue;
                    }
                    mStatusPerImpl.put(ifaceName, STATUS_PENDING);
                    anySuccess = true;
                }
                return anySuccess;
            }

调用 WifiScannerImpl 的startSingleScan方法

Wifi/service/java/com/android/server/wifi/scanner/HalWifiScannerImpl.java:38:public class HalWifiScannerImpl extends WifiScannerImpl implements Handler.Callback {
Wifi/service/java/com/android/server/wifi/scanner/WificondScannerImpl.java:52:public class WificondScannerImpl extends WifiScannerImpl implements Handler.Callback {

 

packages\modules\Wifi\service\java\com\android\server\wifi\scanner\HalWifiScannerImpl.java

public boolean startSingleScan(WifiNative.ScanSettings settings,
            WifiNative.ScanEventHandler eventHandler) {
        return mWificondScannerDelegate.startSingleScan(settings, eventHandler);
    }
    @Override
    public boolean startSingleScan(WifiNative.ScanSettings settings,
            WifiNative.ScanEventHandler eventHandler) {
        if (eventHandler == null || settings == null) {
            Log.w(TAG, "Invalid arguments for startSingleScan: settings=" + settings
                    + ",eventHandler=" + eventHandler);
            return false;
        }
        synchronized (mSettingsLock) {
            if (mLastScanSettings != null) {
                Log.w(TAG, "A single scan is already running");
                return false;
            }

            ChannelCollection allFreqs = mChannelHelper.createChannelCollection();
            boolean reportFullResults = false;

            for (int i = 0; i < settings.num_buckets; ++i) {
                WifiNative.BucketSettings bucketSettings = settings.buckets[i];
                if ((bucketSettings.report_events
                                & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0) {
                    reportFullResults = true;
                }
                allFreqs.addChannels(bucketSettings);
            }

            List<String> hiddenNetworkSSIDSet = new ArrayList<>();
            if (settings.hiddenNetworks != null) {
                int numHiddenNetworks =
                        Math.min(settings.hiddenNetworks.length, MAX_HIDDEN_NETWORK_IDS_PER_SCAN);
                for (int i = 0; i < numHiddenNetworks; i++) {
                    hiddenNetworkSSIDSet.add(settings.hiddenNetworks[i].ssid);
                }
            }
            mLastScanSettings = new LastScanSettings(
                    mClock.getElapsedSinceBootNanos(),
                    reportFullResults, allFreqs, eventHandler);

            boolean success = false;
            Set<Integer> freqs = Collections.emptySet();
            if (!allFreqs.isEmpty()) {
                freqs = allFreqs.getScanFreqs();
                success = mWifiNative.scan(
                        getIfaceName(), settings.scanType, freqs, hiddenNetworkSSIDSet,
                        settings.enable6GhzRnr);
                if (!success) {
                    Log.e(TAG, "Failed to start scan, freqs=" + freqs);
                }
            } else {
                // There is a scan request but no available channels could be scanned for.
                // We regard it as a scan failure in this case.
                Log.e(TAG, "Failed to start scan because there is no available channel to scan");
            }
            if (success) {
                if (DBG) {
                    Log.d(TAG, "Starting wifi scan for freqs=" + freqs
                            + " on iface " + getIfaceName());
                }

                mScanTimeoutListener = new AlarmManager.OnAlarmListener() {
                    @Override public void onAlarm() {
                        handleScanTimeout();
                    }
                };

                mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                        mClock.getElapsedSinceBootMillis() + SCAN_TIMEOUT_MS,
                        TIMEOUT_ALARM_TAG, mScanTimeoutListener, mEventHandler);
            } else {
                // indicate scan failure async
                mEventHandler.post(() -> reportScanFailure());
            }

            return true;
        }
    }

 

调用WifiNative的scan方法

packages\modules\Wifi\service\java\com\android\server\wifi\WifiNative.java

    public boolean scan(
            @NonNull String ifaceName, @WifiAnnotations.ScanType int scanType, Set<Integer> freqs,
            List<String> hiddenNetworkSSIDs, boolean enable6GhzRnr) {
        List<byte[]> hiddenNetworkSsidsArrays = new ArrayList<>();
        for (String hiddenNetworkSsid : hiddenNetworkSSIDs) {
            try {
                hiddenNetworkSsidsArrays.add(
                        NativeUtil.byteArrayFromArrayList(
                                NativeUtil.decodeSsid(hiddenNetworkSsid)));
            } catch (IllegalArgumentException e) {
                Log.e(TAG, "Illegal argument " + hiddenNetworkSsid, e);
                continue;
            }
        }
        // enable6GhzRnr is a new parameter first introduced in Android S.
        if (SdkLevel.isAtLeastS()) {
            Bundle extraScanningParams = new Bundle();
            extraScanningParams.putBoolean(WifiNl80211Manager.SCANNING_PARAM_ENABLE_6GHZ_RNR,
                    enable6GhzRnr);
            return mWifiCondManager.startScan(ifaceName, scanType, freqs, hiddenNetworkSsidsArrays,
                    extraScanningParams);
        } else {
            return mWifiCondManager.startScan(ifaceName, scanType, freqs, hiddenNetworkSsidsArrays);
        }
    }