当前位置:   article > 正文

[Android]Android P(9) WIFI学习笔记 - 扫描 (1)_android 获取wifi扫描结果

android 获取wifi扫描结果

前言

  1. 基于Android P源码学习;
  2. 代码片为了方便阅读段经过删、裁减,请以实际源码为准;

请求WLAN芯片开始扫描,然后获取扫描结果,整个过程是分两个阶段:

  1. 请求扫描
  2. 获取结果

本篇会介绍请求扫描阶段的流程,而后者会在下一篇中梳理;

请求扫描

APP入口

WifiManager
//frameworks/base/wifi/java/android/net/wifi/WifiManager.java
    /**
     * Request a scan for access points. Returns immediately. The availability
     * of the results is made known later by means of an asynchronous event sent
     * on completion of the scan.
     * <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();
        }
    }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

注意:

  1. 返回结果仅仅代表是否已经成功下发“开始扫描”的请求,并不代表扫描过程是否成功;
  2. 扫描结果就位后,系统通过SCAN_RESULTS_AVAILABLE_ACTION广播通知;
  3. 该接口已经标记为deprecated,且表示,后续Android版本不再允许APP侧发起扫描请求(这个后面会尝试解释其原因)

然后,我们来到IWifiManager.startScan()的接口实现中(略去不重要的逻辑):

system_server层

WifiServiceImpl
//frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiServiceImpl.java
	public boolean startScan(String packageName) {
		...
		long ident = Binder.clearCallingIdentity();
		...
        try {
            //权限检查
            mWifiPermissionsUtil.enforceCanAccessScanResults(packageName, callingUid);
            //用于在lambda代码块中更新字段(基本数据类型若需要在lambda代码块中使用,必须声明为final,这无法满足重新赋值的需求)
            Mutable<Boolean> scanSuccess = new Mutable<>();
            //切换到WifiStateMachine执行,但在当前线程等待其完成,超时设定默认4s
            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;
            }
            //如果runWithScissorsSuccess,则表示scanSuccess.value不可能为null,因此可以直接拆箱判断
            if (!scanSuccess.value) {
                Log.e(TAG, "Failed to start scan");
                return false;
            }
        } catch (SecurityException e) {
            return false;
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
        return true;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

小结:

  1. 请求开始扫描的逻辑在ScanRequestProxy.startScan()中;
  2. 请求开始扫描的逻辑运行在WifiStateMachine线程,但当前调用线程会等待其执行完成;
  3. 在获取到返回结果,或超时后,调用线程会基于此,决定startScan方法的最终返回值;
ScanRequestProxy
//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) {
    	//获取WifiScanner的实例对象
        if (!retrieveWifiScannerIfNecessary()) {
            Log.e(TAG, "Failed to retrieve wifiscanner");
            sendScanResultFailureBroadcastToPackage(packageName);
            return false;
        }
        //判断调用方是否具备android.Manifest.permission.NETWORK_SETTINGS或android.Manifest.permission.NETWORK_SETUP_WIZARD权限
        boolean fromSettingsOrSetupWizard =
                mWifiPermissionsUtil.checkNetworkSettingsPermission(callingUid)
                        || mWifiPermissionsUtil.checkNetworkSetupWizardPermission(callingUid);
        // 如果不具备上述两个权限,限制其在短时间内频繁请求扫描        if (!fromSettingsOrSetupWizard
                && shouldScanRequestBeThrottledForApp(callingUid, packageName)) {
            Log.i(TAG, "Scan request from " + packageName + " throttled");
            sendScanResultFailureBroadcastToPackage(packageName);
            return false;
        }
        // 如果允许请求扫描,则继续
        WorkSource workSource = new WorkSource(callingUid);

        WifiScanner.ScanSettings settings = new WifiScanner.ScanSettings();
        // 具备上面两条权限的进程,使用高精度扫描
        if (fromSettingsOrSetupWizard) {
            settings.type = WifiScanner.TYPE_HIGH_ACCURACY;
        }
        // 扫描包含所有支持的频段
        settings.band = WifiScanner.WIFI_BAND_BOTH_WITH_DFS;
        settings.reportEvents = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN
                | WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT;
        // 如果需要扫描隐藏网络,则将其添加到hiddenNetworks成员变量中;
        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,表示有一个扫描请求正在进行;
        mIsScanProcessingComplete = false;
        return true;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

小结:

  1. 该方法主要进行了一些权限检查,并对不同权限、不同运行情况(前后台)的进程发起的请求进行对应的限制、参数调整等;
  2. 完成上述判断后,调用WifiScanner.startScan发起扫描请求;
  3. ScanRequestProxyScanListener用于监听扫描实际返回状态与结果;
  4. mIsScanProcessingComplete的作用是,对于连续两次扫描请求的结果,只广播第一次的结果;(是否合理存疑)

那么接下来,我们来到WifiScanner.startScan

WifiScanner
//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);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

小结:

  1. 使用AsyncChannel通知服务端响应消息CMD_START_SINGLE_SCAN
  2. 两个key传递了之前在ScanRequestProxy.startScan()中封装的两个参数;
  3. AsyncChannel的实现这里就不展开了,具体实现的讲解已经在准备中了,结论就是,这里的CMD_START_SINGLE_SCAN,会在WifiScanningServiceImpl.ClientHandler.handleMessage()中被处理;
WifiScanningServiceImpl
//frameworks/opt/net/wifi/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java
private class ClientHandler extends WifiHandler {

		...

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
			...
            switch (msg.what) {
                ...
                case WifiScanner.CMD_START_SINGLE_SCAN:
                case WifiScanner.CMD_STOP_SINGLE_SCAN:
                    mSingleScanStateMachine.sendMessage(Message.obtain(msg));
                    break;
                ...
            }
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

略去了大量不相关的代码,结果发现实际上是通过WifiSingleScanStateMachine.sendMessage将其转发到了其他线程处理(WifiScanningService线程);

这里需要将WifiSingleScanStateMachine状态机的工作情况介绍一下了:
WifiSingleScanStateMachine状态机层级关系

  • 初始状态为DefaultState
  • 在接收到广播WifiManager.WIFI_SCAN_AVAILABLE时,如果Intent中key为WifiManager.EXTRA_SCAN_AVAILABLE的值等于WifiManager.WIFI_STATE_ENABLED,则发送消息CMD_DRIVER_LOADED
  • 在接收到广播WifiManager.WIFI_SCAN_AVAILABLE时,如果Intent中key为WifiManager.EXTRA_SCAN_AVAILABLE的值等于WifiManager.WIFI_STATE_DISABLED,则发送消息CMD_DRIVER_UNLOADED;(此处不涉及,就不过多展开)
  • CMD_DRIVER_LOADEDDefaultState中收到,会将状态机拨到IdleState
  • 而只要不关闭WLAN,后续主要就只在IdleStateScanningState两者之间切换;
  • 顾名思义,触发扫描,就会由IdleState拨到ScanningState,扫描结束后复位到IdleState

回到上面的内容,WifiScanningServiceImpl.ClientHandler在发送的CMD_START_SINGLE_SCAN,在如下几个状态内会被处理:

  1. DefaultState
    class DefaultState extends State {
    	...
         @Override
    	public boolean processMessage(Message msg) {
             switch (msg.what) {
    			...
               	case WifiScanner.CMD_START_SINGLE_SCAN:
                case WifiScanner.CMD_STOP_SINGLE_SCAN:
                    replyFailed(msg, WifiScanner.REASON_UNSPECIFIED, "not available");
                    return HANDLED;
                ...
                default:
                    return NOT_HANDLED;
             }
         }
         ...
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
  2. DriverStartedState
    class DriverStartedState extends State {
        ...
        @Override
        public boolean processMessage(Message msg) {
            ClientInfo ci = mClients.get(msg.replyTo);
    
            switch (msg.what) {
    			...
                case WifiScanner.CMD_START_SINGLE_SCAN:
                    mWifiMetrics.incrementOneshotScanCount();
                    int handler = msg.arg2;
                    Bundle scanParams = (Bundle) msg.obj;
    				...
                    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);
    
                        // 如果当前正在进行扫描,则:
                        // 1. 当前扫描可以满足这次请求(参数匹配),则会在此次扫描结果返回后,直接将其返回给请求方;
                        // 2. 当前扫描不能满足这次请求,则会将其添加到等待队列中,待状态机拨回IdleState后,再触发扫描;
                        // 如果当前没有进行扫描,则调用tryToStartNewScan请求扫描;
                        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 {
    					...
                    }
                    return HANDLED;
                case WifiScanner.CMD_STOP_SINGLE_SCAN:
                    removeSingleScanRequest(ci, msg.arg2);
                    return HANDLED;
                default:
                    return NOT_HANDLED;
            }
        }
        ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

不难看出,上面逻辑最终有效的调用是tryToStartNewScan()

//frameworks/opt/net/wifi/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java
		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<>();
            //将mPendingScans中所有保存的请求做参数合并(取并集)
            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)) {
                mActiveScanSettings = settings;
                // 交换mActiveScans与mPendingScans变量的引用,并在交换后将mPendingScans清空
                RequestList<ScanSettings> tmp = mActiveScans;
                mActiveScans = mPendingScans;
                mPendingScans = tmp;
                mPendingScans.clear();
                // 状态拨至ScanningState
                transitionTo(mScanningState);
            } else {
				...
            }
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58

以上逻辑梳理如下:

  1. 无论是IdleState还是ScanningState,都没有在自己子状态内处理WifiScanner.CMD_START_SINGLE_SCAN,而是在父状态DriverStartedState中处理的;
  2. 如果处理时状态机置于IdleState状态,则将扫描请求封装后添加到mPendingScans中,并调用tryToStartNewScan()触发扫描;
  3. tryToStartNewScan()主要调用
  4. 如果处理时状态机置于ScanningState状态,需要视情况而定:
    • 如果当前扫描请求mActiveScans的扫描参数满足这次请求,则不再另行请求;
    • 如果不能满足,则将此次扫描请求的参数添加到mPendingScans中;
  5. 当扫描结束后,ScanningState会清空mActiveScans,并将状态拨回IdleState
  6. IdleStateenter()方法处,会再次调用tryToStartNewScan,来将mPendingScans中的请求通知到WifiScannerImpl

最后,我们终于来到了WifiScannerImpl.startSingleScan()这个抽象方法的实现:
WifiScannerImpl有两个子类:

  • HalWifiScannerImpl
  • WificondScannerImpl
    借由一个工厂设计模式,由``的结果来决定构造哪个实现类:
//frameworks/opt/net/wifi/service/java/com/android/server/wifi/scanner/WifiScannerImpl.java
    /**
     * Factory that create the implementation that is most appropriate for the system.
     * This factory should only ever be used once.
     */
    public static final WifiScannerImplFactory DEFAULT_FACTORY = new WifiScannerImplFactory() {
            public WifiScannerImpl create(Context context, Looper looper, Clock clock) {
                WifiNative wifiNative = WifiInjector.getInstance().getWifiNative();
                WifiMonitor wifiMonitor = WifiInjector.getInstance().getWifiMonitor();
                String ifaceName = wifiNative.getClientInterfaceName();
                if (TextUtils.isEmpty(ifaceName)) {
                    return null;
                }
                if (wifiNative.getBgScanCapabilities(
                        ifaceName, new WifiNative.ScanCapabilities())) {
                    return new HalWifiScannerImpl(context, ifaceName, wifiNative, wifiMonitor,
                            looper, clock);
                } else {
                    return new WificondScannerImpl(context, ifaceName, wifiNative, wifiMonitor,
                            new WificondChannelHelper(wifiNative), looper, clock);
                }
            }
        };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

