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.
         *
         * 
  • {@link WifiManager#WIFI_STATE_DISABLED}
  •          *
  • {@link WifiManager#WIFI_STATE_ENABLED}
  •          *
  • {@link WifiManager#WIFI_STATE_DISABLING}
  •          *
  • {@link WifiManager#WIFI_STATE_ENABLING}
  •          *
  • {@link WifiManager#WIFI_STATE_UNKNOWN}
  •          *

             *          * @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 newScanResults = mWifiManager.getScanResults();
            if (isVerboseLoggingEnabled()) {
                Log.i(TAG, "Fetched scan results: " + newScanResults);
            }
    
            List configs = mWifiManager.getConfiguredNetworks();
            //更新AccessPoints
            updateAccessPoints(newScanResults, configs);
        }

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

        /** Update the internal list of access points. */
        private void updateAccessPoints(final List newScanResults,
                List configs) {
    
            // Map configs and scan results necessary to make AccessPoints
            final Map configsByKey = new ArrayMap(configs.size());
            if (configs != null) {
                for (WifiConfiguration config : configs) {
                    configsByKey.put(AccessPoint.getKey(config), config);
                }
            }
            ArrayMap> 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 cachedAccessPoints;
                cachedAccessPoints = new ArrayList<>(mInternalAccessPoints);
    
                ArrayList accessPoints = new ArrayList<>();
    
                final List scoresToRequest = new ArrayList<>();
    
                for (Map.Entry> 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的评分应用

    你可能感兴趣的:(AndroidTV,AOSP研究,android,java,apache)