Wifi模块—源码分析Wifi热点扫描(Android P)

一 前言

       这次接着讲Wifi工程流程中的Wifi热点查找过程,也是Wifi启动的过程延续,Wifi启动过程中会更新Wifi的状态,框架层也有相应广播发出,应用层接收到广播后开始进行热点的扫描。可以先看前面Wifi启动的分析过程。

                               Wifi模块—源码分析Wifi启动1(Android P)

                               Wifi模块—源码分析Wifi启动2(Android P)

 

二 图示调用流程

      由于在 frameworks/base/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java直接接收到框架层发出的wifi状态改变的广播WIFI_STATE_CHANGED_ACTION(这个在后面有交待),所以这里的图示调用流程将从WifiTracker.java开始。

三 代码具体流程

       我们先回顾一下之前wifi启动过程的相关细节,wifi启动过程会走到ClientModeStateMachine这个状态机,具体看 Wifi模块—源码分析Wifi启动1(Android P)

frameworks/opt/net/wifi/service/java/com/android/server/wifi/ClientModeManager.java 

看ClientModeStateMachine的构造函数。

ClientModeStateMachine(Looper looper) {
    super(TAG, looper);

    addState(mIdleState);
    addState(mStartedState);

    setInitialState(mIdleState);
    start();
}

再看start()。

/**
* Start client mode.
*/
public void start() {
    mStateMachine.sendMessage(ClientModeStateMachine.CMD_START);
}

发送了一个消息ClientModeStateMachine.CMD_START。

private class IdleState extends State {

    @Override
    public void enter() {
        Log.d(TAG, "entering IdleState");
        mClientInterfaceName = null;
        mIfaceIsUp = false;
    }

    @Override
    public boolean processMessage(Message message) {
        switch (message.what) {
            case CMD_START:
                updateWifiState(WifiManager.WIFI_STATE_ENABLING,
                                    WifiManager.WIFI_STATE_DISABLED);

                mClientInterfaceName = mWifiNative.setupInterfaceForClientMode(false /* not low priority */, mWifiNativeInterfaceCallback);
                if (TextUtils.isEmpty(mClientInterfaceName)) {
                    Log.e(TAG, "Failed to create ClientInterface. Sit in Idle");
                    updateWifiState(WifiManager.WIFI_STATE_UNKNOWN,
                                WifiManager.WIFI_STATE_ENABLING);
                    updateWifiState(WifiManager.WIFI_STATE_DISABLED,
                                        WifiManager.WIFI_STATE_UNKNOWN);
                    break;
                }
                sendScanAvailableBroadcast(false);
                mScanRequestProxy.enableScanningForHiddenNetworks(false);
                mScanRequestProxy.clearScanResults();
                transitionTo(mStartedState);
                break;
            default:
                Log.d(TAG, "received an invalid message: " + message);
                return NOT_HANDLED;
        }
        return HANDLED;
    }
}

在IdleState的状态里接收到消息做相关处理updateWifiState,继续看这个方法。

/**
     * Update Wifi state and send the broadcast.
     * @param newState new Wifi state
     * @param currentState current wifi state
*/
private void updateWifiState(int newState, int currentState) {
    if (!mExpectedStop) {
        mListener.onStateChanged(newState);
    } else {
        Log.d(TAG, "expected stop, not triggering callbacks: newState = "
            + newState);
    }

    // Once we report the mode has stopped/failed any other stop signals are redundant
    // note: this can happen in failure modes where we get multiple callbacks as underlying
    // components/interface stops or the underlying interface is destroyed in cleanup
    if (newState == WifiManager.WIFI_STATE_UNKNOWN
                || newState == WifiManager.WIFI_STATE_DISABLED) {
        mExpectedStop = true;
    }

    if (newState == WifiManager.WIFI_STATE_UNKNOWN) {
        // do not need to broadcast failure to system
        return;
    }

    mWifiStateMachine.setWifiStateForApiCalls(newState);

    final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION);
    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
    intent.putExtra(WifiManager.EXTRA_WIFI_STATE, newState);
    intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, currentState);
    mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}