由于目前手边设备均是走的else逻辑分支,即构造WificondScannerImpl作为其实现,因此后面均以WificondScannerImpl的逻辑为主;

WificondScannerImpl
//frameworks/opt/net/wifi/service/java/com/android/server/wifi/scanner/WificondScannerImpl.java
 	@Override
    public boolean startSingleScan(WifiNative.ScanSettings settings,
            WifiNative.ScanEventHandler eventHandler) {

        synchronized (mSettingsLock) {
        	//不允许多个扫描请求同时运行
            if (mLastScanSettings != null) {
                Log.w(TAG, "A single scan is already running");
                return false;
            }

			//处理参数,合并所有需要扫描的频段(根据之前逻辑,single scan应该只有一个bucket,且为全频段)
            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);
            }

			//如果有需要扫描的隐藏网络,在此处处理添加
            Set<String> hiddenNetworkSSIDSet = new HashSet<>();
            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,会在扫描失败,或扫描成功,且扫描结果成功取回后置为null
            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);
				...
            } else {
				...
            }
            if (success) {
				...
				//设置定时器,处理扫描耗时过长的情况
                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;
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74

这个方法内也是进行了参数处理、合并,以及异常处理,而正常逻辑主要在WifiNative.scan()

WifiNative
//frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiNative.java
    public boolean scan(
            @NonNull String ifaceName, int scanType, Set<Integer> freqs,
            Set<String> hiddenNetworkSSIDs) {
        return mWificondControl.scan(ifaceName, scanType, freqs, hiddenNetworkSSIDs);
    }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
WificondControl
//frameworks/opt/net/wifi/service/java/com/android/server/wifi/WificondControl.java
    public boolean scan(@NonNull String ifaceName,
                        int scanType,
                        Set<Integer> freqs,
                        Set<String> hiddenNetworkSSIDs) {
        IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName);
		...
        try {
            return scannerImpl.scan(settings);
        } catch (RemoteException e1) {
            Log.e(TAG, "Failed to request scan due to remote exception");
        }
        return false;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

关于getScannerImpl()

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

这里简单介绍下mWificondScanners这个数据结构怎么来的:

在打开WLAN时,会调用到WificondControl.setupInterfaceForClientMode()

    /**
    * 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) {
		...
        IClientInterface clientInterface = null;
        try {
            clientInterface = mWificond.createClientInterface(ifaceName);
        } catch (RemoteException e1) {
            Log.e(TAG, "Failed to get IClientInterface due to remote exception");
            return null;
        }
		...
        // Refresh Handlers
        mClientInterfaces.put(ifaceName, clientInterface);
        try {
            IWifiScannerImpl wificondScanner = clientInterface.getWifiScannerImpl();
			...
            mWificondScanners.put(ifaceName, wificondScanner);
			...
        } catch (RemoteException e) {
            Log.e(TAG, "Failed to refresh wificond scanner due to remote exception");
        }

        return clientInterface;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

这里涉及到wificond了,也就是说上面的IWifiScannerImpl.scan()已经是由wificond在负责完成了;
篇幅有限,这里我们就不继续深入了;

总结

以上是Android Framework层对于应用请求WLAN扫描的处理逻辑,主要是参数封装与合并,线程切换很多,并且也涉及到多个类的跳转;
整体来看,对于第一次了解的开发人员来看,是比较绕的;
但是梳理下来发现,大部分逻辑,都是针对一些特殊情况的处理(频繁重复请求)与优化(相同参数请求合并);
而对于有效的扫描请求,实际上逻辑分叉并不会多;

最后,老规矩,附上一张流程图:

WIFI发起扫描流程图

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/225577
推荐阅读
相关标签
  

闽ICP备14008679号