xref: /packages/apps/Settings/src/com/android/settings/wifi/WifiSettings.java

/** Called when the state of Wifi has changed. */
@Override
public void onWifiStateChanged(int state) {
    if (mIsRestricted) {
        return;
    }

    final int wifiState = mWifiManager.getWifiState();
    switch (wifiState) {
        case WifiManager.WIFI_STATE_ENABLED:
            updateAccessPointPreferences();
            break;

        case WifiManager.WIFI_STATE_ENABLING:
            removeConnectedAccessPointPreference();
            removeAccessPointPreference();
            addMessagePreference(R.string.wifi_starting);
            setProgressBarVisible(true);
            break;

        case WifiManager.WIFI_STATE_DISABLING:
            removeConnectedAccessPointPreference();
            removeAccessPointPreference();
            addMessagePreference(R.string.wifi_stopping);
            break;

        case WifiManager.WIFI_STATE_DISABLED:
            setOffMessage();
            setAdditionalSettingsSummaries();
            setProgressBarVisible(false);
            break;
    }
}


.........................................................................................


private void updateAccessPointPreferences() {
    // in case state has changed
    if (!mWifiManager.isWifiEnabled()) {
        return;
    }
    // AccessPoints are sorted by the WifiTracker
    final List<AccessPoint> accessPoints = mWifiTracker.getAccessPoints();
    if (isVerboseLoggingEnabled()) {
        Log.i(TAG, "updateAccessPoints called for: " + accessPoints);
    }

    boolean hasAvailableAccessPoints = false;
    mStatusMessagePreference.setVisible(false);
    mConnectedAccessPointPreferenceCategory.setVisible(true);
    mAccessPointsPreferenceCategory.setVisible(true);

    cacheRemoveAllPrefs(mAccessPointsPreferenceCategory);

    int index =
            configureConnectedAccessPointPreferenceCategory(accessPoints) ? 1 : 0;
    int numAccessPoints = accessPoints.size();
    for (; index < numAccessPoints; index++) {
        AccessPoint accessPoint = accessPoints.get(index);
        // Ignore access points that are out of range.
        if (accessPoint.isReachable()) {
            String key = accessPoint.getKey();
            hasAvailableAccessPoints = true;
            LongPressAccessPointPreference pref =
                    (LongPressAccessPointPreference) getCachedPreference(key);
            if (pref != null) {
                pref.setOrder(index);
                continue;
            }
            LongPressAccessPointPreference preference =
                    createLongPressAccessPointPreference(accessPoint);
            preference.setKey(key);
            preference.setOrder(index);
            if (mOpenSsid != null && mOpenSsid.equals(accessPoint.getSsidStr())
                    && (accessPoint.getSecurity() != AccessPoint.SECURITY_NONE &&
                    accessPoint.getSecurity() != AccessPoint.SECURITY_OWE &&
                    accessPoint.getSecurity() != AccessPoint.SECURITY_OWE_TRANSITION)) {
                if (!accessPoint.isSaved() || isDisabledByWrongPassword(accessPoint)) {
                    onPreferenceTreeClick(preference);
                    mOpenSsid = null;
                }
            }
            mAccessPointsPreferenceCategory.addPreference(preference);
            accessPoint.setListener(WifiSettings.this);
            preference.refresh();
        }
    }
    removeCachedPrefs(mAccessPointsPreferenceCategory);
    mAddWifiNetworkPreference.setOrder(index);
    mAccessPointsPreferenceCategory.addPreference(mAddWifiNetworkPreference);
    setAdditionalSettingsSummaries();

    if (!hasAvailableAccessPoints) {
        setProgressBarVisible(true);
        Preference pref = new Preference(getPrefContext());
        pref.setSelectable(false);
        pref.setSummary(R.string.wifi_empty_list_wifi_on);
        pref.setOrder(index++);
        pref.setKey(PREF_KEY_EMPTY_WIFI_LIST);
        mAccessPointsPreferenceCategory.addPreference(pref);
    } else {
        // Continuing showing progress bar for an additional delay to overlap with animation
        getView().postDelayed(mHideProgressBarRunnable, 1700 /* delay millis */);
    }
}

        在onWifiStateChanged方法里面处理wifi的开关,当我们打开wifi按钮的时候会走WifiManager.WIFI_STATE_ENABLED分支,调用updateAccessPointPreferences方法完成wifi的添加。accessPoints是通过WifiTracker的getAccessPoints获得的。

xref: /frameworks/base/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java

