1. 程式人生 > >Android O WiFi的掃描流程

Android O WiFi的掃描流程

1.Android O的scan跟之前Android版本存在的差異

1) Android O開始scan的命令不是通過wpa_supplicant下發到kernel,而是直接由wificond傳送到kernel,而scan results的結果也是直接由kernel傳給wificond,再由wificond傳送給上層去顯示
2) 在framework層,現在scan改由scanner這個類去使用,之前從6.0開始慢慢用起來,現在到了8.0,可以說已經用了近95%了,剩下了目前google還在修改中,google的目標應該是想把scan整個完全獨立出來,不跟其他的扯太大,例如WifiStateMachine
3) 目前上層下發的scan,已經改由SettingsLib去定時下,不交給apk去做了。
在android 8.0之前的版本,scan的定時掃描都是在上層app做的,但從8.0開始,google把這個定時器改到了framework中的SettingsLib(frameworks/base/packages/SettingsLib)中去了
4) 之前的版本scan操作,當同一時間下兩次scan時,是直接交由wpa_supplicant去隔開的,現在由於直接把scan下發給kernel。所以,google已經把上層下的scan跟framework的自動scan也實現了分離,不讓這兩種flow打到,讓同一時間只下一次scan,後一次則做pending動作

2. 流程分析

這裡寫圖片描述

2.1 Settings

WiFi Settings在Android 8.0程式碼同樣分為兩部分,packages/apps/Settings和framework/base/packages/SettingsLib。

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

    @Override
    public void onStart() {
        super.onStart();

        // On/off switch is hidden for Setup Wizard (returns null
) mWifiEnabler = createWifiEnabler(); mWifiTracker.startTracking(); if (mIsRestricted) { restrictUi(); return; } onWifiStateChanged(mWifiManager.getWifiState()); }

這裡會呼叫到SettingsLib的WifiTracker,WifiTracker中包含了wifi掃描的操作。比較奇怪的是,這裡沒有走監聽wifi開啟訊息,待WiFi開啟後開始掃描,而是隻要在WiFi介面就開始掃描了。不用奇怪,因為後面會去做檢查只有wifi開啟之後,才會真正下發scan命令到底層,具體請看下面的分析。

2.2 SettingsLib

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

    /**
     * Start tracking wifi networks and scores.
     *
     * <p>Registers listeners and starts scanning for wifi networks. If this is not called
     * then forceUpdate() must be called to populate getAccessPoints().
     */
    @MainThread
    public void startTracking() {
        synchronized (mLock) {
            registerScoreCache();

            mNetworkScoringUiEnabled =
                    Settings.Global.getInt(
                            mContext.getContentResolver(),
                            Settings.Global.NETWORK_SCORING_UI_ENABLED, 0) == 1;

            mMaxSpeedLabelScoreCacheAge =
                    Settings.Global.getLong(
                            mContext.getContentResolver(),
                            Settings.Global.SPEED_LABEL_CACHE_EVICTION_AGE_MILLIS,
                            DEFAULT_MAX_CACHED_SCORE_AGE_MILLIS);

            resumeScanning();
            if (!mRegistered) {
                mContext.registerReceiver(mReceiver, mFilter);
                // NetworkCallback objects cannot be reused. http://b/20701525 .
                mNetworkCallback = new WifiTrackerNetworkCallback();
                mConnectivityManager.registerNetworkCallback(mNetworkRequest, mNetworkCallback);
                mRegistered = true;
            }
        }
    }

startTracking() –> resumeScanning();

    /**
     * Resume scanning for wifi networks after it has been paused.
     *
     * <p>The score cache should be registered before this method is invoked.
     */
    public void resumeScanning() {
        if (mScanner == null) {
            mScanner = new Scanner();
        }

        mWorkHandler.sendEmptyMessage(WorkHandler.MSG_RESUME);
        if (mWifiManager.isWifiEnabled()) {
            mScanner.resume();
        }
    }

這裡可以看到resumeScanning()會去判斷wifi有沒有開啟mWifiManager.isWifiEnabled(),如果沒有開啟就不繼續進行掃描流程了。只有打開了wifi才會呼叫mScanner.resume()繼續走下去
frameworks/base/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java

    @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 forceScan() {
            removeMessages(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);
        }
    }

