Android TVSetting Wifi连接分析(三)

基于Android 9.0 ATV版 TVSetting源码,研究TVSetting Wifi连接方法

Android TVSetting Wifi连接分析(一)

Android TVSetting Wifi连接分析(二)

Android TVSetting Wifi连接分析(三)

一、概要

这篇主要是对WifiTracker分析

WifiTracker不单是提供TVSetting,而是作为一个公共类,由com.android.settingslib提供给TVSetting和Setting使用。WifiTracker通过Lifecycle和一个WifiListener实现与调用者的交互

1.1 WifiTracker观察LifecycleOwner的状态

WifiTracker WifiTracker实现了LifecycleObserver观察者接口,用来观察Fragment的状态。

主要观察了

onStart事件

onStop事件

1.2 WifiTracker实现了一个内部接口WifiListener,用于在监控到wifi信号发生改变后,通知UI更新

public interface WifiListener {
        /**
         * Called when the state of Wifi has changed, the state will be one of
         * the following.
         *
         * <li>{@link WifiManager#WIFI_STATE_DISABLED}</li>
         * <li>{@link WifiManager#WIFI_STATE_ENABLED}</li>
         * <li>{@link WifiManager#WIFI_STATE_DISABLING}</li>
         * <li>{@link WifiManager#WIFI_STATE_ENABLING}</li>
         * <li>{@link WifiManager#WIFI_STATE_UNKNOWN}</li>
         * <p>
         *
         * @param state The new state of wifi.
         */
        void onWifiStateChanged(int state);

        /**
         * Called when the connection state of wifi has changed and
         * {@link WifiTracker#isConnected()} should be called to get the updated state.
         */
        void onConnectedChanged();

        /**
         * Called to indicate the list of AccessPoints has been updated and
         * {@link WifiTracker#getAccessPoints()} should be called to get the updated list.
         */
        void onAccessPointsChanged();
    }

1.3 WifiTracker关键类及构造函数

WifiTracker几个关键类

➢mWifiManager-管理wifi连接

➢mConnectivityManager-管理网络连接状态

➢mNetworkScoreManager-打分管理,资料太少了,需要进一步研究

➢AccessPoint--wifi节点管理类

WifiTracker要求调用者实现WifiListener,LifecycleOwner,并作为构造参数传入

    public WifiTracker(Context context, WifiListener wifiListener,
            @NonNull Lifecycle lifecycle, boolean includeSaved, boolean includeScans) {
        //调用最终的构造函数  
      this(context, wifiListener,
                context.getSystemService(WifiManager.class),
                context.getSystemService(ConnectivityManager.class),
                context.getSystemService(NetworkScoreManager.class),
                newIntentFilter());
        
        //将WifiTracker加入到WifiTracker
        lifecycle.addObserver(this);
    }

 重载的构造函数,初始化主要的Manager

    @VisibleForTesting
    WifiTracker(Context context, WifiListener wifiListener,
            WifiManager wifiManager, ConnectivityManager connectivityManager,
            NetworkScoreManager networkScoreManager,
            IntentFilter filter) {
        mContext = context;
		//管理wifi
        mWifiManager = wifiManager;
		//wifi监听者 回调上层
        mListener = new WifiListenerExecutor(wifiListener);
		//监控网络状态
        mConnectivityManager = connectivityManager;

        // check if verbose logging developer option has been turned on or off
        sVerboseLogging = (mWifiManager.getVerboseLoggingLevel() > 0);

		//广播监听
        mFilter = filter;
		
		//创建网络请求
        mNetworkRequest = new NetworkRequest.Builder()
                .clearCapabilities()
                .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
                .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
                .build();
		//打分机制
        mNetworkScoreManager = networkScoreManager;

        // TODO(sghuman): Remove this and create less hacky solution for testing
        final HandlerThread workThread = new HandlerThread(TAG
                + "{" + Integer.toHexString(System.identityHashCode(this)) + "}",
                Process.THREAD_PRIORITY_BACKGROUND);
		//开始工作线程
        workThread.start();
        setWorkThread(workThread);
    }

二、WifiTracker工作分析

2.1 启动

WifiTracker观察了onStart事件,当被观察者,TVSetting中是NetworkFragment触发onStart,WifiTracker也会执行onStart()事件,进入onStart,主要就是开启wifi刷新,来更新wifi连接列表

    public void onStart() {
        // fetch current ScanResults instead of waiting for broadcast of fresh results
        //更新wifi列表
        forceUpdate();

        //注册分数管理缓存
        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);
        //创建扫描服务,开始Scanning,发生Scanning消息
        resumeScanning();
        if (!mRegistered) {
            //注册wifi广播
            mContext.registerReceiver(mReceiver, mFilter, null /* permission */, mWorkHandler);
            // NetworkCallback objects cannot be reused. http://b/20701525 .
            mNetworkCallback = new WifiTrackerNetworkCallback();
            //mConnectivityManager注册广播
            mConnectivityManager.registerNetworkCallback(
                    mNetworkRequest, mNetworkCallback, mWorkHandler);
            mRegistered = true;
        }
    }

2.1.1 更新wifi列表 forceUpdate

--forceUpdate调用
 -----fetchScansAndConfigsAndUpdateAccessPoints

    /**
     * Retrieves latest scan results and wifi configs, then calls
     * {@link #updateAccessPoints(List, List)}.
     */
    private void fetchScansAndConfigsAndUpdateAccessPoints() {
        //获得wifi扫描结果
        final List<ScanResult> newScanResults = mWifiManager.getScanResults();
        if (isVerboseLoggingEnabled()) {
            Log.i(TAG, "Fetched scan results: " + newScanResults);
        }

        List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
        //更新AccessPoints
        updateAccessPoints(newScanResults, configs);
    }

 ---------------updateAccessPoints 更具打分机制最终生成mInternalAccessPoints,并通知UI显示

    /** Update the internal list of access points. */
    private void updateAccessPoints(final List<ScanResult> newScanResults,
            List<WifiConfiguration> configs) {

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

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

        // Rather than dropping and reacquiring the lock multiple times in this method, we lock
        // once for efficiency of lock acquisition time and readability
        synchronized (mLock) {
            // Swap the current access points into a cached list for maintaining AP listeners
            List<AccessPoint> cachedAccessPoints;
            cachedAccessPoints = new ArrayList<>(mInternalAccessPoints);

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

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

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

                AccessPoint accessPoint =
                        getCachedOrCreate(entry.getValue(), cachedAccessPoints);
                if (mLastInfo != null && mLastNetworkInfo != null) {
                    accessPoint.update(connectionConfig, mLastInfo, mLastNetworkInfo);
                }

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

                accessPoints.add(accessPoint);
            }

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

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

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

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

            mInternalAccessPoints.clear();
            mInternalAccessPoints.addAll(accessPoints);
        }
        //通知UI
        conditionallyNotifyListeners();
    }

2.1.2 扫描wifi resumeScanning

    public void resumeScanning() {
        if (mScanner == null) {
            //创建扫描Handle
            mScanner = new Scanner();
        }

        if (mWifiManager.isWifiEnabled()) {
            //发生scan消息
            mScanner.resume();
        }
    }

 mScanner类负责不断的的扫描wifi

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

3.总结

这一章主要分析了WifiTracker的启动,包含了扫描与更新,下一章分析WifiTracker的评分应用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值