@AnyThread
public List<AccessPoint> getAccessPoints() {
    synchronized (mLock) {
        return new ArrayList<>(mInternalAccessPoints);
    }
}

        这一部分是打开wifi按钮的时候主动获取accessPoints,接下来我们分析accessPoints是如果从系统底层推送的。

xref: /frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiServiceImpl.java

private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (action.equals(Intent.ACTION_USER_REMOVED)) {
            int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
            mClientModeImpl.removeUserConfigs(userHandle);
        } else if (action.equals(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)) {
            int state = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE,
                    BluetoothAdapter.STATE_DISCONNECTED);
            mClientModeImpl.sendBluetoothAdapterStateChange(state);
        } else if (action.equals(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)) {
            boolean emergencyMode = intent.getBooleanExtra("phoneinECMState", false);
            mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, emergencyMode ? 1 : 0, 0);
        } else if (action.equals(TelephonyIntents.ACTION_EMERGENCY_CALL_STATE_CHANGED)) {
            boolean inCall = intent.getBooleanExtra(PhoneConstants.PHONE_IN_EMERGENCY_CALL, false);
            mWifiController.sendMessage(CMD_EMERGENCY_CALL_STATE_CHANGED, inCall ? 1 : 0, 0);
        } else if (action.equals(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED)) {
            handleIdleModeChanged();
        }
    }
};


..................................................................................................


void handleIdleModeChanged() {
    boolean doScan = false;
    synchronized (this) {
        boolean idle = mPowerManager.isDeviceIdleMode();
        if (mInIdleMode != idle) {
            mInIdleMode = idle;
            if (!idle) {
                if (mScanPending) {
                    mScanPending = false;
                    doScan = true;
                }
            }
        }
    }
    if (doScan) {
        // Someone requested a scan while we were idle; do a full scan now.
        // A security check of the caller's identity was made when the request arrived via
        // Binder. Now we'll pass the current process's identity to startScan().
        startScan(mContext.getOpPackageName());
    }
}


..................................................................................................


@Override
public boolean startScan(String packageName) {
    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, callingUid);
        Mutable<Boolean> scanSuccess = new Mutable<>();
        boolean runWithScissorsSuccess = mWifiInjector.getClientModeImplHandler()
                .runWithScissors(() -> {
                    scanSuccess.value = mScanRequestProxy.startScan(callingUid, packageName);
                }, RUN_WITH_SCISSORS_TIMEOUT_MILLIS);
        if (!runWithScissorsSuccess) {
            Log.e(TAG, "Failed to post runnable to start scan");
            sendFailedScanBroadcast();
            return false;
        }
        if (!scanSuccess.value) {
            Log.e(TAG, "Failed to start scan");
            return false;
        }
    } catch (SecurityException e) {
        Slog.e(TAG, "Permission violation - startScan not allowed for"
                + " uid=" + callingUid + ", packageName=" + packageName + ", reason=" + e);
        return false;
    } finally {
        Binder.restoreCallingIdentity(ident);
    }
    return true;
}

        注册广播,当收到ACTION_DEVICE_IDLE_MODE_CHANGED广播通知时就开始扫描。在startScan方法里会调用ScanRequestProxy类的startScan方法开启扫描。

xref: /frameworks/opt/net/wifi/service/java/com/android/server/wifi/ScanRequestProxy.java

public boolean startScan(int callingUid, String packageName) {
    if (!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.
    if (!fromSettingsOrSetupWizard && mThrottleEnabledSettingObserver.isEnabled()
            && shouldScanRequestBeThrottledForApp(callingUid, packageName)) {
        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);

    // 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.TYPE_HIGH_ACCURACY;
    }
    // always do full scans
    settings.band = WifiScanner.WIFI_BAND_BOTH_WITH_DFS;
    settings.reportEvents = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN
            | WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT;
    if (mScanningForHiddenNetworksEnabled) {
        // retrieve the list of hidden network SSIDs to scan for, if enabled.
        List<WifiScanner.ScanSettings.HiddenNetwork> hiddenNetworkList =
                mWifiConfigManager.retrieveHiddenNetworkList();
        settings.hiddenNetworks = hiddenNetworkList.toArray(
                new WifiScanner.ScanSettings.HiddenNetwork[hiddenNetworkList.size()]);
    }
    mWifiScanner.startScan(settings, new ScanRequestProxyScanListener(), workSource);
    return true;
}

        mWifiScanner.startScan(settings, new ScanRequestProxyScanListener(), workSource)调用WifiScanner类的startScan方法扫描,同时设置扫描回调监听,当扫描结束后通过监听回调。

        我先分析如何扫描,然后再分析ScanRequestProxyScanListener扫描监听回调。

