Android4.4(MT8685)源码WIFI--初始化2
上一章我们讲到在SystemServer中会初始化一个WifiService对象,那么我们来看看这个WifiService的构造方法,路径:./frameworks/base/services/java/com/android/server/wifi/WifiService.java
<span style="font-size:14px;"><span style="font-size:12px;"><span style="font-size:10px;"> public WifiService(Context context) { mContext = context; mInterfaceName = SystemProperties.get("wifi.interface", "wlan0"); mWifiStateMachine = new WifiStateMachine(mContext, mInterfaceName); mWifiStateMachine.enableRssiPolling(true); mBatteryStats = BatteryStatsService.getService(); mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE); mNotificationController = new WifiNotificationController(mContext, mWifiStateMachine); mTrafficPoller = new WifiTrafficPoller(mContext, mInterfaceName); mSettingsStore = new WifiSettingsStore(mContext); HandlerThread wifiThread = new HandlerThread("WifiService"); wifiThread.start(); mClientHandler = new ClientHandler(wifiThread.getLooper()); mWifiStateMachineHandler = new WifiStateMachineHandler(wifiThread.getLooper()); mWifiController = new WifiController(mContext, this, wifiThread.getLooper()); mWifiController.start(); mBatchedScanSupported = mContext.getResources().getBoolean( R.bool.config_wifi_batched_scan_supported); registerForScanModeChange(); mContext.registerReceiver( new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { ///M: modify for timing issue to access Settings.Global.AIRPLANE_MODE_ON boolean isAirplaneModeOn = intent.getBooleanExtra("state", false); SXlog.i(TAG, "ACTION_AIRPLANE_MODE_CHANGED isAirplaneModeOn="+isAirplaneModeOn); if (mSettingsStore.handleAirplaneModeToggled(isAirplaneModeOn)) { mWifiController.sendMessage(CMD_AIRPLANE_TOGGLED); } } }, new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED)); // Adding optimizations of only receiving broadcasts when wifi is enabled // can result in race conditions when apps toggle wifi in the background // without active user involvement. Always receive broadcasts. registerForBroadcasts(); ///M: initializeExtra(); }</span></span></span>
这里主要创建一个WifiStateMachine,然后新建一个HandlerThread来处理消息,还创建了一个WifiController。接下来我们先分析WifiStateMachine。
WifiStateMachine用于控制整个Wifi的开启、关闭、连接、断开的状态切换,它继承了StateMachine,来看看构造方法的关键代码,路径为:./frameworks/base/wifi/java/android/net/wifi/WifiStateMachine.java
<span style="font-size:14px;"><span style="font-size:12px;"><span style="font-size:10px;">public WifiStateMachine(Context context, String wlanInterface) { super("WifiStateMachine"); addState(mDefaultState); addState(mInitialState, mDefaultState); addState(mSupplicantStartingState, mDefaultState); addState(mSupplicantStartedState, mDefaultState); addState(mDriverStartingState, mSupplicantStartedState); addState(mDriverStartedState, mSupplicantStartedState); addState(mScanModeState, mDriverStartedState); addState(mConnectModeState, mDriverStartedState); addState(mL2ConnectedState, mConnectModeState); addState(mObtainingIpState, mL2ConnectedState); addState(mVerifyingLinkState, mL2ConnectedState); addState(mCaptivePortalCheckState, mL2ConnectedState); addState(mConnectedState, mL2ConnectedState); addState(mDisconnectingState, mConnectModeState); addState(mDisconnectedState, mConnectModeState); addState(mWpsRunningState, mConnectModeState); addState(mWaitForP2pDisableState, mSupplicantStartedState); addState(mDriverStoppingState, mSupplicantStartedState); addState(mDriverStoppedState, mSupplicantStartedState); addState(mSupplicantStoppingState, mDefaultState); addState(mSoftApStartingState, mDefaultState); addState(mSoftApStartedState, mDefaultState); addState(mTetheringState, mSoftApStartedState); addState(mTetheredState, mSoftApStartedState); addState(mUntetheringState, mSoftApStartedState); setInitialState(mInitialState); //start the state machine start(); }</span></span></span>
这里添加了各种状态,并且调用了父类的start方法,说明此时,WifiStateMachine的状态为其初始状态,也就是mInitialState状态。
回到WifiService的构造方法中,还有一个WifiController对象被创建,WifiController类也是StateMachine的子类,看看其构造方法,路径:./frameworks/base/services/java/com/android/server/wifi/WifiController.java
<span style="font-size:14px;"><span style="font-size:12px;"><span style="font-size:10px;">WifiController(Context context, WifiService service, Looper looper) { super(TAG, looper); mContext = context; mWifiStateMachine = service.mWifiStateMachine; mSettingsStore = service.mSettingsStore; mLocks = service.mLocks; mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE); Intent idleIntent = new Intent(ACTION_DEVICE_IDLE, null); mIdleIntent = PendingIntent.getBroadcast(mContext, IDLE_REQUEST, idleIntent, 0); ///M: add plugin mWifiFwkExt = MediatekClassFactory.createInstance(IWifiFwkExt.class, mContext); addState(mDefaultState); addState(mApStaDisabledState, mDefaultState); addState(mStaEnabledState, mDefaultState); addState(mDeviceActiveState, mStaEnabledState); addState(mDeviceInactiveState, mStaEnabledState); addState(mScanOnlyLockHeldState, mDeviceInactiveState); addState(mFullLockHeldState, mDeviceInactiveState); addState(mFullHighPerfLockHeldState, mDeviceInactiveState); addState(mNoLockHeldState, mDeviceInactiveState); addState(mStaDisabledWithScanState, mDefaultState); addState(mApEnabledState, mDefaultState); addState(mEcmState, mDefaultState); if (mSettingsStore.isScanAlwaysAvailable()) { setInitialState(mStaDisabledWithScanState); } else { setInitialState(mApStaDisabledState); } setLogRecSize(100); setLogOnlyTransitions(false); IntentFilter filter = new IntentFilter(); filter.addAction(ACTION_DEVICE_IDLE); filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); mContext.registerReceiver( new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (action.equals(ACTION_DEVICE_IDLE)) { sendMessage(CMD_DEVICE_IDLE); } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) { mNetworkInfo = (NetworkInfo) intent.getParcelableExtra( WifiManager.EXTRA_NETWORK_INFO); } } }, new IntentFilter(filter)); initializeAndRegisterForSettingsChange(looper); }</span></span></span>
这里把WifiService中的WifiStateMachine对象传递进来了,并且添加了各种状态,并且设置了初始状态。
在WifiService构造方法中创建了WifiController对象了就立即调用了它的start方法,所以此时WifiController所在状态为它的初始状态,那么它的初始状态为什么呢?我们可以看到当系统在Wifi关闭的状态下仍然可以扫描时,那么初始状态为mStaDisabledWithScanState,否则为mApStaDisabledState,目前我们的系统的初始状态应该属于后者,也就是mApStaDisabledState,那我们进入到其enter方法中
<span style="font-size:14px;"><span style="font-size:12px;"><span style="font-size:10px;">@Override public void enter() { if (DBG) log(getName() + "\n"); mWifiStateMachine.setSupplicantRunning(false); // Supplicant can't restart right away, so not the time we switched off mDisabledTimestamp = SystemClock.elapsedRealtime(); mDeferredEnableSerialNumber++; mHaveDeferredEnable = false; }</span></span></span>
其实这里没做什么特别的操作。此时WifiService的初始化分析完成。
在之前的SystemServer中,在WifiService对象创建之后,我们还调用了它的checkAndStartWifi方法,这个方法就是用来检测是否可以启动Wifi了
<span style="font-size:14px;"><span style="font-size:12px;"><span style="font-size:10px;"> /** * Check if Wi-Fi needs to be enabled and start * if needed * * This function is used only at boot time */ public void checkAndStartWifi() { ///M: mWifiStateMachine.autoConnectInit(); /* Check if wi-fi needs to be enabled */ boolean wifiEnabled = mSettingsStore.isWifiToggleEnabled(); ///M: @{ if (mWifiStateMachine.hasCustomizedAutoConnect() && mSettingsStore.isAirplaneModeOn()) { SXlog.i(TAG, "Don't enable wifi when airplane mode is on for customization."); } else { ///@} Slog.i(TAG, "WifiService starting up with Wi-Fi " + (wifiEnabled ? "enabled" : "disabled")); // If we are already disabled (could be due to airplane mode), avoid changing persist // state here if (wifiEnabled) setWifiEnabled(wifiEnabled); } mWifiWatchdogStateMachine = WifiWatchdogStateMachine. makeWifiWatchdogStateMachine(mContext); }</span></span></span>
这里获取到上一次系统的Wifi状态,根据状态来决定是否开启Wifi,我们看看setWifiEnabled这个方法
<span style="font-size:14px;"><span style="font-size:12px;"><span style="font-size:10px;"> /** * see {@link android.net.wifi.WifiManager#setWifiEnabled(boolean)} * @param enable {@code true} to enable, {@code false} to disable. * @return {@code true} if the enable/disable operation was * started or is already in the queue. */ public synchronized boolean setWifiEnabled(boolean enable) { Slog.d(TAG, "setWifiEnabled: " + enable + " pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()); enforceChangePermission(); ///M: @{ if (mWifiStateMachine.hasCustomizedAutoConnect() && enable && mSettingsStore.isAirplaneModeOn()) { SXlog.i(TAG, "Can't enable wifi when airplane mode is on for customization."); return false; } ///@} if (DBG) { Slog.e(TAG, "Invoking mWifiStateMachine.setWifiEnabled\n"); } if (FeatureOption.MTK_MOBILE_MANAGEMENT) { if (enable == true) { if (mMobileManagerService == null) { mMobileManagerService = IMobileManagerService.Stub.asInterface( ServiceManager.getService(Context.MOBILE_SERVICE)); } try { mMobileManagerService.checkPermissionAsync(SubPermissions.CHANGE_WIFI_STATE_ON, Binder.getCallingUid(), new wifiOnPermissionCheckCallback()); } catch (RemoteException e) { Slog.e(TAG, "checkPermissionAsync() failed", e); } return true; } } /* * Caller might not have WRITE_SECURE_SETTINGS, * only CHANGE_WIFI_STATE is enforced */ long ident = Binder.clearCallingIdentity(); try { if (! mSettingsStore.handleWifiToggled(enable)) { // Nothing to do if wifi cannot be toggled return true; } } finally { Binder.restoreCallingIdentity(ident); } ///M: put extra mWifiIpoOff Log.e("dd","mWifiIpoOff1="+String.valueOf(mWifiIpoOff)); mWifiController.obtainMessage(CMD_WIFI_TOGGLED, mWifiIpoOff ? 1 : 0 ).sendToTarget(); return true; }</span></span></span>
这里主要就是想WifiController发送一个CMD_WIFI_TOGGLED消息,我们看看接收消息的地方,找到ApStaDisabledState类的processMessage方法
<span style="font-size:14px;"><span style="font-size:12px;"><span style="font-size:10px;"> @Override public boolean processMessage(Message msg) { SXlog.d(TAG, getName() + msg.toString() + "\n"); switch (msg.what) { case CMD_WIFI_TOGGLED: case CMD_AIRPLANE_TOGGLED: ///M: add WifiIpoOff@{ boolean wifiIpoOff = (msg.arg1==1) ? true: false; boolean ipoStateChange= false; if(mWifiIpoOff!=wifiIpoOff) ipoStateChange=true; mWifiIpoOff = wifiIpoOff; if(wifiIpoOff ==true){ SXlog.d(TAG,"ipooff don't enable wifi\n"); break; } if (mSettingsStore.isWifiToggleEnabled()) { //@} if (doDeferEnable(msg)) { if (mHaveDeferredEnable) { // have 2 toggles now, inc serial number an ignore both mDeferredEnableSerialNumber++; } mHaveDeferredEnable = !mHaveDeferredEnable; break; } if (mDeviceIdle == false) { Log.e("dd","ApStaDisabledState to mDeviceActiveState"); transitionTo(mDeviceActiveState); } else { checkLocksAndTransitionWhenDeviceIdle(); } ///M: check scan always avaliable only when ipo change from ipo on to off }else if (ipoStateChange ==true && mSettingsStore.isScanAlwaysAvailable() && mSettingsStore.isAirplaneModeOn()==false ) { SXlog.d(TAG,"ipoStateChange = "+ipoStateChange + "isAirplaneModeOn= "+mSettingsStore.isAirplaneModeOn()); transitionTo(mStaDisabledWithScanState); } break; default: return NOT_HANDLED; } return HANDLED; }</span></span></span>
这里只是简单的跳转状态到mDeviceActiveState,mDeviceActiveState状态的父状态是mStaEnabledState,所以分别会进入它们的enter方法:
<span style="font-size:14px;"><span style="font-size:12px;"><span style="font-size:10px;"> class StaEnabledState extends State { @Override public void enter() { mWifiStateMachine.setSupplicantRunning(true); } class DeviceActiveState extends State { @Override public void enter() { mWifiStateMachine.setOperationalMode(WifiStateMachine.CONNECT_MODE); mWifiStateMachine.setDriverStart(true); mWifiStateMachine.setHighPerfModeEnabled(false); }</span></span></span>
这里都是向WifiStateMachine发送消息CMD_START_SUPPLICANT、SET_OPERATIONAL_MODE、CMD_START_DRIVER和SET_HTGH_PERF_MODE
先来看看处理CMD_START_SUPPLICANT消息的地方
<span style="font-size:14px;"><span style="font-size:12px;"><span style="font-size:10px;">@Override public boolean processMessage(Message message) { if (DBG) log(getName() + message.toString() + "\n"); switch (message.what) { case CMD_START_SUPPLICANT: setWifiState(WIFI_STATE_ENABLING); if (mWifiNative.loadDriver()) { try { mNwService.wifiFirmwareReload(mInterfaceName, "STA"); } catch (Exception e) { loge("Failed to reload STA firmware " + e); // continue } try { // A runtime crash can leave the interface up and // this affects connectivity when supplicant starts up. // Ensure interface is down before a supplicant start. mNwService.setInterfaceDown(mInterfaceName); // Set privacy extensions mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true); // IPv6 is enabled only as long as access point is connected since: // - IPv6 addresses and routes stick around after disconnection // - kernel is unaware when connected and fails to start IPv6 negotiation // - kernel can start autoconfiguration when 802.1x is not complete mNwService.disableIpv6(mInterfaceName); } catch (RemoteException re) { loge("Unable to change interface settings: " + re); } catch (IllegalStateException ie) { loge("Unable to change interface settings: " + ie); } /* Stop a running supplicant after a runtime restart * Avoids issues with drivers that do not handle interface down * on a running supplicant properly. */ mWifiMonitor.killSupplicant(mP2pSupported); if(mWifiNative.startSupplicant(mP2pSupported)) { if (DBG) log("Supplicant start successful"); mWifiMonitor.startMonitoring(); transitionTo(mSupplicantStartingState); } else { loge("Failed to start supplicant!"); } } else { loge("Failed to load driver"); } break; default: return NOT_HANDLED; } return HANDLED; } }</span></span></span>
这里主要调用WifiNative的loadDriver和startSupplicant两个函数去加载wifi driver和启动wpa_supplicant,当启动成功wpa_supplicant后,就会调用WifiMonitor的startMonitoring去和wpa_supplicant建立socket连接,并不断的从wpa_supplicant收event。wpa_supplicant是一个独立的运行程序,它和应用程序之间通过socket来通信,主要存在两个socket连接,一个用来向wpa_supplicant发送命令,另一个是wpa_supplicant用来向应用程序通知event,应用程序在收到event后可以知道当前的连接状态来进行下一步动作。我们进入到WifiMonitor.startMonitoring这个函数看看:
<span style="font-size:14px;"><span style="font-size:12px;"><span style="font-size:10px;"> public synchronized void startMonitoring(String iface) { WifiMonitor m = mIfaceMap.get(iface); if (m == null) { Log.e(TAG, "startMonitor called with unknown iface=" + iface); return; } Log.d(TAG, "startMonitoring(" + iface + ") with mConnected = " + mConnected); if (mConnected) { m.mMonitoring = true; m.mWifiStateMachine.sendMessage(SUP_CONNECTION_EVENT); } else { if (DBG) Log.d(TAG, "connecting to supplicant"); int connectTries = 0; while (true) { if (mWifiNative.connectToSupplicant()) { m.mMonitoring = true; m.mWifiStateMachine.sendMessage(SUP_CONNECTION_EVENT); new MonitorThread(mWifiNative, this).start(); mConnected = true; break; } if (connectTries++ < 5) { try { Thread.sleep(1000); } catch (InterruptedException ignore) { } } else { Log.d(TAG, "rmIfaceMap remove " + iface); mIfaceMap.remove(iface); ///M:@{ if (!m.mInterfaceName.equals("ap0")) { m.mWifiStateMachine.sendMessage(SUP_DISCONNECTION_EVENT); } ///@} Log.e(TAG, "startMonitoring(" + iface + ") failed! " ); break; } } } </span></span></span>
这个方法里面主要调用WifiNative的connenctToSupplicant去和wpa_supplicant建立socket连接,然后给WifiStateMachine发送一个SUP_CONNECTION_EVENT消息,最后新建一个MonitorThread运行,MonitorThread就是一个循环,不断的从wpa_supplicant收event,然后进行解析,并dispatch到不同的函数去处理,后面我们再来分析MonitorThread的流程。回到WifiStateMachine的InitialState中去看看如何处理CMD_START_SUPPLICANT的流程中来,当startMonitoring结束后,WifiStateMachine就跳转到SupplicantStartingState。
接着来看当WifiStateMachine处理完SUP_CONNECTION_EVENT消息后,马上会收到SET_OPERATIONAL_MODE和CMD_START_DRIVER消息,这两个消息都会被SupplicantStartingState延迟处理,SET_HTGH_PERF_MODE会被DefaultState处理。接着SupplicantStartingState会收到SUP_CONNECTION_EVENT,处理代码如下:
<span style="font-size:14px;"><span style="font-size:12px;"><span style="font-size:10px;"> @Override public boolean processMessage(Message message) { if (DBG) log(getName() + message.toString() + "\n"); switch(message.what) { case WifiMonitor.SUP_CONNECTION_EVENT: if (DBG) log("Supplicant connection established"); setWifiState(WIFI_STATE_ENABLED); mSupplicantRestartCount = 0; /* Reset the supplicant state to indicate the supplicant * state is not known at this time */ mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE); /* Initialize data structures */ mLastBssid = null; mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID; mLastSignalLevel = -1; mWifiInfo.setMacAddress(mWifiNative.getMacAddress()); mWifiConfigStore.loadAndEnableAllNetworks(); initializeWpsDetails(); mStopSupplicantScan = false; mConnectNetwork = false; mLastExplicitNetworkId = INVALID_NETWORK_ID; mOnlineStartTime = 0; mUsingPppoe = false; if (mWifiFwkExt.hasCustomizedAutoConnect()) { mWifiNative.setBssExpireAge(IWifiFwkExt.BSS_EXPIRE_AGE); mWifiNative.setBssExpireCount(IWifiFwkExt.BSS_EXPIRE_COUNT); mDisconnectOperation = false; mScanForWeakSignal = false; mShowReselectDialog = false; if (!mWifiFwkExt.shouldAutoConnect()) { disableAllNetworks(false); } } if (FeatureOption.MTK_EAP_SIM_AKA) { if (isAirplaneModeOn()) { List<WifiConfiguration> networks = mWifiConfigStore.getConfiguredNetworks(); if (null != networks) { for (WifiConfiguration network : networks) { int value = network.enterpriseConfig.getEapMethod(); Xlog.d(TAG, "EAP value:" + value); if (value == WifiEnterpriseConfig.Eap.SIM || value == WifiEnterpriseConfig.Eap.AKA) { mWifiConfigStore.disableNetwork(network.networkId, WifiConfiguration.DISABLED_UNKNOWN_REASON); } } } else { Xlog.d(TAG, "Check for EAP_SIM_AKA, networks is null!"); } } } sendSupplicantConnectionChangedBroadcast(true); transitionTo(mDriverStartedState); break; case WifiMonitor.SUP_DISCONNECTION_EVENT: if (++mSupplicantRestartCount <= SUPPLICANT_RESTART_TRIES) { loge("Failed to setup control channel, restart supplicant"); mWifiMonitor.killSupplicant(mP2pSupported); transitionTo(mInitialState); sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS); } else { loge("Failed " + mSupplicantRestartCount + " times to start supplicant, unload driver"); mSupplicantRestartCount = 0; setWifiState(WIFI_STATE_UNKNOWN); transitionTo(mInitialState); } break; case CMD_START_SUPPLICANT: case CMD_STOP_SUPPLICANT: case CMD_START_AP: case CMD_STOP_AP: case CMD_START_DRIVER: case CMD_STOP_DRIVER: case CMD_SET_OPERATIONAL_MODE: case CMD_SET_COUNTRY_CODE: case CMD_SET_FREQUENCY_BAND: case CMD_START_PACKET_FILTERING: case CMD_STOP_PACKET_FILTERING: deferMessage(message); break; default: return NOT_HANDLED; } return HANDLED; }</span></span></span>
在SUP_CONNECTION_EVENT的处理流程中,主要是调用WifiConfigStore的loadAndEnableAllNetworks函数来加载并enable用户之前连接过并保存的AP,然后会初始化一些Wps相关的信息,最后transition到DriverStartedState上。再来看DriverStartedState的enter函数,这里面有一些重要的流程:
<span style="font-size:14px;"><span style="font-size:12px;"><span style="font-size:10px;"> class DriverStartedState extends State { @Override public void enter() { /* set country code */ setCountryCode(); /* set frequency band of operation */ setFrequencyBand(); /* initialize network state */ setNetworkDetailedState(DetailedState.DISCONNECTED); mDhcpActive = false; startBatchedScan(); if (mOperationalMode != CONNECT_MODE) { mWifiNative.disconnect(); mWifiConfigStore.disableAllNetworks(); if (mOperationalMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) { setWifiState(WIFI_STATE_DISABLED); } transitionTo(mScanModeState); } else { /* Driver stop may have disabled networks, enable right after start */ mWifiConfigStore.enableAllNetworks(); if (DBG) log("Attempting to reconnect to wifi network .."); mWifiNative.reconnect(); // Status pulls in the current supplicant state and network connection state // events over the monitor connection. This helps framework sync up with // current supplicant state mWifiNative.status(); transitionTo(mDisconnectedState); }</span></span></span>
enter函数的代码比较多,上面是精简后的代码,上面主要分为两条分支,一是OperationalMode != CONNECT_MODE,一种是OperationalMode = CONNECT_MODE,根据官方的解释,OperationalMode一共有三种,分别如下:
1.CONNECT_MODE,这种模式下,STA可以scan并连接热点
2.SCAN_ONLY_MODE,这种模式下,STA只能扫描热点
3.SCAN_ONLY_WIFI_OFF_MODE,在这种模式下,当wifi是toggle off的情况下,也可以进行scan
这三种模式默认的是CONNECT_MODE,后面两种模式现在用到的不多,但按照Google的设计,后面可能会有很多的app会用到,比如利用热点来进行点位,这个应用其实这需要能够scan,并不需要链接热点。那我们接看这OperationalMode = CONNECT_MODE的流程,它直接transition 到DisconnectedState,如上图中的Figure 4,在transition到DisconnectedState之前,还会向WifiNative下reconnect的命令,用于重新连接上次连接但没有forget的AP,即开机后自动连上AP。
接着会进入mDisconnectedState状态,由于mDisconnectedState的父状态为mConnectedState状态,所以会先进入mConnectedState状态再进入mDisconnectedState状态,在这两个状态的enter方法中都没有什么特别的操作。
分析到这里,系统启动Wifi的初始化分析已经完成。
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。