class Scanner是WifiTracker中的一個內部類,整個類就這麼一點程式碼,那麼來分析一下這個Scanner類,前面呼叫的這個mScanner.resume()函式中就傳送了一條訊息sendEmptyMessage(MSG_SCAN);然後在類中的handleMessage中對這個訊息進行處理,這個類的handleMessage中也只處理這一個訊息,從handleMessage函式中可以看到if (message.what != MSG_SCAN) return;如果不是MSG_SCAN就退出函數了,如果是才會接著去進行掃描。
這裡可以看到如果掃描失敗3次就會導致掃描失敗的toast的提醒,如果掃描成功則會進行掃描間隔為10s的持續掃描。

   // TODO: Allow control of this?
    // Combo scans can take 5-6s to complete - set to 10s.
    private static final int WIFI_RESCAN_INTERVAL_MS = 10 * 1000;

從mWifiManager.startScan()可以知道,這裡掃描流程就走到WifiManager裡去了

2.3 WiFi framework

2.3.1 WifiManager

/framework/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.
     * @return {@code true} if the operation succeeded, i.e., the scan was initiated
     */
    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();
            mService.startScan(null, workSource, packageName);
            return true;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

從程式碼角度來看startScan要麼返回true要麼拋異常呀,false不可能。。。

之前分析過mService對應的服務端是WifiServiceImpl

2.3.2 WifiServiceImpl

framework/opt/net/wifi/service/java/com/android/server/wifi/WifiServiceImpl.java

 /**
     * see {@link android.net.wifi.WifiManager#startScan}
     * and {@link android.net.wifi.WifiManager#startCustomizedScan}
     *
     * @param settings If null, use default parameter, i.e. full scan.
     * @param workSource If null, all blame is given to the calling uid.
     * @param packageName Package name of the app that requests wifi scan.
     */
    @Override
    public void startScan(ScanSettings settings, WorkSource workSource, String packageName) {
        enforceChangePermission();

        mLog.info("startScan uid=%").c(Binder.getCallingUid()).flush();
        // Check and throttle background apps for wifi scan.
        if (isRequestFromBackground(packageName)) {
            long lastScanMs = mLastScanTimestamps.getOrDefault(packageName, 0L);
            long elapsedRealtime = mClock.getElapsedSinceBootMillis();

            if (lastScanMs != 0 && (elapsedRealtime - lastScanMs) < mBackgroundThrottleInterval) {
                sendFailedScanBroadcast();
                return;
            }
            // Proceed with the scan request and record the time.
            mLastScanTimestamps.put(packageName, elapsedRealtime);
        }
        synchronized (this) {
            if (mWifiScanner == null) {
                mWifiScanner = mWifiInjector.getWifiScanner();
            }
            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;
            }
        }
        if (settings != null) {
            settings = new ScanSettings(settings);
            if (!settings.isValid()) {
                Slog.e(TAG, "invalid scan setting");
                return;
            }
        }
        if (workSource != null) {
            enforceWorkSourcePermission();
            // WifiManager currently doesn't use names, so need to clear names out of the
            // supplied WorkSource to allow future WorkSource combining.
            workSource.clearNames();
        }
        if (workSource == null && Binder.getCallingUid() >= 0) {
            workSource = new WorkSource(Binder.getCallingUid());
        }
        mWifiStateMachine.startScan(Binder.getCallingUid(), scanRequestCounter++,
                settings, workSource);
    }

注意這個方法前兩個引數傳進來的都是null,這邊一開始會對wifi掃描的請求物件做個過濾,畢竟WiFi掃描是耗電的,不能誰都來請求一下。不在白名單的後臺應用的請求掃描時間間隔如果短於30min,則通報批評。下面是startScan函式中對此過濾的操作和分析

        // Check and throttle background apps for wifi scan.
        if (isRequestFromBackground(packageName)) {
            long lastScanMs = mLastScanTimestamps.getOrDefault(packageName, 0L);
            long elapsedRealtime = mClock.getElapsedSinceBootMillis();

            if (lastScanMs != 0 && (elapsedRealtime - lastScanMs) < mBackgroundThrottleInterval) {
                sendFailedScanBroadcast();
                return;
            }
            // Proceed with the scan request and record the time.
            mLastScanTimestamps.put(packageName, elapsedRealtime);
        }