xref: /frameworks/base/wifi/java/android/net/wifi/WifiScanner.java        

@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
public void startScan(ScanSettings settings, ScanListener listener, WorkSource workSource) {
    Preconditions.checkNotNull(listener, "listener cannot be null");
    int key = addListener(listener);
    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());
    mAsyncChannel.sendMessage(CMD_START_SINGLE_SCAN, 0, key, scanParams);
}

        通过AsyncChannel发送message(CMD_START_SINGLE_SCAN),最终在WifiScanningServiceImpl.java里面处理

xref: /frameworks/opt/net/wifi/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java

class WifiSingleScanStateMachine extends StateMachine implements WifiNative.ScanEventHandler {
    /**
     * Maximum age of results that we return from our cache via
     * {@link WifiScanner#getScanResults()}.
     * This is currently set to 3 minutes to restore parity with the wpa_supplicant's scan
     * result cache expiration policy. (See b/62253332 for details)
     */
    @VisibleForTesting
    public static final int CACHED_SCAN_RESULTS_MAX_AGE_IN_MILLIS = 180 * 1000;

    private final DefaultState mDefaultState = new DefaultState();
    private final DriverStartedState mDriverStartedState = new DriverStartedState();
    private final IdleState  mIdleState  = new IdleState();
    private final ScanningState  mScanningState  = new ScanningState();

    private WifiNative.ScanSettings mActiveScanSettings = null;
    private RequestList<ScanSettings> mActiveScans = new RequestList<>();
    private RequestList<ScanSettings> mPendingScans = new RequestList<>();

    // Scan results cached from the last full single scan request.
    private final List<ScanResult> mCachedScanResults = new ArrayList<>();

    WifiSingleScanStateMachine(Looper looper) {
        super("WifiSingleScanStateMachine", looper);

        setLogRecSize(128);
        setLogOnlyTransitions(false);

        // CHECKSTYLE:OFF IndentationCheck
        addState(mDefaultState);
            addState(mDriverStartedState, mDefaultState);
                addState(mIdleState, mDriverStartedState);
                addState(mScanningState, mDriverStartedState);
        // CHECKSTYLE:ON IndentationCheck

        setInitialState(mDefaultState);
    }

    .....................................................................................


    /**
     * State representing when the driver is running. This state is not meant to be transitioned
     * directly, but is instead intended as a parent state of ScanningState and IdleState
     * to hold common functionality and handle cleaning up scans when the driver is shut down.
     */
    class DriverStartedState extends State {
	
    .....................................................................................................

