diff options
Diffstat (limited to 'wifi/java/android')
-rw-r--r-- | wifi/java/android/net/wifi/WifiApConfigStore.java | 245 | ||||
-rw-r--r-- | wifi/java/android/net/wifi/WifiStateMachine.java | 363 | ||||
-rw-r--r-- | wifi/java/android/net/wifi/WifiWatchdogStateMachine.java | 113 |
3 files changed, 499 insertions, 222 deletions
diff --git a/wifi/java/android/net/wifi/WifiApConfigStore.java b/wifi/java/android/net/wifi/WifiApConfigStore.java index bb5427d..0531ca3 100644 --- a/wifi/java/android/net/wifi/WifiApConfigStore.java +++ b/wifi/java/android/net/wifi/WifiApConfigStore.java @@ -19,11 +19,16 @@ package android.net.wifi; import android.content.Context; import android.net.wifi.WifiConfiguration.KeyMgmt; import android.os.Environment; -import android.os.Message; import android.os.Handler; -import android.os.HandlerThread; +import android.os.Message; +import android.os.Messenger; import android.util.Log; +import com.android.internal.util.AsyncChannel; +import com.android.internal.R; +import com.android.internal.util.State; +import com.android.internal.util.StateMachine; + import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataInputStream; @@ -34,16 +39,13 @@ import java.io.IOException; import java.net.InetAddress; import java.util.UUID; -import com.android.internal.R; - - /** * Provides API to the WifiStateMachine for doing read/write access * to soft access point configuration */ -class WifiApConfigStore { +class WifiApConfigStore extends StateMachine { - private static Context sContext; + private Context mContext; private static final String TAG = "WifiApConfigStore"; private static final String AP_CONFIG_FILE = Environment.getDataDirectory() + @@ -51,131 +53,160 @@ class WifiApConfigStore { private static final int AP_CONFIG_FILE_VERSION = 1; - private static WifiConfiguration sApConfig = new WifiConfiguration(); - private static final Object sApConfigLock = new Object(); + private State mDefaultState = new DefaultState(); + private State mInactiveState = new InactiveState(); + private State mActiveState = new ActiveState(); + + private WifiConfiguration mWifiApConfig = null; + private AsyncChannel mReplyChannel = new AsyncChannel(); - private static FileReadWriteHandler sFileReadWriteHandler; - private static final int READ_AP_CONFIG = 1; - private static final int WRITE_AP_CONFIG = 2; + WifiApConfigStore(Context context, Handler target) { + super(TAG, target.getLooper()); - static void initialize(Context context) { - sContext = context; + mContext = context; + addState(mDefaultState); + addState(mInactiveState, mDefaultState); + addState(mActiveState, mDefaultState); - /* File operations happen on a seperate thread */ - HandlerThread configThread = new HandlerThread("WifiApConfigStore"); - configThread.start(); - sFileReadWriteHandler = new FileReadWriteHandler(configThread.getLooper()); - Message.obtain(sFileReadWriteHandler, READ_AP_CONFIG).sendToTarget(); + setInitialState(mInactiveState); } + public static WifiApConfigStore makeWifiApConfigStore(Context context, Handler target) { + WifiApConfigStore s = new WifiApConfigStore(context, target); + s.start(); + return s; + } - static void setApConfiguration(WifiConfiguration config) { - synchronized (sApConfigLock) { - sApConfig = config; + class DefaultState extends State { + public boolean processMessage(Message message) { + switch (message.what) { + case WifiStateMachine.CMD_SET_AP_CONFIG: + case WifiStateMachine.CMD_SET_AP_CONFIG_COMPLETED: + Log.e(TAG, "Unexpected message: " + message); + break; + case WifiStateMachine.CMD_REQUEST_AP_CONFIG: + mReplyChannel.replyToMessage(message, + WifiStateMachine.CMD_RESPONSE_AP_CONFIG, mWifiApConfig); + break; + default: + Log.e(TAG, "Failed to handle " + message); + break; + } + return HANDLED; } - Message.obtain(sFileReadWriteHandler, WRITE_AP_CONFIG, new WifiConfiguration(config)) - .sendToTarget(); } - static WifiConfiguration getApConfiguration() { - synchronized (sApConfigLock) { - return new WifiConfiguration(sApConfig); + class InactiveState extends State { + public boolean processMessage(Message message) { + switch (message.what) { + case WifiStateMachine.CMD_SET_AP_CONFIG: + mWifiApConfig = (WifiConfiguration) message.obj; + transitionTo(mActiveState); + break; + default: + return NOT_HANDLED; + } + return HANDLED; } } - /** - * File read/write handler - */ - private static class FileReadWriteHandler extends Handler { - - public FileReadWriteHandler(android.os.Looper looper) { - super(looper); + class ActiveState extends State { + public void enter() { + new Thread(new Runnable() { + public void run() { + writeApConfiguration(mWifiApConfig); + sendMessage(WifiStateMachine.CMD_SET_AP_CONFIG_COMPLETED); + } + }).start(); } - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case WRITE_AP_CONFIG: - writeApConfiguration((WifiConfiguration) msg.obj); + public boolean processMessage(Message message) { + switch (message.what) { + //TODO: have feedback to the user when we do this + //to indicate the write is currently in progress + case WifiStateMachine.CMD_SET_AP_CONFIG: + deferMessage(message); break; - case READ_AP_CONFIG: - readApConfiguration(); + case WifiStateMachine.CMD_SET_AP_CONFIG_COMPLETED: + transitionTo(mInactiveState); break; default: - Log.e(TAG, "Unknown command in FileReadWriteHandler: " + msg); - break; + return NOT_HANDLED; } + return HANDLED; } + } - private static void writeApConfiguration(final WifiConfiguration config) { - DataOutputStream out = null; - try { - out = new DataOutputStream(new BufferedOutputStream( - new FileOutputStream(AP_CONFIG_FILE))); - - out.writeInt(AP_CONFIG_FILE_VERSION); - out.writeUTF(config.SSID); - int authType = config.getAuthType(); - out.writeInt(authType); - if(authType != KeyMgmt.NONE) { - out.writeUTF(config.preSharedKey); - } - } catch (IOException e) { - Log.e(TAG, "Error writing hotspot configuration" + e); - } finally { - if (out != null) { - try { - out.close(); - } catch (IOException e) {} - } - } - } + void loadApConfiguration() { + DataInputStream in = null; + try { + WifiConfiguration config = new WifiConfiguration(); + in = new DataInputStream(new BufferedInputStream(new FileInputStream( + AP_CONFIG_FILE))); - private static void readApConfiguration() { - DataInputStream in = null; - try { - WifiConfiguration config = new WifiConfiguration(); - in = new DataInputStream(new BufferedInputStream(new FileInputStream( - AP_CONFIG_FILE))); - - int version = in.readInt(); - if (version != 1) { - Log.e(TAG, "Bad version on hotspot configuration file, set defaults"); - setDefaultApConfiguration(); - return; - } - config.SSID = in.readUTF(); - int authType = in.readInt(); - config.allowedKeyManagement.set(authType); - if (authType != KeyMgmt.NONE) { - config.preSharedKey = in.readUTF(); - } - synchronized (sApConfigLock) { - sApConfig = config; - } - } catch (IOException ignore) { + int version = in.readInt(); + if (version != 1) { + Log.e(TAG, "Bad version on hotspot configuration file, set defaults"); setDefaultApConfiguration(); - } finally { - if (in != null) { - try { - in.close(); - } catch (IOException e) {} - } + return; + } + config.SSID = in.readUTF(); + int authType = in.readInt(); + config.allowedKeyManagement.set(authType); + if (authType != KeyMgmt.NONE) { + config.preSharedKey = in.readUTF(); + } + mWifiApConfig = config; + } catch (IOException ignore) { + setDefaultApConfiguration(); + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException e) {} } } + } - /* Generate a default WPA2 based configuration with a random password. - We are changing the Wifi Ap configuration storage from secure settings to a - flat file accessible only by the system. A WPA2 based default configuration - will keep the device secure after the update */ - private static void setDefaultApConfiguration() { - WifiConfiguration config = new WifiConfiguration(); - config.SSID = sContext.getString(R.string.wifi_tether_configure_ssid_default); - config.allowedKeyManagement.set(KeyMgmt.WPA2_PSK); - String randomUUID = UUID.randomUUID().toString(); - //first 12 chars from xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx - config.preSharedKey = randomUUID.substring(0, 8) + randomUUID.substring(9,13); - setApConfiguration(config); + Messenger getMessenger() { + return new Messenger(getHandler()); + } + + private void writeApConfiguration(final WifiConfiguration config) { + DataOutputStream out = null; + try { + out = new DataOutputStream(new BufferedOutputStream( + new FileOutputStream(AP_CONFIG_FILE))); + + out.writeInt(AP_CONFIG_FILE_VERSION); + out.writeUTF(config.SSID); + int authType = config.getAuthType(); + out.writeInt(authType); + if(authType != KeyMgmt.NONE) { + out.writeUTF(config.preSharedKey); + } + } catch (IOException e) { + Log.e(TAG, "Error writing hotspot configuration" + e); + } finally { + if (out != null) { + try { + out.close(); + } catch (IOException e) {} + } } } + + /* Generate a default WPA2 based configuration with a random password. + We are changing the Wifi Ap configuration storage from secure settings to a + flat file accessible only by the system. A WPA2 based default configuration + will keep the device secure after the update */ + private void setDefaultApConfiguration() { + WifiConfiguration config = new WifiConfiguration(); + config.SSID = mContext.getString(R.string.wifi_tether_configure_ssid_default); + config.allowedKeyManagement.set(KeyMgmt.WPA2_PSK); + String randomUUID = UUID.randomUUID().toString(); + //first 12 chars from xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx + config.preSharedKey = randomUUID.substring(0, 8) + randomUUID.substring(9,13); + sendMessage(WifiStateMachine.CMD_SET_AP_CONFIG, config); + } } diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java index e981da7..3ed9bd5 100644 --- a/wifi/java/android/net/wifi/WifiStateMachine.java +++ b/wifi/java/android/net/wifi/WifiStateMachine.java @@ -123,6 +123,8 @@ public class WifiStateMachine extends StateMachine { private final LruCache<String, ScanResult> mScanResultCache; private String mInterfaceName; + /* Tethering interface could be seperate from wlan interface */ + private String mTetherInterfaceName; private int mLastSignalLevel = -1; private String mLastBssid; @@ -156,6 +158,14 @@ public class WifiStateMachine extends StateMachine { /* Tracks sequence number on stop failure message */ private int mSupplicantStopFailureToken = 0; + /** + * Tether state change notification time out + */ + private static final int TETHER_NOTIFICATION_TIME_OUT_MSECS = 5000; + + /* Tracks sequence number on a tether notification time out */ + private int mTetherToken = 0; + private LinkProperties mLinkProperties; // Wakelock held during wifi start/stop and driver load/unload @@ -184,6 +194,7 @@ public class WifiStateMachine extends StateMachine { private WifiP2pManager mWifiP2pManager; //Used to initiate a connection with WifiP2pService private AsyncChannel mWifiP2pChannel = new AsyncChannel(); + private AsyncChannel mWifiApConfigChannel = new AsyncChannel(); // Event log tags (must be in sync with event-log-tags) private static final int EVENTLOG_WIFI_STATE_CHANGED = 50021; @@ -211,7 +222,7 @@ public class WifiStateMachine extends StateMachine { static final int CMD_STOP_SUPPLICANT = BASE + 12; /* Start the driver */ static final int CMD_START_DRIVER = BASE + 13; - /* Start the driver */ + /* Stop the driver */ static final int CMD_STOP_DRIVER = BASE + 14; /* Indicates Static IP succeded */ static final int CMD_STATIC_IP_SUCCESS = BASE + 15; @@ -219,6 +230,9 @@ public class WifiStateMachine extends StateMachine { static final int CMD_STATIC_IP_FAILURE = BASE + 16; /* Indicates supplicant stop failed */ static final int CMD_STOP_SUPPLICANT_FAILED = BASE + 17; + /* Delayed stop to avoid shutting down driver too quick*/ + static final int CMD_DELAYED_STOP_DRIVER = BASE + 18; + /* Start the soft access point */ static final int CMD_START_AP = BASE + 21; @@ -230,12 +244,18 @@ public class WifiStateMachine extends StateMachine { static final int CMD_STOP_AP = BASE + 24; /* Set the soft access point configuration */ static final int CMD_SET_AP_CONFIG = BASE + 25; - /* Get the soft access point configuration */ - static final int CMD_GET_AP_CONFIG = BASE + 26; - /* Set configuration on tether interface */ - static final int CMD_TETHER_INTERFACE = BASE + 27; - - static final int CMD_BLUETOOTH_ADAPTER_STATE_CHANGE = BASE + 28; + /* Soft access point configuration set completed */ + static final int CMD_SET_AP_CONFIG_COMPLETED = BASE + 26; + /* Request the soft access point configuration */ + static final int CMD_REQUEST_AP_CONFIG = BASE + 27; + /* Response to access point configuration request */ + static final int CMD_RESPONSE_AP_CONFIG = BASE + 28; + /* Invoked when getting a tether state change notification */ + static final int CMD_TETHER_STATE_CHANGE = BASE + 29; + /* A delayed message sent to indicate tether state change failed to arrive */ + static final int CMD_TETHER_NOTIFICATION_TIMED_OUT = BASE + 30; + + static final int CMD_BLUETOOTH_ADAPTER_STATE_CHANGE = BASE + 31; /* Supplicant commands */ /* Is supplicant alive ? */ @@ -389,6 +409,13 @@ public class WifiStateMachine extends StateMachine { private static final int MIN_INTERVAL_ENABLE_ALL_NETWORKS_MS = 10 * 60 * 1000; /* 10 minutes */ private long mLastEnableAllNetworksTime; + /** + * Starting and shutting down driver too quick causes problems leading to driver + * being in a bad state. Delay driver stop. + */ + private static final int DELAYED_DRIVER_STOP_MS = 2 * 60 * 1000; /* 2 minutes */ + private int mDelayedStopCounter; + private boolean mInDelayedStop = false; private static final int MIN_RSSI = -200; private static final int MAX_RSSI = 256; @@ -440,12 +467,25 @@ public class WifiStateMachine extends StateMachine { private State mSoftApStartingState = new SoftApStartingState(); /* Soft ap is running */ private State mSoftApStartedState = new SoftApStartedState(); + /* Soft ap is running and we are waiting for tether notification */ + private State mTetheringState = new TetheringState(); /* Soft ap is running and we are tethered through connectivity service */ private State mTetheredState = new TetheredState(); + /* Waiting for untether confirmation to stop soft Ap */ + private State mSoftApStoppingState = new SoftApStoppingState(); /* Wait till p2p is disabled */ private State mWaitForP2pDisableState = new WaitForP2pDisableState(); + private class TetherStateChange { + ArrayList<String> available; + ArrayList<String> active; + TetherStateChange(ArrayList<String> av, ArrayList<String> ac) { + available = av; + active = ac; + } + } + /** * One of {@link WifiManager#WIFI_STATE_DISABLED}, @@ -520,6 +560,11 @@ public class WifiStateMachine extends StateMachine { mWpsStateMachine = new WpsStateMachine(context, this, getHandler()); mLinkProperties = new LinkProperties(); + WifiApConfigStore wifiApConfigStore = WifiApConfigStore.makeWifiApConfigStore( + context, getHandler()); + wifiApConfigStore.loadApConfiguration(); + mWifiApConfigChannel.connectSync(mContext, getHandler(), wifiApConfigStore.getMessenger()); + mNetworkInfo.setIsAvailable(false); mLinkProperties.clear(); mLastBssid = null; @@ -542,7 +587,9 @@ public class WifiStateMachine extends StateMachine { public void onReceive(Context context, Intent intent) { ArrayList<String> available = intent.getStringArrayListExtra( ConnectivityManager.EXTRA_AVAILABLE_TETHER); - sendMessage(CMD_TETHER_INTERFACE, available); + ArrayList<String> active = intent.getStringArrayListExtra( + ConnectivityManager.EXTRA_ACTIVE_TETHER); + sendMessage(CMD_TETHER_STATE_CHANGE, new TetherStateChange(available, active)); } },new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED)); @@ -583,7 +630,9 @@ public class WifiStateMachine extends StateMachine { addState(mSupplicantStoppingState, mDefaultState); addState(mSoftApStartingState, mDefaultState); addState(mSoftApStartedState, mDefaultState); + addState(mTetheringState, mSoftApStartedState); addState(mTetheredState, mSoftApStartedState); + addState(mSoftApStoppingState, mDefaultState); addState(mWaitForP2pDisableState, mDefaultState); setInitialState(mInitialState); @@ -649,11 +698,11 @@ public class WifiStateMachine extends StateMachine { } public void setWifiApConfiguration(WifiConfiguration config) { - sendMessage(obtainMessage(CMD_SET_AP_CONFIG, config)); + mWifiApConfigChannel.sendMessage(CMD_SET_AP_CONFIG, config); } - public WifiConfiguration syncGetWifiApConfiguration(AsyncChannel channel) { - Message resultMsg = channel.sendMessageSynchronously(CMD_GET_AP_CONFIG); + public WifiConfiguration syncGetWifiApConfiguration() { + Message resultMsg = mWifiApConfigChannel.sendMessageSynchronously(CMD_REQUEST_AP_CONFIG); WifiConfiguration ret = (WifiConfiguration) resultMsg.obj; resultMsg.recycle(); return ret; @@ -1119,6 +1168,7 @@ public class WifiStateMachine extends StateMachine { loge("Error tethering on " + intf); return false; } + mTetherInterfaceName = intf; return true; } } @@ -1145,11 +1195,27 @@ public class WifiStateMachine extends StateMachine { loge("Error resetting interface " + mInterfaceName + ", :" + e); } - if (mCm.untether(mInterfaceName) != ConnectivityManager.TETHER_ERROR_NO_ERROR) { + if (mCm.untether(mTetherInterfaceName) != ConnectivityManager.TETHER_ERROR_NO_ERROR) { loge("Untether initiate failed!"); } } + private boolean isWifiTethered(ArrayList<String> active) { + + checkAndSetConnectivityInstance(); + + String[] wifiRegexs = mCm.getTetherableWifiRegexs(); + for (String intf : active) { + for (String regex : wifiRegexs) { + if (intf.matches(regex)) { + return true; + } + } + } + // We found no interfaces that are tethered + return false; + } + /** * Set the country code from the system setting value, if any. */ @@ -1651,6 +1717,7 @@ public class WifiStateMachine extends StateMachine { mDhcpInfoInternal = dhcpInfoInternal; } mLastSignalLevel = -1; // force update of signal strength + mReconnectCount = 0; //Reset IP failure tracking WifiConfigStore.setIpConfiguration(mLastNetworkId, dhcpInfoInternal); InetAddress addr = NetworkUtils.numericToInetAddress(dhcpInfoInternal.ipAddress); mWifiInfo.setInetAddress(addr); @@ -1703,25 +1770,27 @@ public class WifiStateMachine extends StateMachine { * TODO: Add control channel setup through hostapd that allows changing config * on a running daemon */ - private boolean startSoftApWithConfig(WifiConfiguration config) { - if (config == null) { - config = WifiApConfigStore.getApConfiguration(); - } else { - WifiApConfigStore.setApConfiguration(config); - } - try { - mNwService.startAccessPoint(config, mInterfaceName, SOFTAP_IFACE); - } catch (Exception e) { - loge("Exception in softap start " + e); - try { - mNwService.stopAccessPoint(mInterfaceName); - mNwService.startAccessPoint(config, mInterfaceName, SOFTAP_IFACE); - } catch (Exception e1) { - loge("Exception in softap re-start " + e1); - return false; + private void startSoftApWithConfig(final WifiConfiguration config) { + // start hostapd on a seperate thread + new Thread(new Runnable() { + public void run() { + try { + mNwService.startAccessPoint(config, mInterfaceName, SOFTAP_IFACE); + } catch (Exception e) { + loge("Exception in softap start " + e); + try { + mNwService.stopAccessPoint(mInterfaceName); + mNwService.startAccessPoint(config, mInterfaceName, SOFTAP_IFACE); + } catch (Exception e1) { + loge("Exception in softap re-start " + e1); + sendMessage(CMD_START_AP_FAILURE); + return; + } + } + if (DBG) log("Soft AP start successful"); + sendMessage(CMD_START_AP_SUCCESS); } - } - return true; + }).start(); } /******************************************************** @@ -1764,13 +1833,6 @@ public class WifiStateMachine extends StateMachine { case CMD_ENABLE_BACKGROUND_SCAN: mEnableBackgroundScan = (message.arg1 == 1); break; - case CMD_SET_AP_CONFIG: - WifiApConfigStore.setApConfiguration((WifiConfiguration) message.obj); - break; - case CMD_GET_AP_CONFIG: - WifiConfiguration config = WifiApConfigStore.getApConfiguration(); - mReplyChannel.replyToMessage(message, message.what, config); - break; /* Discard */ case CMD_LOAD_DRIVER: case CMD_UNLOAD_DRIVER: @@ -1779,11 +1841,13 @@ public class WifiStateMachine extends StateMachine { case CMD_STOP_SUPPLICANT_FAILED: case CMD_START_DRIVER: case CMD_STOP_DRIVER: + case CMD_DELAYED_STOP_DRIVER: case CMD_START_AP: case CMD_START_AP_SUCCESS: case CMD_START_AP_FAILURE: case CMD_STOP_AP: - case CMD_TETHER_INTERFACE: + case CMD_TETHER_STATE_CHANGE: + case CMD_TETHER_NOTIFICATION_TIMED_OUT: case CMD_START_SCAN: case CMD_DISCONNECT: case CMD_RECONNECT: @@ -1811,6 +1875,11 @@ public class WifiStateMachine extends StateMachine { case CMD_ENABLE_ALL_NETWORKS: case DhcpStateMachine.CMD_PRE_DHCP_ACTION: case DhcpStateMachine.CMD_POST_DHCP_ACTION: + /* Handled by WifiApConfigStore */ + case CMD_SET_AP_CONFIG: + case CMD_SET_AP_CONFIG_COMPLETED: + case CMD_REQUEST_AP_CONFIG: + case CMD_RESPONSE_AP_CONFIG: break; case WifiMonitor.DRIVER_HUNG_EVENT: setWifiEnabled(false); @@ -1844,8 +1913,6 @@ public class WifiStateMachine extends StateMachine { // 50021 wifi_state_changed (custom|1|5) EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName()); - WifiApConfigStore.initialize(mContext); - if (WifiNative.isDriverLoaded()) { transitionTo(mDriverLoadedState); } @@ -2441,6 +2508,7 @@ public class WifiStateMachine extends StateMachine { EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName()); mIsRunning = true; + mInDelayedStop = false; updateBatteryWorkSource(null); /** @@ -2520,6 +2588,30 @@ public class WifiStateMachine extends StateMachine { WifiNative.setBluetoothCoexistenceScanModeCommand(mBluetoothConnectionActive); break; case CMD_STOP_DRIVER: + /* Already doing a delayed stop */ + if (mInDelayedStop) { + if (DBG) log("Already in delayed stop"); + break; + } + mInDelayedStop = true; + mDelayedStopCounter++; + if (DBG) log("Delayed stop message " + mDelayedStopCounter); + sendMessageDelayed(obtainMessage(CMD_DELAYED_STOP_DRIVER, mDelayedStopCounter, + 0), DELAYED_DRIVER_STOP_MS); + break; + case CMD_START_DRIVER: + if (mInDelayedStop) { + mInDelayedStop = false; + mDelayedStopCounter++; + if (DBG) log("Delayed stop ignored due to start"); + } + break; + case CMD_DELAYED_STOP_DRIVER: + if (message.arg1 != mDelayedStopCounter) break; + if (getCurrentState() != mDisconnectedState) { + WifiNative.disconnectCommand(); + handleNetworkDisconnect(); + } mWakeLock.acquire(); WifiNative.stopDriverCommand(); transitionTo(mDriverStoppingState); @@ -2878,10 +2970,6 @@ public class WifiStateMachine extends StateMachine { /* Ignore */ case WifiMonitor.NETWORK_CONNECTION_EVENT: break; - case CMD_STOP_DRIVER: - sendMessage(CMD_DISCONNECT); - deferMessage(message); - break; case CMD_SET_SCAN_MODE: if (message.arg1 == SCAN_ONLY_MODE) { sendMessage(CMD_DISCONNECT); @@ -2936,10 +3024,6 @@ public class WifiStateMachine extends StateMachine { WifiNative.disconnectCommand(); transitionTo(mDisconnectingState); break; - case CMD_STOP_DRIVER: - sendMessage(CMD_DISCONNECT); - deferMessage(message); - break; case CMD_REQUEST_CM_WAKELOCK: checkAndSetConnectivityInstance(); mCm.requestNetworkTransitionWakelock(TAG); @@ -3035,9 +3119,6 @@ public class WifiStateMachine extends StateMachine { public boolean processMessage(Message message) { if (DBG) log(getName() + message.toString() + "\n"); switch (message.what) { - case CMD_STOP_DRIVER: /* Stop driver only after disconnect handled */ - deferMessage(message); - break; case CMD_SET_SCAN_MODE: if (message.arg1 == SCAN_ONLY_MODE) { deferMessage(message); @@ -3217,21 +3298,19 @@ public class WifiStateMachine extends StateMachine { if (DBG) log(getName() + "\n"); EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName()); - final Message message = Message.obtain(getCurrentMessage()); - final WifiConfiguration config = (WifiConfiguration) message.obj; + final Message message = getCurrentMessage(); + if (message.what == CMD_START_AP) { + final WifiConfiguration config = (WifiConfiguration) message.obj; - // start hostapd on a seperate thread - new Thread(new Runnable() { - public void run() { - if (startSoftApWithConfig(config)) { - if (DBG) log("Soft AP start successful"); - sendMessage(CMD_START_AP_SUCCESS); - } else { - loge("Soft AP start failed"); - sendMessage(CMD_START_AP_FAILURE); - } + if (config == null) { + mWifiApConfigChannel.sendMessage(CMD_REQUEST_AP_CONFIG); + } else { + mWifiApConfigChannel.sendMessage(CMD_SET_AP_CONFIG, config); + startSoftApWithConfig(config); } - }).start(); + } else { + throw new RuntimeException("Illegal transition to SoftApStartingState: " + message); + } } @Override public boolean processMessage(Message message) { @@ -3252,10 +3331,19 @@ public class WifiStateMachine extends StateMachine { case CMD_SET_FREQUENCY_BAND: case CMD_START_PACKET_FILTERING: case CMD_STOP_PACKET_FILTERING: - case CMD_TETHER_INTERFACE: + case CMD_TETHER_STATE_CHANGE: case WifiP2pService.P2P_ENABLE_PENDING: deferMessage(message); break; + case WifiStateMachine.CMD_RESPONSE_AP_CONFIG: + WifiConfiguration config = (WifiConfiguration) message.obj; + if (config != null) { + startSoftApWithConfig(config); + } else { + loge("Softap config is null!"); + sendMessage(CMD_START_AP_FAILURE); + } + break; case CMD_START_AP_SUCCESS: setWifiApState(WIFI_AP_STATE_ENABLED); transitionTo(mSoftApStartedState); @@ -3285,7 +3373,8 @@ public class WifiStateMachine extends StateMachine { case CMD_STOP_AP: if (DBG) log("Stopping Soft AP"); setWifiApState(WIFI_AP_STATE_DISABLING); - stopTethering(); + + /* We have not tethered at this point, so we just shutdown soft Ap */ try { mNwService.stopAccessPoint(mInterfaceName); } catch(Exception e) { @@ -3301,10 +3390,10 @@ public class WifiStateMachine extends StateMachine { loge("Cannot start supplicant with a running soft AP"); setWifiState(WIFI_STATE_UNKNOWN); break; - case CMD_TETHER_INTERFACE: - ArrayList<String> available = (ArrayList<String>) message.obj; - if (startTethering(available)) { - transitionTo(mTetheredState); + case CMD_TETHER_STATE_CHANGE: + TetherStateChange stateChange = (TetherStateChange) message.obj; + if (startTethering(stateChange.available)) { + transitionTo(mTetheringState); } break; case WifiP2pService.P2P_ENABLE_PENDING: @@ -3364,6 +3453,58 @@ public class WifiStateMachine extends StateMachine { } } + class TetheringState extends State { + @Override + public void enter() { + if (DBG) log(getName() + "\n"); + EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName()); + + /* Send ourselves a delayed message to shut down if tethering fails to notify */ + sendMessageDelayed(obtainMessage(CMD_TETHER_NOTIFICATION_TIMED_OUT, + ++mTetherToken, 0), TETHER_NOTIFICATION_TIME_OUT_MSECS); + } + @Override + public boolean processMessage(Message message) { + if (DBG) log(getName() + message.toString() + "\n"); + switch(message.what) { + case CMD_TETHER_STATE_CHANGE: + TetherStateChange stateChange = (TetherStateChange) message.obj; + if (isWifiTethered(stateChange.active)) { + transitionTo(mTetheredState); + } + return HANDLED; + case CMD_TETHER_NOTIFICATION_TIMED_OUT: + if (message.arg1 == mTetherToken) { + loge("Failed to get tether update, shutdown soft access point"); + setWifiApEnabled(null, false); + } + break; + case CMD_LOAD_DRIVER: + case CMD_UNLOAD_DRIVER: + 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_SCAN_MODE: + case CMD_SET_SCAN_TYPE: + case CMD_SET_HIGH_PERF_MODE: + case CMD_SET_COUNTRY_CODE: + case CMD_SET_FREQUENCY_BAND: + case CMD_START_PACKET_FILTERING: + case CMD_STOP_PACKET_FILTERING: + case WifiP2pService.P2P_ENABLE_PENDING: + deferMessage(message); + break; + default: + return NOT_HANDLED; + } + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + return HANDLED; + } + } + class TetheredState extends State { @Override public void enter() { @@ -3374,13 +3515,89 @@ public class WifiStateMachine extends StateMachine { public boolean processMessage(Message message) { if (DBG) log(getName() + message.toString() + "\n"); switch(message.what) { - case CMD_TETHER_INTERFACE: - // Ignore any duplicate interface available notifications - // when in tethered state + case CMD_TETHER_STATE_CHANGE: + TetherStateChange stateChange = (TetherStateChange) message.obj; + if (!isWifiTethered(stateChange.active)) { + loge("Tethering reports wifi as untethered!, shut down soft Ap"); + setWifiApEnabled(null, false); + } return HANDLED; + case CMD_STOP_AP: + if (DBG) log("Untethering before stopping AP"); + setWifiApState(WIFI_AP_STATE_DISABLING); + stopTethering(); + transitionTo(mSoftApStoppingState); + break; + default: + return NOT_HANDLED; + } + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + return HANDLED; + } + } + + class SoftApStoppingState extends State { + @Override + public void enter() { + if (DBG) log(getName() + "\n"); + EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName()); + + /* Send ourselves a delayed message to shut down if tethering fails to notify */ + sendMessageDelayed(obtainMessage(CMD_TETHER_NOTIFICATION_TIMED_OUT, + ++mTetherToken, 0), TETHER_NOTIFICATION_TIME_OUT_MSECS); + + } + @Override + public boolean processMessage(Message message) { + if (DBG) log(getName() + message.toString() + "\n"); + switch(message.what) { + case CMD_TETHER_STATE_CHANGE: + TetherStateChange stateChange = (TetherStateChange) message.obj; + + /* Wait till wifi is untethered */ + if (isWifiTethered(stateChange.active)) break; + + try { + mNwService.stopAccessPoint(mInterfaceName); + } catch(Exception e) { + loge("Exception in stopAccessPoint()"); + } + transitionTo(mDriverLoadedState); + break; + case CMD_TETHER_NOTIFICATION_TIMED_OUT: + if (message.arg1 == mTetherToken) { + loge("Failed to get tether update, force stop access point"); + try { + mNwService.stopAccessPoint(mInterfaceName); + } catch(Exception e) { + loge("Exception in stopAccessPoint()"); + } + transitionTo(mDriverLoadedState); + } + break; + case CMD_LOAD_DRIVER: + case CMD_UNLOAD_DRIVER: + 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_SCAN_MODE: + case CMD_SET_SCAN_TYPE: + case CMD_SET_HIGH_PERF_MODE: + case CMD_SET_COUNTRY_CODE: + case CMD_SET_FREQUENCY_BAND: + case CMD_START_PACKET_FILTERING: + case CMD_STOP_PACKET_FILTERING: + case WifiP2pService.P2P_ENABLE_PENDING: + deferMessage(message); + break; default: return NOT_HANDLED; } + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + return HANDLED; } } diff --git a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java index 97697180..c58253d 100644 --- a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java +++ b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java @@ -37,6 +37,7 @@ import android.provider.Settings; import android.provider.Settings.Secure; import android.util.Log; +import com.android.internal.R; import com.android.internal.util.Protocol; import com.android.internal.util.State; import com.android.internal.util.StateMachine; @@ -68,7 +69,8 @@ public class WifiWatchdogStateMachine extends StateMachine { private static final boolean DBG = false; private static final String TAG = "WifiWatchdogStateMachine"; - private static final String WATCHDOG_NOTIFICATION_ID = "Android.System.WifiWatchdog"; + private static final String DISABLED_NETWORK_NOTIFICATION_ID = "WifiWatchdog.networkdisabled"; + private static final String WALLED_GARDEN_NOTIFICATION_ID = "WifiWatchdog.walledgarden"; private static final int WIFI_SIGNAL_LEVELS = 4; /** @@ -185,7 +187,8 @@ public class WifiWatchdogStateMachine extends StateMachine { */ public boolean mDisableAPNextFailure = false; private static boolean sWifiOnly = false; - private boolean mNotificationShown; + private boolean mDisabledNotificationShown; + private boolean mWalledGardenNotificationShown; public boolean mHasConnectedWifiManager = false; /** @@ -479,51 +482,76 @@ public class WifiWatchdogStateMachine extends StateMachine { mLastWalledGardenCheckTime = null; mNumCheckFailures = 0; mBssids.clear(); - cancelNetworkNotification(); + setDisabledNetworkNotificationVisible(false); + setWalledGardenNotificationVisible(false); } - private void popUpBrowser() { - Uri uri = Uri.parse("http://www.google.com"); - Intent intent = new Intent(Intent.ACTION_VIEW, uri); - intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | - Intent.FLAG_ACTIVITY_NEW_TASK); - mContext.startActivity(intent); - } + private void setWalledGardenNotificationVisible(boolean visible) { + // If it should be hidden and it is already hidden, then noop + if (!visible && !mWalledGardenNotificationShown) { + return; + } - private void displayDisabledNetworkNotification(String ssid) { Resources r = Resources.getSystem(); - CharSequence title = - r.getText(com.android.internal.R.string.wifi_watchdog_network_disabled); - String msg = ssid + - r.getText(com.android.internal.R.string.wifi_watchdog_network_disabled_detailed); - - Notification wifiDisabledWarning = new Notification.Builder(mContext) - .setSmallIcon(com.android.internal.R.drawable.stat_sys_warning) - .setDefaults(Notification.DEFAULT_ALL) - .setTicker(title) - .setContentTitle(title) - .setContentText(msg) - .setContentIntent(PendingIntent.getActivity(mContext, 0, - new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK) - .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK), 0)) - .setWhen(System.currentTimeMillis()) - .setAutoCancel(true) - .getNotification(); - NotificationManager notificationManager = (NotificationManager) mContext - .getSystemService(Context.NOTIFICATION_SERVICE); - - notificationManager.notify(WATCHDOG_NOTIFICATION_ID, 1, wifiDisabledWarning); - mNotificationShown = true; + .getSystemService(Context.NOTIFICATION_SERVICE); + + if (visible) { + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(mWalledGardenUrl)); + intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK); + + CharSequence title = r.getString(R.string.wifi_available_sign_in, 0); + CharSequence details = r.getString(R.string.wifi_available_sign_in_detailed, + mConnectionInfo.getSSID()); + + Notification notification = new Notification(); + notification.when = 0; + notification.icon = com.android.internal.R.drawable.stat_notify_wifi_in_range; + notification.flags = Notification.FLAG_AUTO_CANCEL; + notification.contentIntent = PendingIntent.getActivity(mContext, 0, intent, 0); + notification.tickerText = title; + notification.setLatestEventInfo(mContext, title, details, notification.contentIntent); + + notificationManager.notify(WALLED_GARDEN_NOTIFICATION_ID, 1, notification); + } else { + notificationManager.cancel(WALLED_GARDEN_NOTIFICATION_ID, 1); + } + mWalledGardenNotificationShown = visible; } - public void cancelNetworkNotification() { - if (mNotificationShown) { - NotificationManager notificationManager = (NotificationManager) mContext - .getSystemService(Context.NOTIFICATION_SERVICE); - notificationManager.cancel(WATCHDOG_NOTIFICATION_ID, 1); - mNotificationShown = false; + private void setDisabledNetworkNotificationVisible(boolean visible) { + // If it should be hidden and it is already hidden, then noop + if (!visible && !mDisabledNotificationShown) { + return; + } + + Resources r = Resources.getSystem(); + NotificationManager notificationManager = (NotificationManager) mContext + .getSystemService(Context.NOTIFICATION_SERVICE); + + if (visible) { + CharSequence title = r.getText(R.string.wifi_watchdog_network_disabled); + String msg = mConnectionInfo.getSSID() + + r.getText(R.string.wifi_watchdog_network_disabled_detailed); + + Notification wifiDisabledWarning = new Notification.Builder(mContext) + .setSmallIcon(R.drawable.stat_sys_warning) + .setDefaults(Notification.DEFAULT_ALL) + .setTicker(title) + .setContentTitle(title) + .setContentText(msg) + .setContentIntent(PendingIntent.getActivity(mContext, 0, + new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK) + .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK), 0)) + .setWhen(System.currentTimeMillis()) + .setAutoCancel(true) + .getNotification(); + + notificationManager.notify(DISABLED_NETWORK_NOTIFICATION_ID, 1, wifiDisabledWarning); + } else { + notificationManager.cancel(DISABLED_NETWORK_NOTIFICATION_ID, 1); } + mDisabledNotificationShown = visible; } class DefaultState extends State { @@ -578,9 +606,10 @@ public class WifiWatchdogStateMachine extends StateMachine { NetworkInfo networkInfo = (NetworkInfo) stateChangeIntent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO); + setDisabledNetworkNotificationVisible(false); + setWalledGardenNotificationVisible(false); switch (networkInfo.getState()) { case CONNECTED: - cancelNetworkNotification(); WifiInfo wifiInfo = (WifiInfo) stateChangeIntent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO); if (wifiInfo == null) { @@ -976,7 +1005,7 @@ public class WifiWatchdogStateMachine extends StateMachine { } mWifiManager.disableNetwork(networkId, WifiConfiguration.DISABLED_DNS_FAILURE); if (mShowDisabledNotification && mConnectionInfo.isExplicitConnect()) { - displayDisabledNetworkNotification(mConnectionInfo.getSSID()); + setDisabledNetworkNotificationVisible(true); } transitionTo(mNotConnectedState); } else { @@ -1009,7 +1038,7 @@ public class WifiWatchdogStateMachine extends StateMachine { } return HANDLED; } - popUpBrowser(); + setWalledGardenNotificationVisible(true); transitionTo(mOnlineWatchState); return HANDLED; } |