在这里发送了一个广播sendStickyBroadcastAsUser,广播具体是WifiManager.WIFI_STATE_CHANGED_ACTION。

        OK,上面的分析都属于上次分析wifi启动的部分,就是wifi在启动过程中会更新wifi状态并发送wifi状态改变的广播。不过。上次在分析wifi启动过程中也没有分析这个更新wifi状态发送广播的这个内容,这次作为一个补充也是开启接下来的wifi热点扫描的流程分析,接下来的wifi扫描过程是指打开wifi之后自动扫描过程而不是手动去刷新扫描。

 

1 应用层

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

在WifiSettings.java里初始化WifiTracker。

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);

    mWifiTracker = WifiTrackerFactory.create(
            getActivity(), this, getLifecycle(), true, true);
    mWifiManager = mWifiTracker.getManager();
    ...
}

   

1.2 packages/apps/Settings/src/com/android/settings/wifi/WifiEnabler.java

       应用层在扫描过程中貌似没做什么事情但暂且提一下WifiEnabler也会接收到WifiManager.WIFI_STATE_CHANGED_ACTION广播并会相关的处理。

private boolean mStateMachineEvent;
private final IntentFilter mIntentFilter;
private 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)) {
            handleWifiStateChanged(mWifiManager.getWifiState());
        } else if (WifiManager.SUPPLICANT_STATE_CHANGED_ACTION.equals(action)) {
            if (!mConnected.get()) {
                handleStateChanged(WifiInfo.getDetailedStateOf((SupplicantState)
                        intent.getParcelableExtra(WifiManager.EXTRA_NEW_STATE)));
            }
        } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
            NetworkInfo info = (NetworkInfo) intent.getParcelableExtra(
                    WifiManager.EXTRA_NETWORK_INFO);
            mConnected.set(info.isConnected());
            handleStateChanged(info.getDetailedState());
        }
    }
};

看handleWifiStateChanged。

private void handleWifiStateChanged(int state) {
    // Clear any previous state
    mSwitchWidget.setDisabledByAdmin(null);

    switch (state) {
        case WifiManager.WIFI_STATE_ENABLING:
            break;
        case WifiManager.WIFI_STATE_ENABLED:
            setSwitchBarChecked(true);
            mSwitchWidget.setEnabled(true);
            break;
        case WifiManager.WIFI_STATE_DISABLING:
            break;
        case WifiManager.WIFI_STATE_DISABLED:
            setSwitchBarChecked(false);
            mSwitchWidget.setEnabled(true);
            break;
        default:
            setSwitchBarChecked(false);
            mSwitchWidget.setEnabled(true);
    }

    if (RestrictedLockUtils.hasBaseUserRestriction(mContext,
            UserManager.DISALLOW_CONFIG_TETHERING, UserHandle.myUserId())) {
        mSwitchWidget.setEnabled(false);
    } else {
        final EnforcedAdmin admin = RestrictedLockUtils.checkIfRestrictionEnforced(mContext,
            UserManager.DISALLOW_CONFIG_TETHERING, UserHandle.myUserId());
        mSwitchWidget.setDisabledByAdmin(admin);
    }
}

根据wifi状态,switchBar的状态会变化。

 

2 java框架层

     真正的扫描过程从这里开始。framework/base/packages/SettingsLib。SettingsLib放在frameworks/base/packages/SettingsLib,因为SystemUi和开机向导中的蓝牙WiFi流程也会用到对应的代码。

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

/**
*  Receiver for handling broadcasts.
*
*  This receiver is registered on the WorkHandler.
*/
@VisibleForTesting
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;

            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);
        }
    }
};

看updateWifiState。