        @Override
        public boolean processMessage(Message msg) {
            ClientInfo ci = mClients.get(msg.replyTo);

            switch (msg.what) {
                case CMD_DRIVER_LOADED:
                    // Ignore if we're already in driver loaded state.
                    return HANDLED;
                case WifiScanner.CMD_START_SINGLE_SCAN:
                    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 HANDLED;
                    }
                    scanParams.setDefusable(true);
                    ScanSettings scanSettings =
                            scanParams.getParcelable(WifiScanner.SCAN_PARAMS_SCAN_SETTINGS_KEY);
                    WorkSource workSource =
                            scanParams.getParcelable(WifiScanner.SCAN_PARAMS_WORK_SOURCE_KEY);
                    if (validateScanRequest(ci, handler, scanSettings)) {
                        mWifiMetrics.incrementOneshotScanCount();
                        if (scanSettings.band == WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY
                                || scanSettings.band == WifiScanner.WIFI_BAND_5_GHZ_WITH_DFS
                                || scanSettings.band == WifiScanner.WIFI_BAND_BOTH_WITH_DFS) {
                            mWifiMetrics.incrementOneshotScanWithDfsCount();
                        }
                        logScanRequest("addSingleScanRequest", ci, handler, workSource,
                                scanSettings, null);
                        replySucceeded(msg);

                        // 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 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.
                        if (getCurrentState() == mScanningState) {
                            if (activeScanSatisfies(scanSettings)) {
                                mActiveScans.addRequest(ci, handler, workSource, scanSettings);
                            } else {
                                mPendingScans.addRequest(ci, handler, workSource, scanSettings);
                            }
                        } else {
                            mPendingScans.addRequest(ci, handler, workSource, scanSettings);
                            tryToStartNewScan();
                        }
                    } else {
                        logCallback("singleScanInvalidRequest",  ci, handler, "bad request");
                        replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request");
                        mWifiMetrics.incrementScanReturnEntry(
                                WifiMetricsProto.WifiLog.SCAN_FAILURE_INVALID_CONFIGURATION, 1);
                    }
                    return HANDLED;
                case WifiScanner.CMD_STOP_SINGLE_SCAN:
                    removeSingleScanRequest(ci, msg.arg2);
                    return HANDLED;
                default:
                    return NOT_HANDLED;
            }
        }
    }

    ..........................................................................................................

    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, getNativeScanType(entry.settings.type));
            channels.addChannels(entry.settings);
            if (entry.settings.hiddenNetworks != null) {
                for (int i = 0; i < entry.settings.hiddenNetworks.length; i++) {
                    WifiNative.HiddenNetwork hiddenNetwork = new WifiNative.HiddenNetwork();
                    hiddenNetwork.ssid = entry.settings.hiddenNetworks[i].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 (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 (mScannerImpl.startSingleScan(settings, this)) {
            // 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());
            // notify and cancel failed scans
            sendOpFailedToAllAndClear(mPendingScans, WifiScanner.REASON_UNSPECIFIED,
                    "Failed to start single scan");
        }
    }
}

        在tryToStartNewScan方法里面会调用用WifiScannerImpl类的startSingleScan方法。WifiScannerImpl是个抽象类,具体实现方法是在WificondScannerImpl.java类中。

xref: /frameworks/opt/net/wifi/service/java/com/android/server/wifi/scanner/WificondScannerImpl.java

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.getElapsedSinceBootMillis(),
                    reportFullResults, allFreqs, eventHandler);

        boolean success = false;
        Set<Integer> freqs;
        if (!allFreqs.isEmpty()) {
            freqs = allFreqs.getScanFreqs();
            success = mWifiNative.scan(
                    mIfaceName, settings.scanType, freqs, hiddenNetworkSSIDSet);
            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);
            }

            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(new Runnable() {
                    @Override public void run() {
                        reportScanFailure();
                    }
                });
        }

        return true;
    }
}

        在startSingleScan方法里面会调用WifiNative里面的scan方法(mWifiNative.scan)。

xref: /frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiNative.java

public boolean scan(
        @NonNull String ifaceName, int scanType, Set<Integer> freqs,
        List<String> hiddenNetworkSSIDs) {
    return mWificondControl.scan(ifaceName, scanType, freqs, hiddenNetworkSSIDs);
}

        调用WificondControl类的scan方法

xref: /frameworks/opt/net/wifi/service/java/com/android/server/wifi/WificondControl.java
 

public boolean scan(@NonNull String ifaceName,
                    int scanType,
                    Set<Integer> freqs,
                    List<String> hiddenNetworkSSIDs) {
    IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName);
    if (scannerImpl == null) {
        Log.e(TAG, "No valid wificond scanner interface handler");
        return false;
    }
    SingleScanSettings settings = new SingleScanSettings();
    try {
        settings.scanType = getScanType(scanType);
    } catch (IllegalArgumentException e) {
        Log.e(TAG, "Invalid scan type ", e);
        return false;
    }
    settings.channelSettings  = new ArrayList<>();
    settings.hiddenNetworks  = new ArrayList<>();

    if (freqs != null) {
        for (Integer freq : freqs) {
            ChannelSettings channel = new ChannelSettings();
            channel.frequency = freq;
            settings.channelSettings.add(channel);
        }
    }
    if (hiddenNetworkSSIDs != null) {
        for (String ssid : hiddenNetworkSSIDs) {
            HiddenNetwork network = new HiddenNetwork();
            try {
                network.ssid = NativeUtil.byteArrayFromArrayList(NativeUtil.decodeSsid(ssid));
            } catch (IllegalArgumentException e) {
                Log.e(TAG, "Illegal argument " + ssid, e);
                continue;
            }
            // settings.hiddenNetworks is expected to be very small, so this shouldn't cause
            // any performance issues.
            if (!settings.hiddenNetworks.contains(network)) {
                settings.hiddenNetworks.add(network);
            }
        }
    }

    try {
        return scannerImpl.scan(settings);
    } catch (RemoteException e1) {
        Log.e(TAG, "Failed to request scan due to remote exception");
    }
    return false;
}

        调用scanner_impl.cpp中的scan方法

xref: /system/connectivity/wificond/scanning/scanner_impl.cpp