isRequestFromBackground函式原始碼如下:

    // Check if the request comes from background.
    private boolean isRequestFromBackground(String packageName) {
        // Requests from system or wifi are not background.
        if (Binder.getCallingUid() == Process.SYSTEM_UID
                || Binder.getCallingUid() == Process.WIFI_UID) {
            return false;
        }
        mAppOps.checkPackage(Binder.getCallingUid(), packageName);
        if (mBackgroundThrottlePackageWhitelist.contains(packageName)) {
            return false;
        }

        // getPackageImportance requires PACKAGE_USAGE_STATS permission, so clearing the incoming
        // identify so the permission check can be done on system process where wifi runs in.
        long callingIdentity = Binder.clearCallingIdentity();
        try {
            return mActivityManager.getPackageImportance(packageName)
                    > BACKGROUND_IMPORTANCE_CUTOFF;
        } finally {
            Binder.restoreCallingIdentity(callingIdentity);
        }
    }

掃描間隔DEFAULT_SCAN_BACKGROUND_THROTTLE_INTERVAL_MS定義如下

    private static final long DEFAULT_SCAN_BACKGROUND_THROTTLE_INTERVAL_MS = 30 * 60 * 1000;

sendFailedScanBroadcast函式原始碼如下

    // Send a failed scan broadcast to indicate the current scan request failed.
    private void sendFailedScanBroadcast() {
        // clear calling identity to send broadcast
        long callingIdentity = Binder.clearCallingIdentity();
        try {
            Intent intent = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
            intent.putExtra(WifiManager.EXTRA_RESULTS_UPDATED, false);
            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
        } finally {
            // restore calling identity
            Binder.restoreCallingIdentity(callingIdentity);
        }

    }

回到剛才的startScan函式。由於傳入的前兩個引數是null,則直接走到WifiStateMachine裡去了。

        mWifiStateMachine.startScan(Binder.getCallingUid(), scanRequestCounter++,
                settings, workSource);

2.3.3 WifiStateMachine

framework/opt/net/wifi/service/java/com/android/server/wifi/WifiStateMachine.java

    /**
     * Initiate a wifi scan. If workSource is not null, blame is given to it, otherwise blame is
     * given to callingUid.
     *
     * @param callingUid The uid initiating the wifi scan. Blame will be given here unless
     *                   workSource is specified.
     * @param workSource If not null, blame is given to workSource.
     * @param settings   Scan settings, see {@link ScanSettings}.
     */
    public void startScan(int callingUid, int scanCounter,
                          ScanSettings settings, WorkSource workSource) {
        Bundle bundle = new Bundle();
        bundle.putParcelable(CUSTOMIZED_SCAN_SETTING, settings);
        bundle.putParcelable(CUSTOMIZED_SCAN_WORKSOURCE, workSource);
        bundle.putLong(SCAN_REQUEST_TIME, mClock.getWallClockMillis());
        sendMessage(CMD_START_SCAN, callingUid, scanCounter, bundle);
    }