/**
* Handles updates to WifiState.
*
* <p>If Wifi is not enabled in the enabled state, {@link #mStaleScanResults} will be set to
* true.
*/
private void updateWifiState(int state) {
    if (state == WifiManager.WIFI_STATE_ENABLED) {
        if (mScanner != null) {
            // We only need to resume if mScanner isn't null because
            // that means we want to be scanning.
            mScanner.resume();
        }
    } else {
        clearAccessPointsAndConditionallyUpdate();
        mLastInfo = null;
        mLastNetworkInfo = null;
        if (mScanner != null) {
            mScanner.pause();
        }
        mStaleScanResults = true;
    }
    mListener.onWifiStateChanged(state);
}

看mScanner.resume,其中Scanner是内部类。

@VisibleForTesting
class Scanner extends Handler {
    static final int MSG_SCAN = 0;

    private int mRetry = 0;

    void resume() {
        if (!hasMessages(MSG_SCAN)) {
            sendEmptyMessage(MSG_SCAN);
        }
    }

    void pause() {
        mRetry = 0;
        removeMessages(MSG_SCAN);
    }

    @VisibleForTesting
    boolean isScanning() {
        return hasMessages(MSG_SCAN);
    }

    @Override
    public void handleMessage(Message message) {
        if (message.what != MSG_SCAN) return;
        if (mWifiManager.startScan()) {
            mRetry = 0;
        } else if (++mRetry >= 3) {
            mRetry = 0;
            if (mContext != null) {
                Toast.makeText(mContext, R.string.wifi_fail_to_scan, Toast.LENGTH_LONG).show();
            }
            return;
        }
        sendEmptyMessageDelayed(MSG_SCAN, WIFI_RESCAN_INTERVAL_MS);
    }
}

       在resume方法里面发送消息MSG_SCAN,并接收该消息进行处理,看handleMessage。会调用mWifiManager.startScan,并且每隔10s再次进行扫描。

 

2.2 frameworks/base/wifi/java/android/net/wifi/WifiManager.java

* <p>
* To initiate a Wi-Fi scan, declare the
* {@link android.Manifest.permission#CHANGE_WIFI_STATE}
* permission in the manifest, and perform these steps:
* </p>
* <ol style="1">
* <li>Invoke the following method:
* {@code ((WifiManager) getSystemService(WIFI_SERVICE)).startScan()}</li>
* <li>
* Register a BroadcastReceiver to listen to
* {@code SCAN_RESULTS_AVAILABLE_ACTION}.</li>
* <li>When a broadcast is received, call:
* {@code ((WifiManager) getSystemService(WIFI_SERVICE)).getScanResults()}</li>
* </ol>
* @return {@code true} if the operation succeeded, i.e., the scan was initiated.
* @deprecated The ability for apps to trigger scan requests will be removed in a future
* release.
*/
@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();
        return mService.startScan(packageName);
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

看mService.startScan。这个mService是远程的服务端WifiService,其实现类为WifiServiceImpl,这个过程也是跨进程调用。

 

2.3  frameworks/opt/net/wifi/service/java/com/android/server/wifi/wifiServiceImpl.java

/**
* See {@link android.net.wifi.WifiManager#startScan}
*
* @param packageName Package name of the app that requests wifi scan.
*/
@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.getWifiStateMachineHandler()
                .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) {
        return false;
    } finally {
        Binder.restoreCallingIdentity(ident);
    }
    return true;
}

继续看mScanRequestProxy.startScan。

 

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

/**
* 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 (!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 from apps without NETWORK_SETTINGS permission.
    if (!fromSettingsOrSetupWizard
            && 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);

    // 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);
    mIsScanProcessingComplete = false;
    return true;
}

看mWifiScanner.startScan

 

2.5  frameworks/base/wifi/java/android/net/wifi/WifiScanner.java

/**
* starts a single scan and reports results asynchronously
* @param settings specifies various parameters for the scan; for more information look at
* {@link ScanSettings}
* @param listener specifies the object to report events to. This object is also treated as a
*                key for this scan, and must also be specified to cancel the scan. Multiple
*           scans should also not share this object.
*/
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
public void startScan(ScanSettings settings, ScanListener listener) {
    startScan(settings, listener, null);
}