Status ScannerImpl::scan(const SingleScanSettings& scan_settings,
                         bool* out_success) {
  if (!CheckIsValid()) {
    *out_success = false;
    return Status::ok();
  }

  if (scan_started_) {
    LOG(WARNING) << "Scan already started";
  }
  // Only request MAC address randomization when station is not associated.
  bool request_random_mac =
      wiphy_features_.supports_random_mac_oneshot_scan &&
      !client_interface_->IsAssociated();
  int scan_type = scan_settings.scan_type_;
  if (!IsScanTypeSupported(scan_settings.scan_type_, wiphy_features_)) {
    LOG(DEBUG) << "Ignoring scan type because device does not support it";
    scan_type = SCAN_TYPE_DEFAULT;
  }

  // Initialize it with an empty ssid for a wild card scan.
  vector<vector<uint8_t>> ssids = {{}};

  vector<vector<uint8_t>> skipped_scan_ssids;
  for (auto& network : scan_settings.hidden_networks_) {
    if (ssids.size() + 1 > scan_capabilities_.max_num_scan_ssids) {
      skipped_scan_ssids.emplace_back(network.ssid_);
      continue;
    }
    ssids.push_back(network.ssid_);
  }

  LogSsidList(skipped_scan_ssids, "Skip scan ssid for single scan");

  vector<uint32_t> freqs;
  for (auto& channel : scan_settings.channel_settings_) {
    freqs.push_back(channel.frequency_);
  }

  int error_code = 0;
  if (!scan_utils_->Scan(interface_index_, request_random_mac, scan_type,
                         ssids, freqs, &error_code)) {
    CHECK(error_code != ENODEV) << "Driver is in a bad state, restarting wificond";
    *out_success = false;
    return Status::ok();
  }
  scan_started_ = true;
  *out_success = true;
  return Status::ok();
}

Status ScannerImpl::startPnoScan(const PnoSettings& pno_settings,
                                 bool* out_success) {
  pno_settings_ = pno_settings;
  pno_scan_results_from_offload_ = false;
  LOG(VERBOSE) << "startPnoScan";
  if (offload_scan_supported_ && StartPnoScanOffload(pno_settings)) {
    // scanning over offload succeeded
    *out_success = true;
  } else {
    *out_success = StartPnoScanDefault(pno_settings);
  }
  return Status::ok();
}

        调用scan_utils.app的Scan方法

xref: /system/connectivity/wificond/scanning/scan_utils.cpp

bool ScanUtils::Scan(uint32_t interface_index,
                     bool request_random_mac,
                     int scan_type,
                     const vector<vector<uint8_t>>& ssids,
                     const vector<uint32_t>& freqs,
                     int* error_code) {
  NL80211Packet trigger_scan(
      netlink_manager_->GetFamilyId(),
      NL80211_CMD_TRIGGER_SCAN,
      netlink_manager_->GetSequenceNumber(),
      getpid());
  // If we do not use NLM_F_ACK, we only receive a unicast repsonse
  // when there is an error. If everything is good, scan results notification
  // will only be sent through multicast.
  // If NLM_F_ACK is set, there will always be an unicast repsonse, either an
  // ERROR or an ACK message. The handler will always be called and removed by
  // NetlinkManager.
  trigger_scan.AddFlag(NLM_F_ACK);
  NL80211Attr<uint32_t> if_index_attr(NL80211_ATTR_IFINDEX, interface_index);

  NL80211NestedAttr ssids_attr(NL80211_ATTR_SCAN_SSIDS);
  for (size_t i = 0; i < ssids.size(); i++) {
    ssids_attr.AddAttribute(NL80211Attr<vector<uint8_t>>(i, ssids[i]));
  }
  NL80211NestedAttr freqs_attr(NL80211_ATTR_SCAN_FREQUENCIES);
  for (size_t i = 0; i < freqs.size(); i++) {
    freqs_attr.AddAttribute(NL80211Attr<uint32_t>(i, freqs[i]));
  }

  trigger_scan.AddAttribute(if_index_attr);
  trigger_scan.AddAttribute(ssids_attr);
  // An absence of NL80211_ATTR_SCAN_FREQUENCIES attribue informs kernel to
  // scan all supported frequencies.
  if (!freqs.empty()) {
    trigger_scan.AddAttribute(freqs_attr);
  }

  uint32_t scan_flags = 0;
  if (request_random_mac) {
    scan_flags |= NL80211_SCAN_FLAG_RANDOM_ADDR;
  }
  switch (scan_type) {
    case IWifiScannerImpl::SCAN_TYPE_LOW_SPAN:
      scan_flags |= NL80211_SCAN_FLAG_LOW_SPAN;
      break;
    case IWifiScannerImpl::SCAN_TYPE_LOW_POWER:
      scan_flags |= NL80211_SCAN_FLAG_LOW_POWER;
      break;
    case IWifiScannerImpl::SCAN_TYPE_HIGH_ACCURACY:
      scan_flags |= NL80211_SCAN_FLAG_HIGH_ACCURACY;
      break;
    case IWifiScannerImpl::SCAN_TYPE_DEFAULT:
      break;
    default:
      CHECK(0) << "Invalid scan type received: " << scan_type;
  }
  if (scan_flags) {
    trigger_scan.AddAttribute(
        NL80211Attr<uint32_t>(NL80211_ATTR_SCAN_FLAGS,
                              scan_flags));
  }
  // We are receiving an ERROR/ACK message instead of the actual
  // scan results here, so it is OK to expect a timely response because
  // kernel is supposed to send the ERROR/ACK back before the scan starts.
  vector<unique_ptr<const NL80211Packet>> response;
  if (!netlink_manager_->SendMessageAndGetAckOrError(trigger_scan,
                                                     error_code)) {
    // Logging is done inside |SendMessageAndGetAckOrError|.
    return false;
  }
  if (*error_code != 0) {
    LOG(ERROR) << "NL80211_CMD_TRIGGER_SCAN failed: " << strerror(*error_code);
    return false;
  }
  return true;
}

        通过trigger_scan方法将数据发送给内核开启wifi扫描。接下来我们分析ScanRequestProxyScanListener扫描监听回调。

