1. 從Settings開始
1.1 Settings的UI
上圖的效果是有Settings的布局管理文件settings_headers.xml控制的,位置:packages/apps/Settings/res/xml/settings_headers.xml
<!-- WIRELESS and NETWORKS --> <header android:id="@+id/wireless_section" android:title="@string/header_category_wireless_networks" /> <!-- Wifi --> <header android:id="@+id/wifi_settings" android:fragment="com.android.settings.wifi.WifiSettings" android:title="@string/wifi_settings_title" android:icon="@drawable/ic_settings_wireless" /> <!-- Ethernet --> <header android:id="@+id/ethernet_settings" android:fragment="com.android.settings.ethernet.EthernetSettings" android:title="@string/ethernet_settings_title" android:icon="@drawable/ic_settings_ethernet" />
1.2 Activity
com.android.settings.Settings.java 這個activity 是通過回調onBuildHeaders方法來加載進入應用之后的第一個布局文件的:
public void onBuildHeaders(List<Header> headers) { if (!onIsHidingHeaders()) { loadHeadersFromResource(R.xml.settings_headers, headers); updateHeaderList(headers); } }
那么每一個header 是如果響應點擊操作的呢.這個就要看Setting.java的onHeaderClick 方法了, onHeaderClick 方法會調用父類的onHeaderClick方法來打開相關的應用,其父類是根據我們配置在settings_headers.xml里面的 fragment和intent 來打開相對應的activity的.
public class Settings extends PreferenceActivity implements ButtonBarHandler, OnAccountsUpdateListener { @Override public void onHeaderClick(Header header, int position) { boolean revert = false; if (header.id == R.id.account_add) { revert = true; } super.onHeaderClick(header, position); if (revert && mLastHeader != null) { highlightHeader((int) mLastHeader.id); } else { mLastHeader = header; } } }
frameworks/base/core/java/android/preference/PreferenceActivity.java
public void onHeaderClick(Header header, int position) { if (header.fragment != null) { if (mSinglePane) { int titleRes = header.breadCrumbTitleRes; int shortTitleRes = header.breadCrumbShortTitleRes; if (titleRes == 0) { titleRes = header.titleRes; shortTitleRes = 0; } startWithFragment(header.fragment, header.fragmentArguments, null, 0, titleRes, shortTitleRes); } else { switchToHeader(header); } } else if (header.intent != null) { startActivity(header.intent); } }
點擊后啟動WifiSettings Activity,WifiSettings中onActivityCreated方法會獲取WifiManager的控制句柄,並把控制WiFi開關的Switch與WifiEnabler關聯起來,代碼如下:
// On/off switch is hidden for Setup Wizard if (!mSetupWizardMode) { Switch actionBarSwitch = new Switch(activity); if (activity instanceof PreferenceActivity) { PreferenceActivity preferenceActivity = (PreferenceActivity) activity; if (preferenceActivity.onIsHidingHeaders() || !preferenceActivity.onIsMultiPane()) { final int padding = activity.getResources().getDimensionPixelSize( R.dimen.action_bar_switch_padding); actionBarSwitch.setPaddingRelative(0, 0, padding, 0); activity.getActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM, ActionBar.DISPLAY_SHOW_CUSTOM); activity.getActionBar().setCustomView(actionBarSwitch, new ActionBar.LayoutParams( ActionBar.LayoutParams.WRAP_CONTENT, ActionBar.LayoutParams.WRAP_CONTENT, Gravity.CENTER_VERTICAL | Gravity.END)); } } mWifiEnabler = new WifiEnabler(activity, actionBarSwitch); }
android/packages/apps/Settings/src/com/android/settings/wifi/WifiEnabler.java
public class WifiEnabler implements CompoundButton.OnCheckedChangeListener { public WifiEnabler(Context context, Switch switch_) { mContext = context; mSwitch = switch_; mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); mIntentFilter = new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION); // The order matters! We really should not depend on this. :( mIntentFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION); mIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); } public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { //Do nothing if called as a result of a state machine event if (mStateMachineEvent) { return; } // Show toast message if Wi-Fi is not allowed in airplane mode if (isChecked && !WirelessSettings.isRadioAllowed(mContext, Settings.Global.RADIO_WIFI)) { Toast.makeText(mContext, R.string.wifi_in_airplane_mode, Toast.LENGTH_SHORT).show(); // Reset switch to off. No infinite check/listenenr loop. buttonView.setChecked(false); return; } // Disable tethering if enabling Wifi int wifiApState = mWifiManager.getWifiApState(); if (isChecked && ((wifiApState == WifiManager.WIFI_AP_STATE_ENABLING) || (wifiApState == WifiManager.WIFI_AP_STATE_ENABLED))) { mWifiManager.setWifiApEnabled(null, false); } mSwitch.setEnabled(false); if (!mWifiManager.setWifiEnabled(isChecked)) { // Error mSwitch.setEnabled(true); Toast.makeText(mContext, R.string.wifi_error, Toast.LENGTH_SHORT).show(); } } }
由於WifiEnabler實現了CompoundButton.OnCheckedChangeListener,onCheckedChanged()會在按鈕選中狀態發生改變時被調用,即打開或關閉WiFi時調用此函數。
通過調用WifiManager.setWifiEnabled()實現打開動作。
2. 進入Android frameworks
frameworks/base/wifi/java/android/net/wifi/WifiManager.java
public class WifiManager { IWifiManager mService; public WifiManager(Context context, IWifiManager service) { mContext = context; mService = service; init(); } public boolean setWifiEnabled(boolean enabled) { try { return mService.setWifiEnabled(enabled); } catch (RemoteException e) { return false; } } }
通過AIDL調用,實際調用的是 WifiService的setWifiEnabled 函數
frameworks/base/services/java/com/android/server/wifi/WifiService.java
public synchronized boolean setWifiEnabled(boolean enable) { enforceChangePermission(); Slog.d(TAG, "setWifiEnabled: " + enable + " pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()); if (DBG) { Slog.e(TAG, "Invoking mWifiStateMachine.setWifiEnabled\n"); } /* * 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); } mWifiController.sendMessage(CMD_WIFI_TOGGLED); return true; }
frameworks/base/services/java/com/android/server/wifi/WifiController.java
class WifiController extends StateMachine { WifiController(Context context, WifiService service, Looper looper) { super(TAG, looper); 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 (isWifiEnabled && isScanningAlwaysAvailable) {
setInitialState(mStaDisabledWithScanState);
} else {
setInitialState(mApStaDisabledState);
}
} }
初始狀態為ApStaDisabledState,當收到CMD_WIFI_TOGGLED消息后,狀態會轉換到DeviceActiveState。
class ApStaDisabledState extends State { public void enter() { mWifiStateMachine.setSupplicantRunning(false); // Supplicant can't restart right away, so not the time we switched off mDisabledTimestamp = SystemClock.elapsedRealtime(); mDeferredEnableSerialNumber++; mHaveDeferredEnable = false; } public boolean processMessage(Message msg) { switch (msg.what) { case CMD_WIFI_TOGGLED: case CMD_AIRPLANE_TOGGLED: if (mSettingsStore.isWifiToggleEnabled()) { if (doDeferEnable(msg)) { if (mHaveDeferredEnable) { mDeferredEnableSerialNumber++; } mHaveDeferredEnable = !mHaveDeferredEnable; break; } if (mDeviceIdle == false) { transitionTo(mDeviceActiveState); } else { checkLocksAndTransitionWhenDeviceIdle(); } } break; ........... }
由於DeviceActiveState的父狀態為StaEnabledState,所以在有ApStaDisabledState進入DeviceActiveState時,會先進入StaEnabledState,然后再進入DeviceActiveState。因此,先調用StaEnabledState的enter():
class StaEnabledState extends State { @Override public void enter() { mWifiStateMachine.setSupplicantRunning(true); } public boolean processMessage(Message msg) { switch (msg.what) { } }
然后再調用DeviceActiveState的enter():
class DeviceActiveState extends State { @Override public void enter() { mWifiStateMachine.setOperationalMode(WifiStateMachine.CONNECT_MODE); mWifiStateMachine.setDriverStart(true); mWifiStateMachine.setHighPerfModeEnabled(false); } public boolean processMessage(Message msg) { } }
frameworks/base/wifi/java/android/net/wifi/WifiStateMachine.java
public void setSupplicantRunning(boolean enable) { if (enable) { sendMessage(CMD_START_SUPPLICANT); } else { sendMessage(CMD_STOP_SUPPLICANT); } }
此時WiFi狀態機為初始狀態InitialState,處理CMD_START_SUPPLICANT的動作:
class InitialState extends State { public boolean processMessage(Message message) { switch (message.what) { case CMD_START_SUPPLICANT: if (mWifiNative.loadDriver()) {//加載驅動 try { mNwService.wifiFirmwareReload(mInterfaceName, "STA");//download firmware } 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); } mWifiMonitor.killSupplicant(mP2pSupported); if(mWifiNative.startSupplicant(mP2pSupported)) {//重啟WPA setWifiState(WIFI_STATE_ENABLING); if (DBG) log("Supplicant start successful"); mWifiMonitor.startMonitoring(); transitionTo(mSupplicantStartingState); } else { loge("Failed to start supplicant!"); } } else { loge("Failed to load driver"); } break; }
WifiNative通過JNI調用 wifi.c,加載WiFi驅動,加載成功后會創建網絡接口wlan0,然后啟動wpa_supplicant。都啟動成功后界面上會顯示WiFi激活。