Android 原來的作法是 suspend 時,wifi module 也進入 wifi suspend mode。
系統 wake up 時,wifi 回到 full power mode。
這一項還跟 Wifi 的 Advance setting 有關,可以設定 suspend 時,wifi 是不是要開啟。
如果選不要,suspend 時,wifi moduel 會進入 idle mode (好像也是 suspend mode)
另外,會開啟 wifi module 的 wow (wake on wireless) -- wifi 有動作時會 wakeup 系統(?).
以上這些功能都要拿掉。
所以...
1.先改 WifiService.
原來 ACTION_SCREEN_ON/OFF 的 broadcastreceiver 在 wifi disable 就被 unregister 了,
enable 時才重新 register。
這樣不行,這樣 disable 後就收不到 SCREEN_ON 了。
所以修改一下,在 WifiService( ) Initiate 時就 register,在 disable/enable 時不做。
讓 SCREEN_ON/OFF 的 broadcastreceiver 永遠存在。
2.修改原有 mDeviceIdle 的行為
原來 mDeviceIdle 這個 private variable 會讓 wifi 進入 suspend,
現在改掉,讓 mDeviceIdle = true 時,會 disable wifi。
這段 code 在 WifiService 的 doUpdateWifiState( )
原來 :
boolean wifiShouldBeEnabled = wifiEnabled && !airplaneMode;
加上..
boolean wifiShouldBeEnabled = wifiEnabled && !airplaneMode && !mDeviceIdle;
3. SCREEN_ON,OFF 的 action
原來是設一個 timer , delay,之後再把 wifi 進入 idle。
現在直接進入 idle。
-- follow MESSAGE_DEVICE_IDLE 的 code。
這樣做完後,有時候會發生 unload driver 還沒完全結束,就 suspend 了,然後 wakeup 還繼續作 suspend,
這樣就和 SCREEN_ON 的 動作衝突,導致 wakeup 時 load module fail。
所以參考 stop wifi 的 code,用 PowerManager 的 wakelock 來防止 系統太早 suspend.
就是在 doUpdateWifiState( ),最後,sendEnableMessage( ) 時,設定一個 3 sec 的 wakelock:
sDriverStopWakeLock.acquire();
sWakeLock.acquire();
sendEnableMessage(false, false, ....);
mWifiHandler.sendEmptyMessageDelayed(MESSAGE_RELEASE_WAKELOCK,3000);
其實這是猜 unload module 的時間不會超過 3 sec, 其實有點不好。這樣做完後, suspend/wakeup 10 多次後,偶而會出現 Nullpointer Exception,
指出發生在 WifiStateTracker.java 裡..
+ synchronized (this) {
if (mDhcpTarget != null) {
+ Log.d(TAG,"setCancelCallback");
mDhcpTarget.setCancelCallback(true);
+ Log.d(TAG,"removeMessges");
mDhcpTarget.removeMessages(EVENT_DHCP_START);
}
+ }
猜是 mDhcpTarget 在中間被變更 release 掉了,所以才會有這個 Exception,
所以在這段 code 加上 synchronized( ) 。
同樣,在 release mDhcpTarget 的 code,也用 synchroinzed 包起來:
// When supplicant dies, kill the DHCP thread
+ synchronized (this) {
if (mDhcpTarget != null) {
+ Log.d(TAG,"set mDhcpTarget to null");
mDhcpTarget.getLooper().quit();
mDhcpTarget = null;
}
+ }
這樣改改完,好像就沒發生了...
測試中...
總結大概是:
- SCREEN_ON/OFF
- 等 unload 完成
- mDhcpTarget 的同步
測試結果是 fail,最後好像還是沒有 unload 完成就 suspend 了..
所以用 delay 的方式還是不可行,要用同步。
所以修改 wlan_tool,在 rmmod 時加上 wakelock:
echo rmmodar6000 > /sys/power/wake_lock rmmod ar6000 echo rmmodar6000 > /sys/power/wake_unlock
繼續測試..
還是 fail...
查 log,好像是在 wakeup 後,load driver 時,剛好跟 sdio host driver 衝突。
所以修改 wlan_tool, 的 loaddriver,sleep 2。
之後測試一個晚上 OK (on/off 次數超過前面幾次 fail 的次數)。
但是用 sleep 的方式不好,應該要 check sysfs 看看 sdio driver ready 了沒