xref: /frameworks/opt/net/wifi/service/java/com/android/server/wifi/ScanRequestProxy.java

private class GlobalScanListener implements WifiScanner.ScanListener {
    @Override
    public void onSuccess() {
        // Ignore. These will be processed from the scan request listener.
    }

    @Override
    public void onFailure(int reason, String description) {
        // Ignore. These will be processed from the scan request listener.
    }

    @Override
    public void onResults(WifiScanner.ScanData[] scanDatas) {
        if (mVerboseLoggingEnabled) {
            Log.d(TAG, "Scan results received");
        }
        // For single scans, the array size should always be 1.
        if (scanDatas.length != 1) {
            Log.wtf(TAG, "Found more than 1 batch of scan results, Failing...");
            sendScanResultBroadcast(false);
            return;
        }
        WifiScanner.ScanData scanData = scanDatas[0];
        ScanResult[] scanResults = scanData.getResults();
        if (mVerboseLoggingEnabled) {
            Log.d(TAG, "Received " + scanResults.length + " scan results");
        }
        // Only process full band scan results.
        if (scanData.getBandScanned() == WifiScanner.WIFI_BAND_BOTH_WITH_DFS) {
            // Store the last scan results & send out the scan completion broadcast.
            mLastScanResults.clear();
            mLastScanResults.addAll(Arrays.asList(scanResults));
            sendScanResultBroadcast(true);
        }
    }

    @Override
    public void onFullResult(ScanResult fullScanResult) {
        // Ignore for single scans.
    }

    @Override
    public void onPeriodChanged(int periodInMs) {
        // Ignore for single scans.
    }
};

// Common scan listener for scan requests initiated by this class.
private class ScanRequestProxyScanListener implements WifiScanner.ScanListener {
    @Override
    public void onSuccess() {
        // Scan request succeeded, wait for results to report to external clients.
        if (mVerboseLoggingEnabled) {
            Log.d(TAG, "Scan request succeeded");
        }
    }

    @Override
    public void onFailure(int reason, String description) {
        Log.e(TAG, "Scan failure received. reason: " + reason + ",description: " + description);
        sendScanResultBroadcast(false);
    }

    @Override
    public void onResults(WifiScanner.ScanData[] scanDatas) {
        // Ignore. These will be processed from the global listener.
    }

    @Override
    public void onFullResult(ScanResult fullScanResult) {
        // Ignore for single scans.
    }

    @Override
    public void onPeriodChanged(int periodInMs) {
        // Ignore for single scans.
    }
};



...................................................................................



/**
 * Helper method to send the scan request status broadcast.
 */
private void sendScanResultBroadcast(boolean scanSucceeded) {
    Intent intent = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
    intent.putExtra(WifiManager.EXTRA_RESULTS_UPDATED, scanSucceeded);
    mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
}

        在ScanRequestProxyScanListener的onResults方法里面有一行这样的注释(Ignore. These will be processed from the global listener.),这个方法已经丢弃了,我们看GlobalScanListener的onResults方法,onResult是底层完成扫描后的上报,把扫描结果添加到mLastScanResult中。调用sendScanResultBroadcast方法发送广播SCAN_RESULTS_AVAILABLE_ACTION通知WifiTracker获取扫描结果。