sendMessage(CMD_START_SCAN, callingUid, scanCounter, bundle);傳送一條訊息CMD_START_SCAN;
由於前面已經enable supplicant,所以到目前掃描階段,WifiStateMachine狀態機目前的狀態應該是SupplicantStartedState,下面來看看WifiStateMachine在SupplicantStartedState 怎麼處理這個訊息

    class SupplicantStartedState extends State {
         ...
            @Override
        public boolean processMessage(Message message) {
            logStateAndMessage(message, this);

            switch(message.what) {
                ...
                case CMD_START_SCAN:
                    // TODO: remove scan request path (b/31445200)
                    handleScanRequest(message);
                    break;

呼叫handleScanRequest(message);進行處理

    private void handleScanRequest(Message message) {
        ScanSettings settings = null;
        WorkSource workSource = null;

        // unbundle parameters
        Bundle bundle = (Bundle) message.obj;

        if (bundle != null) {
            settings = bundle.getParcelable(CUSTOMIZED_SCAN_SETTING);
            workSource = bundle.getParcelable(CUSTOMIZED_SCAN_WORKSOURCE);
        }

        Set<Integer> freqs = null;
        if (settings != null && settings.channelSet != null) {
            freqs = new HashSet<>();
            for (WifiChannel channel : settings.channelSet) {
                freqs.add(channel.freqMHz);
            }
        }

        // Retrieve the list of hidden network SSIDs to scan for.
        List<WifiScanner.ScanSettings.HiddenNetwork> hiddenNetworks =
                mWifiConfigManager.retrieveHiddenNetworkList();

        // call wifi native to start the scan
        if (startScanNative(freqs, hiddenNetworks, workSource)) {
            // a full scan covers everything, clearing scan request buffer
            if (freqs == null)
                mBufferedScanMsg.clear();
            messageHandlingStatus = MESSAGE_HANDLING_STATUS_OK;
            return;
        }

        // if reach here, scan request is rejected

        if (!mIsScanOngoing) {
            // if rejection is NOT due to ongoing scan (e.g. bad scan parameters),

            // discard this request and pop up the next one
            if (mBufferedScanMsg.size() > 0) {
                sendMessage(mBufferedScanMsg.remove());
            }
            messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
        } else if (!mIsFullScanOngoing) {
            // if rejection is due to an ongoing scan, and the ongoing one is NOT a full scan,
            // buffer the scan request to make sure specified channels will be scanned eventually
            if (freqs == null)
                mBufferedScanMsg.clear();
            if (mBufferedScanMsg.size() < SCAN_REQUEST_BUFFER_MAX_SIZE) {
                Message msg = obtainMessage(CMD_START_SCAN,
                        message.arg1, message.arg2, bundle);
                mBufferedScanMsg.add(msg);
            } else {
                // if too many requests in buffer, combine them into a single full scan
                bundle = new Bundle();
                bundle.putParcelable(CUSTOMIZED_SCAN_SETTING, null);
                bundle.putParcelable(CUSTOMIZED_SCAN_WORKSOURCE, workSource);
                Message msg = obtainMessage(CMD_START_SCAN, message.arg1, message.arg2, bundle);
                mBufferedScanMsg.clear();
                mBufferedScanMsg.add(msg);
            }
            messageHandlingStatus = MESSAGE_HANDLING_STATUS_LOOPED;
        } else {
            // mIsScanOngoing and mIsFullScanOngoing
            messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
        }
    }

if (startScanNative(freqs, hiddenNetworks, workSource))
繼續呼叫startScanNative

   // TODO this is a temporary measure to bridge between WifiScanner and WifiStateMachine until
    // scan functionality is refactored out of WifiStateMachine.
    /**
     * return true iff scan request is accepted
     */
    private boolean startScanNative(final Set<Integer> freqs,
            List<WifiScanner.ScanSettings.HiddenNetwork> hiddenNetworkList,
            WorkSource workSource) {
        WifiScanner.ScanSettings settings = new WifiScanner.ScanSettings();
        if (freqs == null) {
            settings.band = WifiScanner.WIFI_BAND_BOTH_WITH_DFS;
        } else {
            settings.band = WifiScanner.WIFI_BAND_UNSPECIFIED;
            int index = 0;
            settings.channels = new WifiScanner.ChannelSpec[freqs.size()];
            for (Integer freq : freqs) {
                settings.channels[index++] = new WifiScanner.ChannelSpec(freq);
            }
        }
        settings.reportEvents = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN
                | WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT;

        settings.hiddenNetworks =
                hiddenNetworkList.toArray(
                        new WifiScanner.ScanSettings.HiddenNetwork[hiddenNetworkList.size()]);

        WifiScanner.ScanListener nativeScanListener = new WifiScanner.ScanListener() {
                // ignore all events since WifiStateMachine is registered for the supplicant events
                @Override
                public void onSuccess() {
                }
                @Override
                public void onFailure(int reason, String description) {
                    mIsScanOngoing = false;
                    mIsFullScanOngoing = false;
                }
                @Override
                public void onResults(WifiScanner.ScanData[] results) {
                }
                @Override
                public void onFullResult(ScanResult fullScanResult) {
                }
                @Override
                public void onPeriodChanged(int periodInMs) {
                }
            };
        mWifiScanner.startScan(settings, nativeScanListener, workSource);
        mIsScanOngoing = true;
        mIsFullScanOngoing = (freqs == null);
        lastScanFreqs = freqs;
        return true;
    }

由於freqs和workSource這兩個引數為null,走settings.band = WifiScanner.WIFI_BAND_BOTH_WITH_DFS;所以settings.band是7,即頻段為7

    /** Both 2.4 GHz band and 5 GHz band; with DFS channels */
    public static final int WIFI_BAND_BOTH_WITH_DFS = 7;    /* both bands with DFS channels */

這邊繼續呼叫WifiScanner的startScan,settings不是null了,已經在startScanNative函式中被預設初始化過了。另外
WifiScanner.ScanListener nativeScanListener = new WifiScanner.ScanListener()
這邊初始化了一個nativeScanListener委託給WifiScanner,從方法來看是有結果了會回撥這個Listner,使用了委託模式。

2.3.4 WifiScanner

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

這裡用了AsyncChannel,和aidl一樣,也是IPC的一種方式。其實WifiScanner對應的服務端就是WifiScanningServiceImpl

下面來分析一下為什麼WifiScanner對應的服務端就是WifiScanningServiceImpl,看一WifiScanner的建構函式

    /**
     * Create a new WifiScanner instance.
     * Applications will almost always want to use
     * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
     * the standard {@link android.content.Context#WIFI_SERVICE Context.WIFI_SERVICE}.
     * @param context the application context
     * @param service the Binder interface
     * @param looper the Looper used to deliver callbacks
     * @hide
     */
    public WifiScanner(Context context, IWifiScanner service, Looper looper) {
        mContext = context;
        mService = service;

        Messenger messenger = null;
        try {
            messenger = mService.getMessenger();
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }

        if (messenger == null) {
            throw new IllegalStateException("getMessenger() returned null!  This is invalid.");
        }

        mAsyncChannel = new AsyncChannel();

        mInternalHandler = new ServiceHandler(looper);
        mAsyncChannel.connectSync(mContext, mInternalHandler, messenger);
        // We cannot use fullyConnectSync because it sends the FULL_CONNECTION message
        // synchronously, which causes WifiScanningService to receive the wrong replyTo value.
        mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
    }

從WifiScanner的構造方法裡可以看到InternalHandler/AsyncChannel/Messenger是完成IPC的橋樑。

另外WifiScanner的初始化是經由WifiStateMachine在WifiInjector裡完成的。

            // We can't do this in the constructor because WifiStateMachine is created before the
            // wifi scanning service is initialized
            if (mWifiScanner == null) {
                mWifiScanner = mWifiInjector.getWifiScanner();

framework/opt/net/wifi/service/java/com/android/server/wifi/WifiInjector.java

    /**
     * Obtain an instance of WifiScanner.
     * If it was not already created, then obtain an instance.  Note, this must be done lazily since
     * WifiScannerService is separate and created later.
     */
    public synchronized WifiScanner getWifiScanner() {
        if (mWifiScanner == null) {
            mWifiScanner = new WifiScanner(mContext,
                    IWifiScanner.Stub.asInterface(ServiceManager.getService(
                            Context.WIFI_SCANNING_SERVICE)),
                    mWifiStateMachineHandlerThread.getLooper());
        }
        return mWifiScanner;
    }

先看下在SystemServer會啟動WifiScanningService服務

                traceBeginAndSlog("StartWifiScanning");
                mSystemServiceManager.startService(
                        "com.android.server.wifi.scanner.WifiScanningService");
                traceEnd();

再看下WifiScanningService這個服務

public class WifiScanningService extends SystemService {

    static final String TAG = "WifiScanningService";
    private final WifiScanningServiceImpl mImpl;
    private final HandlerThread mHandlerThread;

    public WifiScanningService(Context context) {
        super(context);
        Log.i(TAG, "Creating " + Context.WIFI_SCANNING_SERVICE);
        mHandlerThread = new HandlerThread("WifiScanningService");
        mHandlerThread.start();
        mImpl = new WifiScanningServiceImpl(getContext(), mHandlerThread.getLooper(),
                WifiScannerImpl.DEFAULT_FACTORY, BatteryStatsService.getService(),
                WifiInjector.getInstance());
    }
    @Override
    public void onStart() {
        Log.i(TAG, "Publishing " + Context.WIFI_SCANNING_SERVICE);
        publishBinderService(Context.WIFI_SCANNING_SERVICE, mImpl);
    }

    /**
     * Publish the service so it is accessible to other services and apps.
     */
    protected final void publishBinderService(String name, IBinder service) {
        publishBinderService(name, service, false);
    }

    /**
     * Publish the service so it is accessible to other services and apps.
     */
    protected final void publishBinderService(String name, IBinder service,
            boolean allowIsolated) {
        ServiceManager.addService(name, service, allowIsolated);
    }

果不其然和WifiService一個套路,裡面會new WifiScanningServiceImpl,經由SystemService的publishBinderService將自己加入到ServiceManager管理的Service中去,那麼這裡WifiScanner對應的服務端就是WifiScanningServiceImpl了。

現在就從WifiScanner進入到WifiScanningServiceImpl了

2.3.5 WifiScanningServiceImpl

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

繼而再看下getMessenger方法:

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

這裡梳理出來WifiScanningServiceImpl的ClientHander是處理WifiScanner發出的CMD_START_SINGLE_SCAN訊息
framework/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) {
               ...
                case WifiScanner.CMD_START_SINGLE_SCAN:
                case WifiScanner.CMD_STOP_SINGLE_SCAN:
                    mSingleScanStateMachine.sendMessage(Message.obtain(msg));
                    break;
        mSingleScanStateMachine = new WifiSingleScanStateMachine(mLooper);

這裡又是狀態模式,其中有個DriverStartedState繼續處理,
framework/opt/net/wifi/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java

        /**
         * State representing when the driver is running. This state is not meant to be transitioned
         * directly, but is instead indented 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 void exit() {
                // clear scan results when scan mode is not active
                mCachedScanResults.clear();

                mWifiMetrics.incrementScanReturnEntry(
                        WifiMetricsProto.WifiLog.SCAN_FAILURE_INTERRUPTED,
                        mPendingScans.size());
                sendOpFailedToAllAndClear(mPendingScans, WifiScanner.REASON_UNSPECIFIED,
                        "Scan was interrupted");
            }

            @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;
                        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, workSource)) {
                            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;

這裡可以看到對上了,把我們千辛萬苦傳來的引數取了出來,雖然workSource就是個null,但是scanSettings的band是7

                    ScanSettings scanSettings =
                            scanParams.getParcelable(WifiScanner.SCAN_PARAMS_SCAN_SETTINGS_KEY);
                    WorkSource workSource =
                            scanParams.getParcelable(WifiScanner.SCAN_PARAMS_WORK_SOURCE_KEY);

之後返回一個操作成功的訊息replySucceeded(msg);但這並不是搜尋成功了,這裡還沒進行搜尋scan;返回訊息之後才會真正去進行搜尋,

    void replySucceeded(Message msg) {
        if (msg.replyTo != null) {
            Message reply = Message.obtain();
            reply.what = WifiScanner.CMD_OP_SUCCEEDED;
            reply.arg2 = msg.arg2;
            if (msg.obj != null) {
                reply.obj = msg.obj;
            }
            try {
                msg.replyTo.send(reply);
                mLog.trace("replySucceeded recvdMessage=%").c(msg.what).flush();
            } catch (RemoteException e) {
                // There's not much we can do if reply can't be sent!
            }
        } else {
            // locally generated message; doesn't need a reply!
        }
    }

這邊WifiScanner的ServiceHandler會收到並處理
frameworks/base/wifi/java/android/net/wifi/WifiScanner.java

    private class ServiceHandler extends Handler {
        ServiceHandler(Looper looper) {
            super(looper);
        }
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED:
                    return;
                case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
                    Log.e(TAG, "Channel connection lost");
                    // This will cause all further async API calls on the WifiManager
                    // to fail and throw an exception
                    mAsyncChannel = null;
                    getLooper().quit();
                    return;
            }

            Object listener = getListener(msg.arg2);

            if (listener == null) {
                if (DBG) Log.d(TAG, "invalid listener key = " + msg.arg2);
                return;
            } else {
                if (DBG) Log.d(TAG, "listener key = " + msg.arg2);
            }

            switch (msg.what) {
                    /* ActionListeners grouped together */
                case CMD_OP_SUCCEEDED :
                    ((ActionListener) listener).onSuccess();
                    break;

最後再呼叫到我們在WifiStateMachine裡初始化好的listener的success方法,但是這裡是空的
frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiStateMachine.java

        WifiScanner.ScanListener nativeScanListener = new WifiScanner.ScanListener() {
                // ignore all events since WifiStateMachine is registered for the supplicant events
                @Override
                public void onSuccess() {
                }
                @Override
                public void onFailure(int reason, String description) {
                    mIsScanOngoing = false;
                    mIsFullScanOngoing = false;
                }
                @Override
                public void onResults(WifiScanner.ScanData[] results) {
                }
                @Override
                public void onFullResult(ScanResult fullScanResult) {
                }
                @Override
                public void onPeriodChanged(int periodInMs) {
                }
            };

接著前面的DriverStartedState進行分析返回操作成功訊息返回一個操作成功的訊息replySucceeded(msg);之後的程式碼如下
framework/opt/net/wifi/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java

                            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;

這邊程式碼意思是如果正在搜尋那麼就不重新開始新的scan了,否則tryToStartNewScan()

然後看下tryToStartNewScan()
framework/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<>();
            for (RequestInfo<ScanSettings> entry : mPendingScans) {
                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;