/**
* starts a single scan and reports results asynchronously
* @param settings specifies various parameters for the scan; for more information look at
* {@link ScanSettings}
* @param workSource WorkSource to blame for power usage
* @param listener specifies the object to report events to. This object is also treated as a
*                 key for this scan, and must also be specified to cancel the scan. Multiple
*                 scans should also not share this object.
*/
@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);
    mAsyncChannel.sendMessage(CMD_START_SINGLE_SCAN, 0, key, scanParams);
    
}

        Android P和Android O框架层代码改变不是很大,不过Android O的操作基本都是经过WifiStateMachine状态机,而Android P很多操作都不再经过WifiStateMachine状态机,在这里是为了把scan功能独立分开出来。而和Android O之前的代码相比则有很大区别,这里接下来使用到了双向异步通道的方式,mAsyncChannel.sendMessage。AsyncChannel处理两个handler之间消息异步传递的问题,这两个handler可以在一个进程也可以处于不同的进程。 

 

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

private class ClientHandler extends WifiHandler {

    ClientHandler(String tag, Looper looper) {
        super(tag, looper);
    }

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        ...
        switch (msg.what) {
            case WifiScanner.CMD_START_BACKGROUND_SCAN:
            case WifiScanner.CMD_STOP_BACKGROUND_SCAN:
                mBackgroundScanStateMachine.sendMessage(Message.obtain(msg));
                break;
            case WifiScanner.CMD_START_PNO_SCAN:
            case WifiScanner.CMD_STOP_PNO_SCAN:
                mPnoScanStateMachine.sendMessage(Message.obtain(msg));
                break;
            case WifiScanner.CMD_START_SINGLE_SCAN:
            case WifiScanner.CMD_STOP_SINGLE_SCAN:
                mSingleScanStateMachine.sendMessage(Message.obtain(msg));
                break;
            ...
        }
    }
}

   ClientHandler接收到CMD_START_SINGLE_SCAN消息并处理也就是向状态机发送这个消息,mSingleScanStateMachine.sendMessage(Message.obtain(msg))。SingleScanStateMachine是个处理热点扫描的状态机。

/**
* State machine that holds the state of single scans. Scans should only be active in the
* ScanningState. The pending scans and active scans maps are swapped when entering
* ScanningState. Any requests queued while scanning will be placed in the pending queue and
* executed after transitioning back to IdleState.
*/
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);
    }
    ...
}

状态机有四个状态,初始状态为默认状态。在DriverStarted对CMD_START_SINGLE_SCAN消息进行处理。

/**
* 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:
                mWifiMetrics.incrementOneshotScanCount();
                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)) {
                            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 {
                ...
                }    
        }
    }   
}

看调用的tryToStartNewScan。

void tryToStartNewScan() {
    if (mPendingScans.size() == 0) { // no pending requests
        return;
    }
    ...
    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");
    }
}

看mScannerImpl.startSingleScan。

 

2.7  frameworks/opt/net/wifi/service/java/com/android/server/wifi/scanner/WifiScannerImpl.java

/**
* Start a one time scan. This method should only be called when there is no scan going on
* (after a callback indicating that the previous scan succeeded/failed).
* @return if the scan paramaters are valid
* Note this may return true even if the parameters are not accepted by the chip because the
* scan may be scheduled async.
*/
public abstract boolean startSingleScan(WifiNative.ScanSettings settings,
                 WifiNative.ScanEventHandler eventHandler);

 

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

WificondScannerImpl.java继承抽象类WifiScannerImpl.java,所以复写了startSingleScan。

