当前位置: 首页 > news >正文

[Android]Android P(9) WIFI学习笔记 - 扫描 (1)

目录

    • 前言
    • 请求扫描
      • APP入口
        • WifiManager
      • system_server层
        • WifiServiceImpl
        • ScanRequestProxy
        • WifiScanner
        • WifiScanningServiceImpl
        • WificondScannerImpl
        • WifiNative
        • WificondControl
    • 总结

前言

  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. 扫描结果就位后,系统通过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. 请求开始扫描的逻辑在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. 完成上述判断后,调用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. 使用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;
                ...
            }
        }

略去了大量不相关的代码,结果发现实际上是通过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;
             }
         }
         ...
     }
    
  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;
            }
        }
        ...
    }
    

不难看出,上面逻辑最终有效的调用是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. 无论是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);
                }
            }
        };

由于目前手边设备均是走的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;
        }
    }

这个方法内也是进行了参数处理、合并,以及异常处理,而正常逻辑主要在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);
    }

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

关于getScannerImpl()

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

这里简单介绍下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;
    }

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

总结

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

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

WIFI发起扫描流程图

相关文章:

  • java众筹网计算机毕业设计MyBatis+系统+LW文档+源码+调试部署
  • .NET BackgroundWorker
  • 中英文说明书丨SYSY NeuN抗体参数及应用实例
  • 物联网开发笔记(6)- 使用Wokwi仿真树莓派Pico实现按键操作
  • 酷家乐基于 Crane EHPA 的弹性落地实践
  • PEG小分子重氮生物素-PEG3-炔烃 有哪些需要了解的知识?
  • 智能自修复防腐涂层研究进展综述
  • 银行互联网类业务基于分布式存储的架构设计与实施运维分享
  • 油气管道系统安全状态监测技术研究进展
  • Room (三) RecyclerView 呈现列表数据
  • 【我的Android进阶之旅】如何在Android中使用ARCore来增强人脸Augmented Faces?
  • 基于云原生存储的容器持久化存储方案
  • 毫米波点云雷达 论文阅读 | 3DRIMR, IPCCC 2021
  • 如何修改网页视频播放倍速?(最高16倍速)
  • Gitlab服务器切换来版本升级,执行漏洞修复
  • 【刷算法】从上往下打印二叉树
  • Docker容器管理
  • HashMap剖析之内部结构
  • IE报vuex requires a Promise polyfill in this browser问题解决
  • Java的Interrupt与线程中断
  • pdf文件如何在线转换为jpg图片
  • Perseus-BERT——业内性能极致优化的BERT训练方案
  • swift基础之_对象 实例方法 对象方法。
  • 测试如何在敏捷团队中工作?
  • 分享自己折腾多时的一套 vue 组件 --we-vue
  • 关于Android中设置闹钟的相对比较完善的解决方案
  • 基于HAProxy的高性能缓存服务器nuster
  • 开年巨制!千人千面回放技术让你“看到”Flutter用户侧问题
  • 排序算法学习笔记
  • 入口文件开始,分析Vue源码实现
  • 通过获取异步加载JS文件进度实现一个canvas环形loading图
  • 学习笔记TF060:图像语音结合,看图说话
  • ​LeetCode解法汇总2808. 使循环数组所有元素相等的最少秒数
  • ​软考-高级-信息系统项目管理师教程 第四版【第19章-配置与变更管理-思维导图】​
  • # .NET Framework中使用命名管道进行进程间通信
  • # 计算机视觉入门
  • #QT(串口助手-界面)
  • (1)常见O(n^2)排序算法解析
  • (1综述)从零开始的嵌入式图像图像处理(PI+QT+OpenCV)实战演练
  • (70min)字节暑假实习二面(已挂)
  • (LeetCode) T14. Longest Common Prefix
  • (WSI分类)WSI分类文献小综述 2024
  • (二)c52学习之旅-简单了解单片机
  • (附源码)ssm基于微信小程序的疫苗管理系统 毕业设计 092354
  • (规划)24届春招和25届暑假实习路线准备规划
  • (六)软件测试分工
  • (一)Thymeleaf用法——Thymeleaf简介
  • (转)Linq学习笔记
  • (转)PlayerPrefs在Windows下存到哪里去了?
  • **CI中自动类加载的用法总结
  • .bat批处理(六):替换字符串中匹配的子串
  • .dat文件写入byte类型数组_用Python从Abaqus导出txt、dat数据
  • .dwp和.webpart的区别
  • .NET 6 在已知拓扑路径的情况下使用 Dijkstra,A*算法搜索最短路径
  • .Net Core缓存组件(MemoryCache)源码解析