xref: /frameworks/base/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java

final BroadcastReceiver mReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();

        if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
            updateWifiState(
                    intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
                            WifiManager.WIFI_STATE_UNKNOWN));
        } else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action)) {
            mStaleScanResults = false;
            mLastScanSucceeded =
                    intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED, true);

            fetchScansAndConfigsAndUpdateAccessPoints();
        } else if (WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION.equals(action)
                || WifiManager.LINK_CONFIGURATION_CHANGED_ACTION.equals(action)) {
            fetchScansAndConfigsAndUpdateAccessPoints();
        } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
            // TODO(sghuman): Refactor these methods so they cannot result in duplicate
            // onAccessPointsChanged updates being called from this intent.
            NetworkInfo info = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
            updateNetworkInfo(info);
            fetchScansAndConfigsAndUpdateAccessPoints();
        } else if (WifiManager.RSSI_CHANGED_ACTION.equals(action)) {
            NetworkInfo info =
                    mConnectivityManager.getNetworkInfo(mWifiManager.getCurrentNetwork());
            updateNetworkInfo(info);
        }
    }
};


........................................................................................


private void fetchScansAndConfigsAndUpdateAccessPoints() {
    List<ScanResult> newScanResults = mWifiManager.getScanResults();

    // Filter all unsupported networks from the scan result list
    final List<ScanResult> filteredScanResults =
            filterScanResultsByCapabilities(newScanResults);

    if (isVerboseLoggingEnabled()) {
        Log.i(TAG, "Fetched scan results: " + filteredScanResults);
    }

    List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
    updateAccessPoints(filteredScanResults, configs);
}

        在mReceiver中走SCAN_RESULTS_AVAILABLE_ACTION分支,调用fetchScansAndConfigsAndUpdateAccessPoints()方法。通过mWifiManager.getScanResults获取扫描数据。

xref: /frameworks/base/wifi/java/android/net/wifi/WifiManager.java