@Override
public boolean startSingleScan(WifiNative.ScanSettings settings,
        WifiNative.ScanEventHandler eventHandler) {
    ...
    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;
    }
}

看 mWifiNative.scan,java框架层最终还是会调用到native层。

 

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

/**
* Start a scan using wificond for the given parameters.
* @param ifaceName Name of the interface.
* @param scanType Type of scan to perform. One of {@link ScanSettings#SCAN_TYPE_LOW_LATENCY},
* {@link ScanSettings#SCAN_TYPE_LOW_POWER} or {@link ScanSettings#SCAN_TYPE_HIGH_ACCURACY}.
* @param freqs list of frequencies to scan for, if null scan all supported channels.
* @param hiddenNetworkSSIDs List of hidden networks to be scanned for.
* @return Returns true on success.
*/
public boolean scan(@NonNull String ifaceName, int scanType, Set<Integer> freqs,
            Set<String> hiddenNetworkSSIDs) {
    return mWificondControl.scan(ifaceName, scanType, freqs, hiddenNetworkSSIDs);
}

看mWificondControl.scan。

 

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

/**
* Start a scan using wificond for the given parameters.
* @param ifaceName Name of the interface.
* @param scanType Type of scan to perform.
* @param freqs list of frequencies to scan for, if null scan all supported channels.
* @param hiddenNetworkSSIDs List of hidden networks to be scanned for.
* @return Returns true on success.
*/
public boolean scan(@NonNull String ifaceName,
                    int scanType,
                    Set<Integer> freqs,
                    Set<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.add(network);
        }
    }

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

又调到scannerImpl.scan了,看getScannerImpl。

/** Helper function to look up the scanner impl handle using name */
private IWifiScannerImpl getScannerImpl(@NonNull String ifaceName) {
    return mWificondScanners.get(ifaceName);
}

看mWificondScanners怎么来的,从下面可以看到wificondScanner = clientInterface.getWifiScannerImpl()。

/**
* Setup interface for client mode via wificond.
* @return An IClientInterface as wificond client interface binder handler.
* Returns null on failure.
*/
public IClientInterface setupInterfaceForClientMode(@NonNull String ifaceName) {
    Log.d(TAG, "Setting up interface for client mode");
    if (!retrieveWificondAndRegisterForDeath()) {
        return null;
    }

    IClientInterface clientInterface = null;
    try {
        clientInterface = mWificond.createClientInterface(ifaceName);
    } catch (RemoteException e1) {
        Log.e(TAG, "Failed to get IClientInterface due to remote exception");
        return null;
    }

    if (clientInterface == null) {
        Log.e(TAG, "Could not get IClientInterface instance from wificond");
        return null;
    }
    Binder.allowBlocking(clientInterface.asBinder());

    // Refresh Handlers
    mClientInterfaces.put(ifaceName, clientInterface);
    try {
        IWifiScannerImpl wificondScanner = clientInterface.getWifiScannerImpl();
        if (wificondScanner == null) {
            Log.e(TAG, "Failed to get WificondScannerImpl");
            return null;
        }
        mWificondScanners.put(ifaceName, wificondScanner);
        Binder.allowBlocking(wificondScanner.asBinder());
        ScanEventHandler scanEventHandler = new ScanEventHandler(ifaceName);
        mScanEventHandlers.put(ifaceName,  scanEventHandler);
        wificondScanner.subscribeScanEvents(scanEventHandler);
        PnoScanEventHandler pnoScanEventHandler = new PnoScanEventHandler(ifaceName);
        mPnoScanEventHandlers.put(ifaceName,  pnoScanEventHandler);
        wificondScanner.subscribePnoScanEvents(pnoScanEventHandler);
    } catch (RemoteException e) {
        Log.e(TAG, "Failed to refresh wificond scanner due to remote exception");
    }

    return clientInterface;
}

看retrieveWificondAndRegisterForDeath。

/**
* Helper method to retrieve the global wificond handle and register for
* death notifications.
*/
private boolean retrieveWificondAndRegisterForDeath() {
    if (mWificond != null) {
        if (mVerboseLoggingEnabled) {
            Log.d(TAG, "Wificond handle already retrieved");
        }
        // We already have a wificond handle.
        return true;
    }
    mWificond = mWifiInjector.makeWificond();
    if (mWificond == null) {
        Log.e(TAG, "Failed to get reference to wificond");
        return false;
    }
    try {
        mWificond.asBinder().linkToDeath(this, 0);
    } catch (RemoteException e) {
        Log.e(TAG, "Failed to register death notification for wificond");
        // The remote has already died.
        return false;
    }
    return true;
}

看mWiifInjector.makeWificond。

 

2.11  frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiInjector.java

public IWificond makeWificond() {
    // We depend on being able to refresh our binder in WifiStateMachine, so don't cache it.
    IBinder binder = ServiceManager.getService(WIFICOND_SERVICE_NAME);
    return IWificond.Stub.asInterface(binder);
}

再往下走就是C++层了。往下的部分以后有机会再继续深入。

 

四 总结

 wifi框架层从Android O版本开始变化还是挺大的,Android P是延续Android O 的改变做了一些不怎么大的变化:

      (1) 与其他Android版本相比,整体架构还好,多了wificond跟底层通信,framework整个修改比较大;

     (2) Android O  scan、scan_results命令不再通过wpa_supplicant下发到kernel,而是直接由wificond传送到kernel,而scan results的结果也是直接由kernel传给wificond,再由wificond传送给上层去显示;

      (3) wifi direct的p2p find还是通过wpa-supplicant通信 ;

      (4) wifi的连接等操作,还是通过wpa_supplicant进行。

  • 0
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
Android 手机可以使用 `WifiManager` 类来进行扫描周边的 WiFi 热点。用户可以通过调用 `startScan()` 方法来触发扫描操作。下面是一个简单的示例代码,展示了如何使用 Android 扫描周边的 WiFi 热点: ```java // 获取 WifiManager 实例 WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE); // 判断 WiFi 是否已经开启,如果未开启,则先开启 WiFi if (!wifiManager.isWifiEnabled()) { wifiManager.setWifiEnabled(true); } // 开始扫描 WiFi 热点 wifiManager.startScan(); // 获取扫描结果 List<ScanResult> scanResults = wifiManager.getScanResults(); // 遍历扫描结果,取得周围的 WiFi 信息 for (ScanResult scanResult : scanResults) { String ssid = scanResult.SSID; // WiFi 名称 String bssid = scanResult.BSSID; // WiFi 地址 int level = scanResult.level; // WiFi 信号强度 // 其他相关字段,如加密类型等,可以通过 ScanResult 类提供的方法获取 // 进行你想要的处理,比如打印或者保存信息 Log.d("ScanResult", "SSID: " + ssid + ", BSSID: " + bssid + ", Level: " + level); } ``` 上述代码中,我们首先获取了 `WifiManager` 实例,然后检查 WiFi 是否开启。如果没有开启,则调用 `setWifiEnabled(true)` 方法来开启。接下来,我们调用 `startScan()` 方法来触发扫描。最后,通过调用 `getScanResults()` 方法来获取扫描结果,然后遍历结果进行处理。 需要注意的是,为了使用 WiFi 功能,必须在 AndroidManifest.xml 文件中添加以下权限: ```xml <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> ``` 另外,还需要在 Android 6.0 及以上的版本中请求运行时权限来访问位置信息: ```java // 检查是否获取了位置权限 if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { // 请求位置权限 ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 1); } ``` 以上是一个简单的 Android 扫描周边 WiFi 热点的方法。根据你的需求,你可以进一步处理扫描结果,比如筛选特定的 WiFi 热点或者显示在用户界面上。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值