public List<ScanResult> getScanResults() {
    try {
        return mService.getScanResults(mContext.getOpPackageName());
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

        mService的具体实现类是WifiServiceImpl.java。所有我们直接去看WifiServiceImpl.java类里面的getScanResults方法

xref: /frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiServiceImpl.java

       

@Override
public List<ScanResult> getScanResults(String callingPackage) {
    enforceAccessPermission();
    int uid = Binder.getCallingUid();
    long ident = Binder.clearCallingIdentity();
    if (mVerboseLoggingEnabled) {
        mLog.info("getScanResults uid=%").c(uid).flush();
    }
    try {
        mWifiPermissionsUtil.enforceCanAccessScanResults(callingPackage, uid);
        final List<ScanResult> scanResults = new ArrayList<>();
        boolean success = mWifiInjector.getClientModeImplHandler().runWithScissors(() -> {
            scanResults.addAll(mScanRequestProxy.getScanResults());
        }, RUN_WITH_SCISSORS_TIMEOUT_MILLIS);
        if (!success) {
            Log.e(TAG, "Failed to post runnable to fetch scan results");
            return new ArrayList<ScanResult>();
        }
        return scanResults;
    } catch (SecurityException e) {
        Slog.e(TAG, "Permission violation - getScanResults not allowed for uid="
                + uid + ", packageName=" + callingPackage + ", reason=" + e);
        return new ArrayList<ScanResult>();
    } finally {
        Binder.restoreCallingIdentity(ident);
    }
}

        通过mScanRequestProxy.getScanResults()我们又回到了ScanRequestProxy.java里面。

xref: /frameworks/opt/net/wifi/service/java/com/android/server/wifi/ScanRequestProxy.java

public List<ScanResult> getScanResults() {
    return mLastScanResults;
}

        mLastScanResults就是我们之前在onResults方法里面遇到过的,就是之前广播接收到的扫描结果,mLastScanResults.addAll(Arrays.asList(scanResults))。

xref: /frameworks/base/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java   

private void fetchScansAndConfigsAndUpdateAccessPoints() {
    List<ScanResult> newScanResults = mWifiManager.getScanResults();

    // Filter all unsupported networks from the scan result list
    final List<ScanResult> filteredScanResults =
            filterScanResultsByCapabilities(newScanResults);

    if (isVerboseLoggingEnabled()) {
        Log.i(TAG, "Fetched scan results: " + filteredScanResults);
    }

    List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
    updateAccessPoints(filteredScanResults, configs);
}



...........................................................................................


private void updateAccessPoints(final List<ScanResult> newScanResults,
        List<WifiConfiguration> configs) {

    // Map configs and scan results necessary to make AccessPoints
    final Map<String, WifiConfiguration> configsByKey = new ArrayMap(configs.size());
    if (configs != null) {
        for (WifiConfiguration config : configs) {
            configsByKey.put(AccessPoint.getKey(config), config);
        }
    }

    WifiConfiguration connectionConfig = null;
    if (mLastInfo != null) {
        connectionConfig = getWifiConfigurationForNetworkId(mLastInfo.getNetworkId(), configs);
    }

    // Rather than dropping and reacquiring the lock multiple times in this method, we lock
    // once for efficiency of lock acquisition time and readability
    synchronized (mLock) {
        ArrayMap<String, List<ScanResult>> scanResultsByApKey =
                updateScanResultCache(newScanResults);

        // Swap the current access points into a cached list for maintaining AP listeners
        List<AccessPoint> cachedAccessPoints;
        cachedAccessPoints = new ArrayList<>(mInternalAccessPoints);

        ArrayList<AccessPoint> accessPoints = new ArrayList<>();

        final List<NetworkKey> scoresToRequest = new ArrayList<>();

        for (Map.Entry<String, List<ScanResult>> entry : scanResultsByApKey.entrySet()) {
            for (ScanResult result : entry.getValue()) {
                NetworkKey key = NetworkKey.createFromScanResult(result);
                if (key != null && !mRequestedScores.contains(key)) {
                    scoresToRequest.add(key);
                }
            }

            AccessPoint accessPoint =
                    getCachedOrCreate(entry.getValue(), cachedAccessPoints);

            // Update the matching config if there is one, to populate saved network info
            accessPoint.update(configsByKey.get(entry.getKey()));

            accessPoints.add(accessPoint);
        }

        List<ScanResult> cachedScanResults = new ArrayList<>(mScanResultCache.values());

        // Add a unique Passpoint AccessPoint for each Passpoint profile's FQDN.
        accessPoints.addAll(updatePasspointAccessPoints(
                mWifiManager.getAllMatchingWifiConfigs(cachedScanResults), cachedAccessPoints));

        // Add OSU Provider AccessPoints
        accessPoints.addAll(updateOsuAccessPoints(
                mWifiManager.getMatchingOsuProviders(cachedScanResults), cachedAccessPoints));

        if (mLastInfo != null && mLastNetworkInfo != null) {
            for (AccessPoint ap : accessPoints) {
                ap.update(connectionConfig, mLastInfo, mLastNetworkInfo);
            }
        }

        // If there were no scan results, create an AP for the currently connected network (if
        // it exists).
        if (accessPoints.isEmpty() && connectionConfig != null) {
            AccessPoint activeAp = new AccessPoint(mContext, connectionConfig);
            activeAp.update(connectionConfig, mLastInfo, mLastNetworkInfo);
            accessPoints.add(activeAp);
            scoresToRequest.add(NetworkKey.createFromWifiInfo(mLastInfo));
        }

        requestScoresForNetworkKeys(scoresToRequest);
        for (AccessPoint ap : accessPoints) {
            ap.update(mScoreCache, mNetworkScoringUiEnabled, mMaxSpeedLabelScoreCacheAge);
        }

        // Pre-sort accessPoints to speed preference insertion
        Collections.sort(accessPoints);

        // Log accesspoints that are being removed
        if (DBG()) {
            Log.d(TAG,
                    "------ Dumping AccessPoints that were not seen on this scan ------");
            for (AccessPoint prevAccessPoint : mInternalAccessPoints) {
                String prevTitle = prevAccessPoint.getTitle();
                boolean found = false;
                for (AccessPoint newAccessPoint : accessPoints) {
                    if (newAccessPoint.getTitle() != null && newAccessPoint.getTitle()
                            .equals(prevTitle)) {
                        found = true;
                        break;
                    }
                }
                if (!found)
                    Log.d(TAG, "Did not find " + prevTitle + " in this scan");
            }
            Log.d(TAG,
                    "---- Done dumping AccessPoints that were not seen on this scan ----");
        }

        mInternalAccessPoints.clear();
        mInternalAccessPoints.addAll(accessPoints);
    }

    conditionallyNotifyListeners();
}

        再回到WifiTracker.java类里面的fetchScansAndConfigsAndUpdateAccessPoints方法。通过fetchScansAndConfigsAndUpdateAccessPoints拿到的扫描结果其实就是mLastScanResults。捋清楚这个流程很重要。接下来调用updateAccessPoints方法,将扫描结果添加到mInternalAccessPoints中。

11-14 13:07