diff options
author | repo sync <isheriff@google.com> | 2011-06-24 14:23:07 -0700 |
---|---|---|
committer | Irfan Sheriff <isheriff@google.com> | 2011-07-24 10:08:47 -0700 |
commit | 55bc5f3e0408bcb5a39a6732de0b2d1aa99a55be (patch) | |
tree | 1206af1015ea8248a8fefbf2672d8df7bfa56bc8 /wifi/java | |
parent | 895de9269fa125bf3903c21faf5e8d2750bfb000 (diff) | |
download | frameworks_base-55bc5f3e0408bcb5a39a6732de0b2d1aa99a55be.zip frameworks_base-55bc5f3e0408bcb5a39a6732de0b2d1aa99a55be.tar.gz frameworks_base-55bc5f3e0408bcb5a39a6732de0b2d1aa99a55be.tar.bz2 |
Updated: Wi-Fi p2p framework
First stage. Get the bones in right now even though
we are not ready on the native side.
Once, we have things underneath working - we will further update the
framework
Change-Id: I4a7dab5cd4267373dc5f8989ae4122f91c384ed5
Diffstat (limited to 'wifi/java')
20 files changed, 2832 insertions, 222 deletions
diff --git a/wifi/java/android/net/wifi/StateChangeResult.java b/wifi/java/android/net/wifi/StateChangeResult.java new file mode 100644 index 0000000..8ab5982 --- /dev/null +++ b/wifi/java/android/net/wifi/StateChangeResult.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + package android.net.wifi; + +/** + * Stores supplicant state change information passed from WifiMonitor to + * a state machine. WifiStateMachine, SupplicantStateTracker and WpsStateMachine + * are example state machines that handle it. + * @hide + */ +public class StateChangeResult { + StateChangeResult(int networkId, String BSSID, SupplicantState state) { + this.state = state; + this.BSSID = BSSID; + this.networkId = networkId; + } + int networkId; + String BSSID; + SupplicantState state; +} diff --git a/wifi/java/android/net/wifi/SupplicantStateTracker.java b/wifi/java/android/net/wifi/SupplicantStateTracker.java index 0c4f9f6..9168e62 100644 --- a/wifi/java/android/net/wifi/SupplicantStateTracker.java +++ b/wifi/java/android/net/wifi/SupplicantStateTracker.java @@ -19,7 +19,7 @@ package android.net.wifi; import com.android.internal.util.State; import com.android.internal.util.StateMachine; -import android.net.wifi.WifiStateMachine.StateChangeResult; +import android.net.wifi.StateChangeResult; import android.content.Context; import android.content.Intent; import android.os.Handler; @@ -159,11 +159,11 @@ class SupplicantStateTracker extends StateMachine { public boolean processMessage(Message message) { if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); switch (message.what) { - case WifiStateMachine.AUTHENTICATION_FAILURE_EVENT: + case WifiMonitor.AUTHENTICATION_FAILURE_EVENT: mAuthenticationFailuresCount++; mAuthFailureInSupplicantBroadcast = true; break; - case WifiStateMachine.SUPPLICANT_STATE_CHANGE_EVENT: + case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT: StateChangeResult stateChangeResult = (StateChangeResult) message.obj; SupplicantState state = stateChangeResult.state; sendSupplicantStateChangedBroadcast(state, mAuthFailureInSupplicantBroadcast); @@ -251,7 +251,7 @@ class SupplicantStateTracker extends StateMachine { public boolean processMessage(Message message) { if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); switch (message.what) { - case WifiStateMachine.SUPPLICANT_STATE_CHANGE_EVENT: + case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT: StateChangeResult stateChangeResult = (StateChangeResult) message.obj; SupplicantState state = stateChangeResult.state; if (SupplicantState.isHandshakeState(state)) { @@ -293,7 +293,7 @@ class SupplicantStateTracker extends StateMachine { public boolean processMessage(Message message) { if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); switch(message.what) { - case WifiStateMachine.SUPPLICANT_STATE_CHANGE_EVENT: + case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT: StateChangeResult stateChangeResult = (StateChangeResult) message.obj; SupplicantState state = stateChangeResult.state; sendSupplicantStateChangedBroadcast(state, mAuthFailureInSupplicantBroadcast); diff --git a/wifi/java/android/net/wifi/WifiMonitor.java b/wifi/java/android/net/wifi/WifiMonitor.java index 4ec4cfc..2ccc8a2 100644 --- a/wifi/java/android/net/wifi/WifiMonitor.java +++ b/wifi/java/android/net/wifi/WifiMonitor.java @@ -16,15 +16,24 @@ package android.net.wifi; -import android.util.Log; import android.net.NetworkInfo; +import android.net.wifi.p2p.WifiP2pConfig; +import android.net.wifi.p2p.WifiP2pDevice; +import android.net.wifi.p2p.WifiP2pGroup; +import android.net.wifi.StateChangeResult; +import android.os.Message; +import android.util.Log; + + +import com.android.internal.util.Protocol; +import com.android.internal.util.StateMachine; import java.util.regex.Pattern; import java.util.regex.Matcher; /** * Listens for events from the wpa_supplicant server, and passes them on - * to the {@link WifiStateMachine} for handling. Runs in its own thread. + * to the {@link StateMachine} for handling. Runs in its own thread. * * @hide */ @@ -45,16 +54,16 @@ public class WifiMonitor { private static final int UNKNOWN = 9; /** All events coming from the supplicant start with this prefix */ - private static final String eventPrefix = "CTRL-EVENT-"; - private static final int eventPrefixLen = eventPrefix.length(); + private static final String EVENT_PREFIX_STR = "CTRL-EVENT-"; + private static final int EVENT_PREFIX_LEN_STR = EVENT_PREFIX_STR.length(); /** All WPA events coming from the supplicant start with this prefix */ - private static final String wpaEventPrefix = "WPA:"; - private static final String passwordKeyMayBeIncorrectEvent = + private static final String WPA_EVENT_PREFIX_STR = "WPA:"; + private static final String PASSWORD_MAY_BE_INCORRECT_STR = "pre-shared key may be incorrect"; /* WPS events */ - private static final String wpsOverlapEvent = "WPS-OVERLAP-DETECTED"; + private static final String WPS_OVERLAP_STR = "WPS-OVERLAP-DETECTED"; /** * Names of events from wpa_supplicant (minus the prefix). In the @@ -68,26 +77,26 @@ public class WifiMonitor { * </pre> * <code>xx:xx:xx:xx:xx:xx</code> is the BSSID of the associated access point */ - private static final String connectedEvent = "CONNECTED"; + private static final String CONNECTED_STR = "CONNECTED"; /** * <pre> * CTRL-EVENT-DISCONNECTED - Disconnect event - remove keys * </pre> */ - private static final String disconnectedEvent = "DISCONNECTED"; + private static final String DISCONNECTED_STR = "DISCONNECTED"; /** * <pre> * CTRL-EVENT-STATE-CHANGE x * </pre> * <code>x</code> is the numerical value of the new state. */ - private static final String stateChangeEvent = "STATE-CHANGE"; + private static final String STATE_CHANGE_STR = "STATE-CHANGE"; /** * <pre> * CTRL-EVENT-SCAN-RESULTS ready * </pre> */ - private static final String scanResultsEvent = "SCAN-RESULTS"; + private static final String SCAN_RESULTS_STR = "SCAN-RESULTS"; /** * <pre> @@ -95,32 +104,32 @@ public class WifiMonitor { * </pre> * {@code x} is the link speed in Mb/sec. */ - private static final String linkSpeedEvent = "LINK-SPEED"; + private static final String LINK_SPEED_STR = "LINK-SPEED"; /** * <pre> * CTRL-EVENT-TERMINATING - signal x * </pre> * <code>x</code> is the signal that caused termination. */ - private static final String terminatingEvent = "TERMINATING"; + private static final String TERMINATING_STR = "TERMINATING"; /** * <pre> * CTRL-EVENT-DRIVER-STATE state * </pre> * <code>state</code> can be HANGED */ - private static final String driverStateEvent = "DRIVER-STATE"; + private static final String DRIVER_STATE_STR = "DRIVER-STATE"; /** * <pre> * CTRL-EVENT-EAP-FAILURE EAP authentication failed * </pre> */ - private static final String eapFailureEvent = "EAP-FAILURE"; + private static final String EAP_FAILURE_STR = "EAP-FAILURE"; /** * This indicates an authentication failure on EAP FAILURE event */ - private static final String eapAuthFailure = "EAP authentication failed"; + private static final String EAP_AUTH_FAILURE_STR = "EAP authentication failed"; /** * Regex pattern for extracting an Ethernet-style MAC address from a string. @@ -130,17 +139,116 @@ public class WifiMonitor { private static Pattern mConnectedEventPattern = Pattern.compile("((?:[0-9a-f]{2}:){5}[0-9a-f]{2}) .* \\[id=([0-9]+) "); - private final WifiStateMachine mWifiStateMachine; + /** P2P events */ + private static final String P2P_EVENT_PREFIX_STR = "P2P"; + + /* P2P-DEVICE-FOUND fa:7b:7a:42:02:13 p2p_dev_addr=fa:7b:7a:42:02:13 pri_dev_type=1-0050F204-1 + name='p2p-TEST1' config_methods=0x188 dev_capab=0x27 group_capab=0x0 */ + private static final String P2P_DEVICE_FOUND_STR = "P2P-DEVICE-FOUND"; + + /* P2P-DEVICE-LOST p2p_dev_addr=42:fc:89:e1:e2:27 */ + private static final String P2P_DEVICE_LOST_STR = "P2P-DEVICE-LOST"; + + /* P2P-GO-NEG-REQUEST 42:fc:89:a8:96:09 dev_passwd_id=4 */ + private static final String P2P_GO_NEG_REQUEST_STR = "P2P-GO-NEG-REQUEST"; + + private static final String P2P_GO_NEG_SUCCESS_STR = "P2P-GO-NEG-SUCCESS"; + + private static final String P2P_GO_NEG_FAILURE_STR = "P2P-GO-NEG-FAILURE"; + + private static final String P2P_GROUP_FORMATION_SUCCESS_STR = + "P2P-GROUP-FORMATION-SUCCESS"; + + private static final String P2P_GROUP_FORMATION_FAILURE_STR = + "P2P-GROUP-FORMATION-FAILURE"; + + /* P2P-GROUP-STARTED p2p-wlan0-0 [client|GO] ssid="DIRECT-W8" freq=2437 + [psk=2182b2e50e53f260d04f3c7b25ef33c965a3291b9b36b455a82d77fd82ca15bc|passphrase="fKG4jMe3"] + go_dev_addr=fa:7b:7a:42:02:13 */ + private static final String P2P_GROUP_STARTED_STR = "P2P-GROUP-STARTED"; + + /* P2P-GROUP-REMOVED p2p-wlan0-0 [client|GO] reason=REQUESTED */ + private static final String P2P_GROUP_REMOVED_STR = "P2P-GROUP-REMOVED"; + + /* P2P-INVITATION-RECEIVED sa=fa:7b:7a:42:02:13 go_dev_addr=f8:7b:7a:42:02:13 + bssid=fa:7b:7a:42:82:13 unknown-network */ + private static final String P2P_INVITATION_RECEIVED_STR = "P2P-INVITATION-RECEIVED"; + + /* P2P-INVITATION-RESULT status=1 */ + private static final String P2P_INVITATION_RESULT_STR = "P2P-INVITATION-RESULT"; + + /* P2P-PROV-DISC-PBC-REQ 42:fc:89:e1:e2:27 p2p_dev_addr=42:fc:89:e1:e2:27 + pri_dev_type=1-0050F204-1 name='p2p-TEST2' config_methods=0x188 dev_capab=0x27 + group_capab=0x0 */ + private static final String P2P_PROV_DISC_PBC_REQ_STR = "P2P-PROV-DISC-PBC-REQ"; + /* P2P-PROV-DISC-ENTER-PIN 42:fc:89:e1:e2:27 p2p_dev_addr=42:fc:89:e1:e2:27 + pri_dev_type=1-0050F204-1 name='p2p-TEST2' config_methods=0x188 dev_capab=0x27 + group_capab=0x0 */ + private static final String P2P_PROV_DISC_ENTER_PIN_STR = "P2P-PROV-DISC-ENTER-PIN"; + /* P2P-PROV-DISC-SHOW-PIN 42:fc:89:e1:e2:27 44490607 p2p_dev_addr=42:fc:89:e1:e2:27 + pri_dev_type=1-0050F204-1 name='p2p-TEST2' config_methods=0x188 dev_capab=0x27 + group_capab=0x0 */ + private static final String P2P_PROV_DISC_SHOW_PIN_STR = "P2P-PROV-DISC-SHOW-PIN"; + + private static final String HOST_AP_EVENT_PREFIX_STR = "AP"; + /* AP-STA-CONNECTED 42:fc:89:a8:96:09 */ + private static final String AP_STA_CONNECTED_STR = "AP-STA-CONNECTED"; + /* AP-STA-DISCONNECTED 42:fc:89:a8:96:09 */ + private static final String AP_STA_DISCONNECTED_STR = "AP-STA-DISCONNECTED"; + + private final StateMachine mStateMachine; + + /* Supplicant events reported to a state machine */ + private static final int BASE = Protocol.BASE_WIFI_MONITOR; + + /* Connection to supplicant established */ + public static final int SUP_CONNECTION_EVENT = BASE + 1; + /* Connection to supplicant lost */ + public static final int SUP_DISCONNECTION_EVENT = BASE + 2; + /* Network connection completed */ + public static final int NETWORK_CONNECTION_EVENT = BASE + 3; + /* Network disconnection completed */ + public static final int NETWORK_DISCONNECTION_EVENT = BASE + 4; + /* Scan results are available */ + public static final int SCAN_RESULTS_EVENT = BASE + 5; + /* Supplicate state changed */ + public static final int SUPPLICANT_STATE_CHANGE_EVENT = BASE + 6; + /* Password failure and EAP authentication failure */ + public static final int AUTHENTICATION_FAILURE_EVENT = BASE + 7; + /* WPS overlap detected */ + public static final int WPS_OVERLAP_EVENT = BASE + 8; + /* Driver was hung */ + public static final int DRIVER_HUNG_EVENT = BASE + 9; + + /* P2P events */ + public static final int P2P_DEVICE_FOUND_EVENT = BASE + 21; + public static final int P2P_DEVICE_LOST_EVENT = BASE + 22; + public static final int P2P_GO_NEGOTIATION_REQUEST_EVENT = BASE + 23; + public static final int P2P_GO_NEGOTIATION_SUCCESS_EVENT = BASE + 25; + public static final int P2P_GO_NEGOTIATION_FAILURE_EVENT = BASE + 26; + public static final int P2P_GROUP_FORMATION_SUCCESS_EVENT = BASE + 27; + public static final int P2P_GROUP_FORMATION_FAILURE_EVENT = BASE + 28; + public static final int P2P_GROUP_STARTED_EVENT = BASE + 29; + public static final int P2P_GROUP_REMOVED_EVENT = BASE + 30; + public static final int P2P_INVITATION_RECEIVED_EVENT = BASE + 31; + public static final int P2P_INVITATION_RESULT_EVENT = BASE + 32; + public static final int P2P_PROV_DISC_PBC_REQ_EVENT = BASE + 33; + public static final int P2P_PROV_DISC_ENTER_PIN_EVENT = BASE + 34; + public static final int P2P_PROV_DISC_SHOW_PIN_EVENT = BASE + 35; + + /* hostap events */ + public static final int AP_STA_DISCONNECTED_EVENT = BASE + 41; + public static final int AP_STA_CONNECTED_EVENT = BASE + 42; /** * This indicates the supplicant connection for the monitor is closed */ - private static final String monitorSocketClosed = "connection closed"; + private static final String MONITOR_SOCKET_CLOSED_STR = "connection closed"; /** * This indicates a read error on the monitor socket conenction */ - private static final String wpaRecvError = "recv error"; + private static final String WPA_RECV_ERROR_STR = "recv error"; /** * Tracks consecutive receive errors @@ -152,8 +260,8 @@ public class WifiMonitor { */ private static final int MAX_RECV_ERRORS = 10; - public WifiMonitor(WifiStateMachine wifiStateMachine) { - mWifiStateMachine = wifiStateMachine; + public WifiMonitor(StateMachine wifiStateMachine) { + mStateMachine = wifiStateMachine; } public void startMonitoring() { @@ -170,9 +278,9 @@ public class WifiMonitor { if (connectToSupplicant()) { // Send a message indicating that it is now possible to send commands // to the supplicant - mWifiStateMachine.notifySupplicantConnection(); + mStateMachine.sendMessage(SUP_CONNECTION_EVENT); } else { - mWifiStateMachine.notifySupplicantLost(); + mStateMachine.sendMessage(SUP_DISCONNECTION_EVENT); return; } @@ -181,20 +289,24 @@ public class WifiMonitor { String eventStr = WifiNative.waitForEvent(); // Skip logging the common but mostly uninteresting scan-results event - if (false && eventStr.indexOf(scanResultsEvent) == -1) { - Log.v(TAG, "Event [" + eventStr + "]"); + if (false && eventStr.indexOf(SCAN_RESULTS_STR) == -1) { + Log.d(TAG, "Event [" + eventStr + "]"); } - if (!eventStr.startsWith(eventPrefix)) { - if (eventStr.startsWith(wpaEventPrefix) && - 0 < eventStr.indexOf(passwordKeyMayBeIncorrectEvent)) { - mWifiStateMachine.notifyAuthenticationFailure(); - } else if (eventStr.startsWith(wpsOverlapEvent)) { - mWifiStateMachine.notifyWpsOverlap(); + if (!eventStr.startsWith(EVENT_PREFIX_STR)) { + if (eventStr.startsWith(WPA_EVENT_PREFIX_STR) && + 0 < eventStr.indexOf(PASSWORD_MAY_BE_INCORRECT_STR)) { + mStateMachine.sendMessage(AUTHENTICATION_FAILURE_EVENT); + } else if (eventStr.startsWith(WPS_OVERLAP_STR)) { + mStateMachine.sendMessage(WPS_OVERLAP_EVENT); + } else if (eventStr.startsWith(P2P_EVENT_PREFIX_STR)) { + handleP2pEvents(eventStr); + } else if (eventStr.startsWith(HOST_AP_EVENT_PREFIX_STR)) { + handleHostApEvents(eventStr); } continue; } - String eventName = eventStr.substring(eventPrefixLen); + String eventName = eventStr.substring(EVENT_PREFIX_LEN_STR); int nameEnd = eventName.indexOf(' '); if (nameEnd != -1) eventName = eventName.substring(0, nameEnd); @@ -206,21 +318,21 @@ public class WifiMonitor { * Map event name into event enum */ int event; - if (eventName.equals(connectedEvent)) + if (eventName.equals(CONNECTED_STR)) event = CONNECTED; - else if (eventName.equals(disconnectedEvent)) + else if (eventName.equals(DISCONNECTED_STR)) event = DISCONNECTED; - else if (eventName.equals(stateChangeEvent)) + else if (eventName.equals(STATE_CHANGE_STR)) event = STATE_CHANGE; - else if (eventName.equals(scanResultsEvent)) + else if (eventName.equals(SCAN_RESULTS_STR)) event = SCAN_RESULTS; - else if (eventName.equals(linkSpeedEvent)) + else if (eventName.equals(LINK_SPEED_STR)) event = LINK_SPEED; - else if (eventName.equals(terminatingEvent)) + else if (eventName.equals(TERMINATING_STR)) event = TERMINATING; - else if (eventName.equals(driverStateEvent)) + else if (eventName.equals(DRIVER_STATE_STR)) event = DRIVER_STATE; - else if (eventName.equals(eapFailureEvent)) + else if (eventName.equals(EAP_FAILURE_STR)) event = EAP_FAILURE; else event = UNKNOWN; @@ -249,7 +361,7 @@ public class WifiMonitor { * If monitor socket is closed, we have already * stopped the supplicant, simply exit the monitor thread */ - if (eventData.startsWith(monitorSocketClosed)) { + if (eventData.startsWith(MONITOR_SOCKET_CLOSED_STR)) { if (false) { Log.d(TAG, "Monitor socket is closed, exiting thread"); } @@ -260,7 +372,7 @@ public class WifiMonitor { * Close the supplicant connection if we see * too many recv errors */ - if (eventData.startsWith(wpaRecvError)) { + if (eventData.startsWith(WPA_RECV_ERROR_STR)) { if (++mRecvErrors > MAX_RECV_ERRORS) { if (false) { Log.d(TAG, "too many recv errors, closing connection"); @@ -271,11 +383,11 @@ public class WifiMonitor { } // notify and exit - mWifiStateMachine.notifySupplicantLost(); + mStateMachine.sendMessage(SUP_DISCONNECTION_EVENT); break; } else if (event == EAP_FAILURE) { - if (eventData.startsWith(eapAuthFailure)) { - mWifiStateMachine.notifyAuthenticationFailure(); + if (eventData.startsWith(EAP_AUTH_FAILURE_STR)) { + mStateMachine.sendMessage(AUTHENTICATION_FAILURE_EVENT); } } else { handleEvent(event, eventData); @@ -305,7 +417,7 @@ public class WifiMonitor { return; } if (state.equals("HANGED")) { - mWifiStateMachine.notifyDriverHung(); + mStateMachine.sendMessage(DRIVER_HUNG_EVENT); } } @@ -326,7 +438,7 @@ public class WifiMonitor { break; case SCAN_RESULTS: - mWifiStateMachine.notifyScanResultsAvailable(); + mStateMachine.sendMessage(SCAN_RESULTS_EVENT); break; case UNKNOWN: @@ -335,6 +447,59 @@ public class WifiMonitor { } /** + * Handle p2p events + */ + private void handleP2pEvents(String dataString) { + if (dataString.startsWith(P2P_DEVICE_FOUND_STR)) { + mStateMachine.sendMessage(P2P_DEVICE_FOUND_EVENT, new WifiP2pDevice(dataString)); + } else if (dataString.startsWith(P2P_DEVICE_LOST_STR)) { + mStateMachine.sendMessage(P2P_DEVICE_LOST_EVENT, new WifiP2pDevice(dataString)); + } else if (dataString.startsWith(P2P_GO_NEG_REQUEST_STR)) { + mStateMachine.sendMessage(P2P_GO_NEGOTIATION_REQUEST_EVENT, + new WifiP2pConfig(dataString)); + } else if (dataString.startsWith(P2P_GO_NEG_SUCCESS_STR)) { + mStateMachine.sendMessage(P2P_GO_NEGOTIATION_SUCCESS_EVENT); + } else if (dataString.startsWith(P2P_GO_NEG_FAILURE_STR)) { + mStateMachine.sendMessage(P2P_GO_NEGOTIATION_FAILURE_EVENT); + } else if (dataString.startsWith(P2P_GROUP_FORMATION_SUCCESS_STR)) { + mStateMachine.sendMessage(P2P_GROUP_FORMATION_SUCCESS_EVENT); + } else if (dataString.startsWith(P2P_GROUP_FORMATION_FAILURE_STR)) { + mStateMachine.sendMessage(P2P_GROUP_FORMATION_FAILURE_EVENT); + } else if (dataString.startsWith(P2P_GROUP_STARTED_STR)) { + mStateMachine.sendMessage(P2P_GROUP_STARTED_EVENT, new WifiP2pGroup(dataString)); + } else if (dataString.startsWith(P2P_GROUP_REMOVED_STR)) { + mStateMachine.sendMessage(P2P_GROUP_REMOVED_EVENT, new WifiP2pGroup(dataString)); + } else if (dataString.startsWith(P2P_INVITATION_RECEIVED_STR)) { + mStateMachine.sendMessage(P2P_INVITATION_RECEIVED_EVENT, + new WifiP2pGroup(dataString)); + } else if (dataString.startsWith(P2P_INVITATION_RESULT_STR)) { + String[] tokens = dataString.split(" "); + if (tokens.length != 2) return; + String[] nameValue = tokens[1].split("="); + if (nameValue.length != 2) return; + mStateMachine.sendMessage(P2P_INVITATION_RESULT_EVENT, nameValue[1]); + } else if (dataString.startsWith(P2P_PROV_DISC_PBC_REQ_STR)) { + mStateMachine.sendMessage(P2P_PROV_DISC_PBC_REQ_EVENT, + new WifiP2pDevice(dataString)); + } else if (dataString.startsWith(P2P_PROV_DISC_ENTER_PIN_STR)) { + mStateMachine.sendMessage(P2P_PROV_DISC_ENTER_PIN_EVENT, + new WifiP2pDevice(dataString)); + } + } + + /** + * Handle hostap events + */ + private void handleHostApEvents(String dataString) { + String[] tokens = dataString.split(" "); + if (tokens[0].equals(AP_STA_CONNECTED_STR)) { + mStateMachine.sendMessage(AP_STA_CONNECTED_EVENT, tokens[1]); + } else if (tokens[0].equals(AP_STA_DISCONNECTED_STR)) { + mStateMachine.sendMessage(AP_STA_DISCONNECTED_EVENT, tokens[1]); + } + } + + /** * Handle the supplicant STATE-CHANGE event * @param dataString New supplicant state string in the format: * id=network-id state=new-state @@ -383,7 +548,7 @@ public class WifiMonitor { if (newSupplicantState == SupplicantState.INVALID) { Log.w(TAG, "Invalid supplicant state: " + newState); } - mWifiStateMachine.notifySupplicantStateChange(networkId, BSSID, newSupplicantState); + notifySupplicantStateChange(networkId, BSSID, newSupplicantState); } } @@ -403,7 +568,40 @@ public class WifiMonitor { } } } - mWifiStateMachine.notifyNetworkStateChange(newState, BSSID, networkId); + notifyNetworkStateChange(newState, BSSID, networkId); + } + + /** + * Send the state machine a notification that the state of Wifi connectivity + * has changed. + * @param networkId the configured network on which the state change occurred + * @param newState the new network state + * @param BSSID when the new state is {@link DetailedState#CONNECTED + * NetworkInfo.DetailedState.CONNECTED}, + * this is the MAC address of the access point. Otherwise, it + * is {@code null}. + */ + void notifyNetworkStateChange(NetworkInfo.DetailedState newState, String BSSID, int netId) { + if (newState == NetworkInfo.DetailedState.CONNECTED) { + Message m = mStateMachine.obtainMessage(NETWORK_CONNECTION_EVENT, + netId, 0, BSSID); + mStateMachine.sendMessage(m); + } else { + Message m = mStateMachine.obtainMessage(NETWORK_DISCONNECTION_EVENT, + netId, 0, BSSID); + mStateMachine.sendMessage(m); + } + } + + /** + * Send the state machine a notification that the state of the supplicant + * has changed. + * @param networkId the configured network on which the state change occurred + * @param newState the new {@code SupplicantState} + */ + void notifySupplicantStateChange(int networkId, String BSSID, SupplicantState newState) { + mStateMachine.sendMessage(mStateMachine.obtainMessage(SUPPLICANT_STATE_CHANGE_EVENT, + new StateChangeResult(networkId, BSSID, newState))); } /** diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java index f1f0fcc..3b043b3 100644 --- a/wifi/java/android/net/wifi/WifiNative.java +++ b/wifi/java/android/net/wifi/WifiNative.java @@ -16,6 +16,16 @@ package android.net.wifi; +import android.net.wifi.p2p.WifiP2pConfig; +import android.net.wifi.p2p.WifiP2pGroup; +import android.net.wifi.p2p.WifiP2pDevice; +import android.util.Log; + +import java.io.InputStream; +import java.lang.Process; +import java.util.ArrayList; +import java.util.List; + /** * Native calls for sending requests to the supplicant daemon, and for * receiving asynchronous events. All methods of the form "xxxxCommand()" @@ -28,6 +38,10 @@ package android.net.wifi; * WifiStateTracker class except for waitForEvent() call which is * on a separate monitor channel for WifiMonitor * + * TODO: clean up the API and move the functionality from JNI to here. We should + * be able to get everything done with doBooleanCommand, doIntCommand and + * doStringCommand native commands + * * {@hide} */ public class WifiNative { @@ -186,4 +200,141 @@ public class WifiNative { public native static void enableBackgroundScanCommand(boolean enable); public native static void setScanIntervalCommand(int scanInterval); + + private native static boolean doBooleanCommand(String command); + + //STOPSHIP: remove this after native interface works and replace all + //calls to doBooleanTempCommand() with doBooleanCommand() + private static boolean doBooleanTempCommand(String command) { + try { + String str = "/system/bin/wpa_cli " + command; + Log.e("WifiNative", "===> " + str); + Runtime.getRuntime() + .exec(str).waitFor(); + } catch (Exception e) { + Log.e("WifiNative", "exception with doBooleanTempCommand"); + return false; + } + return true; + } + + private static String doStringTempCommand(String command) { + String lines[] = null; + try { + String str = "/system/bin/wpa_cli " + command; + Log.e("WifiNative", "===> " + str); + Process p = Runtime.getRuntime() + .exec(str); + InputStream in = p.getInputStream(); + p.waitFor(); + byte[] bytes=new byte[in.available()]; + in.read(bytes); + String s = new String(bytes); + Log.e("WifiNative", "====> doString: " + s); + lines = s.split("\\r?\\n"); + } catch (Exception e) { + Log.e("WifiNative", "exception with doBooleanTempCommand"); + return null; + } + return lines[1]; + } + + private native static int doIntCommand(String command); + + private native static String doStringCommand(String command); + + public static boolean p2pFind() { + return doBooleanTempCommand("p2p_find"); + } + + public static boolean p2pFind(int timeout) { + if (timeout <= 0) { + return p2pFind(); + } + return doBooleanTempCommand("p2p_find " + timeout); + } + + public static boolean p2pListen() { + return doBooleanTempCommand("p2p_listen"); + } + + public static boolean p2pListen(int timeout) { + if (timeout <= 0) { + return p2pListen(); + } + return doBooleanTempCommand("p2p_listen " + timeout); + } + + public static boolean p2pFlush() { + return doBooleanTempCommand("p2p_flush"); + } + + /* p2p_connect <peer device address> <pbc|pin|PIN#> [label|display|keypad] + [persistent] [join|auth] [go_intent=<0..15>] [freq=<in MHz>] */ + public static String p2pConnect(WifiP2pConfig config) { + if (config == null) return null; + List<String> args = new ArrayList<String>(); + WpsConfiguration wpsConfig = config.wpsConfig; + args.add(config.deviceAddress); + + switch (wpsConfig.setup) { + case PBC: + args.add("pbc"); + break; + case DISPLAY: + //TODO: pass the pin back for display + args.add("pin"); + args.add("display"); + break; + case KEYPAD: + args.add(wpsConfig.pin); + args.add("keypad"); + break; + case LABEL: + args.add(wpsConfig.pin); + args.add("label"); + default: + break; + } + + if (config.isPersistent) args.add("persistent"); + if (config.joinExistingGroup) args.add("join"); + + args.add("go_intent=" + config.groupOwnerIntent); + if (config.channel > 0) args.add("freq=" + config.channel); + + String command = "p2p_connect "; + for (String s : args) command += s + " "; + + return doStringTempCommand(command); + } + + public static boolean p2pGroupAdd() { + return doBooleanTempCommand("p2p_group_add"); + } + + public static boolean p2pGroupRemove(String iface) { + if (iface == null) return false; + return doBooleanTempCommand("p2p_group_remove " + iface); + } + + public static boolean p2pReject(String deviceAddress) { + return doBooleanTempCommand("p2p_reject " + deviceAddress); + } + + /* Invite a peer to a group */ + public static boolean p2pInvite(WifiP2pGroup group, String deviceAddress) { + if (group == null || deviceAddress == null) return false; + return doBooleanTempCommand("p2p_invite group=" + group.getInterface() + + " peer=" + deviceAddress + " go_dev_addr=" + group.getOwner().deviceAddress); + } + + public static boolean p2pWpsPbc() { + return doBooleanTempCommand("wps_pbc"); + } + + public static boolean p2pWpsPin(String pin) { + return doBooleanTempCommand("wps_pin any " + pin); + } + } diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java index 1e3afbd..54685a6 100644 --- a/wifi/java/android/net/wifi/WifiStateMachine.java +++ b/wifi/java/android/net/wifi/WifiStateMachine.java @@ -56,6 +56,8 @@ import android.net.NetworkInfo; import android.net.NetworkInfo.DetailedState; import android.net.NetworkUtils; import android.net.wifi.WpsResult.Status; +import android.net.wifi.p2p.WifiP2pManager; +import android.net.wifi.StateChangeResult; import android.os.Binder; import android.os.IBinder; import android.os.INetworkManagementService; @@ -67,6 +69,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemProperties; import android.os.WorkSource; +import android.server.WifiP2pService; import android.provider.Settings; import android.util.EventLog; import android.util.Log; @@ -89,6 +92,14 @@ import java.util.regex.Pattern; * Track the state of Wifi connectivity. All event handling is done here, * and all changes in connectivity state are initiated here. * + * Wi-Fi now supports three modes of operation: Client, Soft Ap and Direct + * In the current implementation, we do not support any concurrency and thus only + * one of Client, Soft Ap or Direct operation is supported at any time. + * + * The WifiStateMachine supports Soft Ap and Client operations while WifiP2pService + * handles Direct. WifiP2pService and WifiStateMachine co-ordinate to ensure only + * one exists at a certain time. + * * @hide */ public class WifiStateMachine extends StateMachine { @@ -167,6 +178,10 @@ public class WifiStateMachine extends StateMachine { // Channel for sending replies. private AsyncChannel mReplyChannel = new AsyncChannel(); + private WifiP2pManager mWifiP2pManager; + //Used to initiate a connection with WifiP2pService + private AsyncChannel mWifiP2pChannel = new AsyncChannel(); + // Event log tags (must be in sync with event-log-tags) private static final int EVENTLOG_WIFI_STATE_CHANGED = 50021; private static final int EVENTLOG_WIFI_EVENT_HANDLED = 50022; @@ -213,25 +228,6 @@ public class WifiStateMachine extends StateMachine { static final int CMD_BLUETOOTH_ADAPTER_STATE_CHANGE = BASE + 26; - /* Supplicant events */ - /* Connection to supplicant established */ - static final int SUP_CONNECTION_EVENT = BASE + 31; - /* Connection to supplicant lost */ - static final int SUP_DISCONNECTION_EVENT = BASE + 32; - /* Network connection completed */ - static final int NETWORK_CONNECTION_EVENT = BASE + 33; - /* Network disconnection completed */ - static final int NETWORK_DISCONNECTION_EVENT = BASE + 34; - /* Scan results are available */ - static final int SCAN_RESULTS_EVENT = BASE + 35; - /* Supplicate state changed */ - static final int SUPPLICANT_STATE_CHANGE_EVENT = BASE + 36; - /* Password failure and EAP authentication failure */ - static final int AUTHENTICATION_FAILURE_EVENT = BASE + 37; - /* WPS overlap detected */ - static final int WPS_OVERLAP_EVENT = BASE + 38; - - /* Supplicant commands */ /* Is supplicant alive ? */ static final int CMD_PING_SUPPLICANT = BASE + 51; @@ -332,6 +328,10 @@ public class WifiStateMachine extends StateMachine { /* Reset the WPS state machine */ static final int CMD_RESET_WPS_STATE = BASE + 122; + /* Interaction with WifiP2pService */ + public static final int WIFI_ENABLE_PENDING = BASE + 131; + public static final int P2P_ENABLE_PROCEED = BASE + 132; + private static final int CONNECT_MODE = 1; private static final int SCAN_ONLY_MODE = 2; @@ -428,6 +428,9 @@ public class WifiStateMachine extends StateMachine { /* Soft ap is running and we are tethered through connectivity service */ private State mTetheredState = new TetheredState(); + /* Wait till p2p is disabled */ + private State mWaitForP2pDisableState = new WaitForP2pDisableState(); + /** * One of {@link WifiManager#WIFI_STATE_DISABLED}, @@ -560,6 +563,7 @@ public class WifiStateMachine extends StateMachine { addState(mSupplicantStoppingState, mDefaultState); addState(mSoftApStartedState, mDefaultState); addState(mTetheredState, mSoftApStartedState); + addState(mWaitForP2pDisableState, mDefaultState); setInitialState(mInitialState); @@ -1661,105 +1665,6 @@ public class WifiStateMachine extends StateMachine { return true; } - - /********************************************************* - * Notifications from WifiMonitor - ********************************************************/ - - /** - * Stores supplicant state change information passed from WifiMonitor - */ - static class StateChangeResult { - StateChangeResult(int networkId, String BSSID, SupplicantState state) { - this.state = state; - this.BSSID = BSSID; - this.networkId = networkId; - } - int networkId; - String BSSID; - SupplicantState state; - } - - /** - * Send the tracker a notification that a user provided - * configuration caused authentication failure - this could - * be a password failure or a EAP authentication failure - */ - void notifyAuthenticationFailure() { - sendMessage(AUTHENTICATION_FAILURE_EVENT); - } - - /** - * Send a notification that the supplicant has detected overlapped - * WPS sessions - */ - void notifyWpsOverlap() { - sendMessage(WPS_OVERLAP_EVENT); - } - - /** - * Send the tracker a notification that a connection to the supplicant - * daemon has been established. - */ - void notifySupplicantConnection() { - sendMessage(SUP_CONNECTION_EVENT); - } - - /** - * Send the tracker a notification that connection to the supplicant - * daemon is lost - */ - void notifySupplicantLost() { - sendMessage(SUP_DISCONNECTION_EVENT); - } - - /** - * Send the tracker a notification that the state of Wifi connectivity - * has changed. - * @param networkId the configured network on which the state change occurred - * @param newState the new network state - * @param BSSID when the new state is {@link DetailedState#CONNECTED - * NetworkInfo.DetailedState.CONNECTED}, - * this is the MAC address of the access point. Otherwise, it - * is {@code null}. - */ - void notifyNetworkStateChange(DetailedState newState, String BSSID, int networkId) { - if (newState == NetworkInfo.DetailedState.CONNECTED) { - sendMessage(obtainMessage(NETWORK_CONNECTION_EVENT, networkId, 0, BSSID)); - } else { - sendMessage(obtainMessage(NETWORK_DISCONNECTION_EVENT, networkId, 0, BSSID)); - } - } - - /** - * Send the tracker a notification that the state of the supplicant - * has changed. - * @param networkId the configured network on which the state change occurred - * @param newState the new {@code SupplicantState} - */ - void notifySupplicantStateChange(int networkId, String BSSID, SupplicantState newState) { - sendMessage(obtainMessage(SUPPLICANT_STATE_CHANGE_EVENT, - new StateChangeResult(networkId, BSSID, newState))); - } - - /** - * Send the tracker a notification that a scan has completed, and results - * are available. - */ - void notifyScanResultsAvailable() { - /** - * Switch scan mode over to passive. - * Turning off scan-only mode happens only in "Connect" mode - */ - setScanType(false); - sendMessage(SCAN_RESULTS_EVENT); - } - - void notifyDriverHung() { - setWifiEnabled(false); - setWifiEnabled(true); - } - /******************************************************** * HSM states *******************************************************/ @@ -1769,6 +1674,18 @@ public class WifiStateMachine extends StateMachine { public boolean processMessage(Message message) { if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); switch (message.what) { + case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: + if (message.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { + mWifiP2pChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION); + } else { + Log.e(TAG, "WifiP2pService connection failure, error=" + message.arg1); + } + break; + case AsyncChannel.CMD_CHANNEL_DISCONNECTED: + Log.e(TAG, "WifiP2pService channel lost, message.arg1 =" + message.arg1); + //TODO: Re-establish connection to state machine after a delay + //mWifiP2pChannel.connect(mContext, getHandler(), mWifiP2pManager.getMessenger()); + break; case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE: mBluetoothConnectionActive = (message.arg1 != BluetoothAdapter.STATE_DISCONNECTED); @@ -1809,14 +1726,14 @@ public class WifiStateMachine extends StateMachine { case CMD_DISCONNECT: case CMD_RECONNECT: case CMD_REASSOCIATE: - case SUP_CONNECTION_EVENT: - case SUP_DISCONNECTION_EVENT: - case NETWORK_CONNECTION_EVENT: - case NETWORK_DISCONNECTION_EVENT: - case SCAN_RESULTS_EVENT: - case SUPPLICANT_STATE_CHANGE_EVENT: - case AUTHENTICATION_FAILURE_EVENT: - case WPS_OVERLAP_EVENT: + case WifiMonitor.SUP_CONNECTION_EVENT: + case WifiMonitor.SUP_DISCONNECTION_EVENT: + case WifiMonitor.NETWORK_CONNECTION_EVENT: + case WifiMonitor.NETWORK_DISCONNECTION_EVENT: + case WifiMonitor.SCAN_RESULTS_EVENT: + case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT: + case WifiMonitor.AUTHENTICATION_FAILURE_EVENT: + case WifiMonitor.WPS_OVERLAP_EVENT: case CMD_BLACKLIST_NETWORK: case CMD_CLEAR_BLACKLIST: case CMD_SET_SCAN_MODE: @@ -1833,11 +1750,20 @@ public class WifiStateMachine extends StateMachine { case DhcpStateMachine.CMD_PRE_DHCP_ACTION: case DhcpStateMachine.CMD_POST_DHCP_ACTION: break; + case WifiMonitor.DRIVER_HUNG_EVENT: + setWifiEnabled(false); + setWifiEnabled(true); + break; case CMD_START_WPS: /* Return failure when the state machine cannot handle WPS initiation*/ mReplyChannel.replyToMessage(message, WifiManager.CMD_WPS_COMPLETED, new WpsResult(Status.FAILURE)); break; + case WifiP2pService.P2P_ENABLE_PENDING: + // turn off wifi and defer to be handled in DriverUnloadedState + setWifiEnabled(false); + deferMessage(message); + break; default: Log.e(TAG, "Error! unhandled message" + message); break; @@ -1864,6 +1790,11 @@ public class WifiStateMachine extends StateMachine { else { transitionTo(mDriverUnloadedState); } + + //Connect to WifiP2pService + mWifiP2pManager = (WifiP2pManager) mContext.getSystemService(Context.WIFI_P2P_SERVICE); + mWifiP2pChannel.connect(mContext, getHandler(), mWifiP2pManager.getMessenger()); + } } @@ -2079,7 +2010,11 @@ public class WifiStateMachine extends StateMachine { if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); switch (message.what) { case CMD_LOAD_DRIVER: - transitionTo(mDriverLoadingState); + mWifiP2pChannel.sendMessage(WIFI_ENABLE_PENDING); + transitionTo(mWaitForP2pDisableState); + break; + case WifiP2pService.P2P_ENABLE_PENDING: + mReplyChannel.replyToMessage(message, P2P_ENABLE_PROCEED); break; default: return NOT_HANDLED; @@ -2113,7 +2048,7 @@ public class WifiStateMachine extends StateMachine { public boolean processMessage(Message message) { if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); switch(message.what) { - case SUP_CONNECTION_EVENT: + case WifiMonitor.SUP_CONNECTION_EVENT: Log.d(TAG, "Supplicant connection established"); setWifiState(WIFI_STATE_ENABLED); mSupplicantRestartCount = 0; @@ -2133,7 +2068,7 @@ public class WifiStateMachine extends StateMachine { sendSupplicantConnectionChangedBroadcast(true); transitionTo(mDriverStartedState); break; - case SUP_DISCONNECTION_EVENT: + case WifiMonitor.SUP_DISCONNECTION_EVENT: if (++mSupplicantRestartCount <= SUPPLICANT_RESTART_TRIES) { Log.e(TAG, "Failed to setup control channel, restart supplicant"); WifiNative.killSupplicant(); @@ -2196,7 +2131,7 @@ public class WifiStateMachine extends StateMachine { case CMD_STOP_SUPPLICANT: /* Supplicant stopped by user */ transitionTo(mSupplicantStoppingState); break; - case SUP_DISCONNECTION_EVENT: /* Supplicant connection lost */ + case WifiMonitor.SUP_DISCONNECTION_EVENT: /* Supplicant connection lost */ Log.e(TAG, "Connection lost, restart supplicant"); WifiNative.killSupplicant(); WifiNative.closeSupplicantConnection(); @@ -2208,7 +2143,7 @@ public class WifiStateMachine extends StateMachine { transitionTo(mDriverLoadedState); sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS); break; - case SCAN_RESULTS_EVENT: + case WifiMonitor.SCAN_RESULTS_EVENT: eventLoggingEnabled = false; setScanResults(WifiNative.scanResultsCommand()); sendScanResultsAvailableBroadcast(); @@ -2310,10 +2245,10 @@ public class WifiStateMachine extends StateMachine { public boolean processMessage(Message message) { if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); switch(message.what) { - case SUP_CONNECTION_EVENT: + case WifiMonitor.SUP_CONNECTION_EVENT: Log.e(TAG, "Supplicant connection received while stopping"); break; - case SUP_DISCONNECTION_EVENT: + case WifiMonitor.SUP_DISCONNECTION_EVENT: Log.d(TAG, "Supplicant connection lost"); WifiNative.closeSupplicantConnection(); transitionTo(mDriverLoadedState); @@ -2353,7 +2288,7 @@ public class WifiStateMachine extends StateMachine { public boolean processMessage(Message message) { if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); switch(message.what) { - case SUPPLICANT_STATE_CHANGE_EVENT: + case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT: SupplicantState state = handleSupplicantStateChange(message); /* If suplicant is exiting out of INTERFACE_DISABLED state into * a state that indicates driver has started, it is ready to @@ -2366,10 +2301,10 @@ public class WifiStateMachine extends StateMachine { /* Queue driver commands & connection events */ case CMD_START_DRIVER: case CMD_STOP_DRIVER: - case NETWORK_CONNECTION_EVENT: - case NETWORK_DISCONNECTION_EVENT: - case AUTHENTICATION_FAILURE_EVENT: - case WPS_OVERLAP_EVENT: + case WifiMonitor.NETWORK_CONNECTION_EVENT: + case WifiMonitor.NETWORK_DISCONNECTION_EVENT: + case WifiMonitor.AUTHENTICATION_FAILURE_EVENT: + case WifiMonitor.WPS_OVERLAP_EVENT: case CMD_SET_SCAN_TYPE: case CMD_SET_HIGH_PERF_MODE: case CMD_SET_COUNTRY_CODE: @@ -2526,7 +2461,7 @@ public class WifiStateMachine extends StateMachine { public boolean processMessage(Message message) { if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); switch(message.what) { - case SUPPLICANT_STATE_CHANGE_EVENT: + case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT: SupplicantState state = handleSupplicantStateChange(message); if (state == SupplicantState.INTERFACE_DISABLED) { transitionTo(mDriverStoppedState); @@ -2570,7 +2505,7 @@ public class WifiStateMachine extends StateMachine { WifiNative.startDriverCommand(); mWakeLock.release(); break; - case SUPPLICANT_STATE_CHANGE_EVENT: + case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT: SupplicantState state = handleSupplicantStateChange(message); /* A driver start causes supplicant to first report an INTERFACE_DISABLED * state before transitioning out of it for connection. Stay in @@ -2616,9 +2551,9 @@ public class WifiStateMachine extends StateMachine { case CMD_DISCONNECT: case CMD_RECONNECT: case CMD_REASSOCIATE: - case SUPPLICANT_STATE_CHANGE_EVENT: - case NETWORK_CONNECTION_EVENT: - case NETWORK_DISCONNECTION_EVENT: + case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT: + case WifiMonitor.NETWORK_CONNECTION_EVENT: + case WifiMonitor.NETWORK_DISCONNECTION_EVENT: break; default: return NOT_HANDLED; @@ -2639,14 +2574,14 @@ public class WifiStateMachine extends StateMachine { if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); StateChangeResult stateChangeResult; switch(message.what) { - case AUTHENTICATION_FAILURE_EVENT: - mSupplicantStateTracker.sendMessage(AUTHENTICATION_FAILURE_EVENT); + case WifiMonitor.AUTHENTICATION_FAILURE_EVENT: + mSupplicantStateTracker.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT); break; - case WPS_OVERLAP_EVENT: + case WifiMonitor.WPS_OVERLAP_EVENT: /* We just need to broadcast the error */ sendErrorBroadcast(WifiManager.WPS_OVERLAP_ERROR); break; - case SUPPLICANT_STATE_CHANGE_EVENT: + case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT: handleSupplicantStateChange(message); break; /* Do a redundant disconnect without transition */ @@ -2688,12 +2623,12 @@ public class WifiStateMachine extends StateMachine { mWpsStateMachine.sendMessage(Message.obtain(message)); transitionTo(mWaitForWpsCompletionState); break; - case SCAN_RESULTS_EVENT: + case WifiMonitor.SCAN_RESULTS_EVENT: /* Set the scan setting back to "connect" mode */ WifiNative.setScanResultHandlingCommand(CONNECT_MODE); /* Handle scan results */ return NOT_HANDLED; - case NETWORK_CONNECTION_EVENT: + case WifiMonitor.NETWORK_CONNECTION_EVENT: Log.d(TAG,"Network connection established"); mLastNetworkId = message.arg1; mLastBssid = (String) message.obj; @@ -2707,7 +2642,7 @@ public class WifiStateMachine extends StateMachine { sendNetworkStateChangeBroadcast(mLastBssid); transitionTo(mConnectingState); break; - case NETWORK_DISCONNECTION_EVENT: + case WifiMonitor.NETWORK_DISCONNECTION_EVENT: Log.d(TAG,"Network connection lost"); handleNetworkDisconnect(); transitionTo(mDisconnectedState); @@ -2794,7 +2729,7 @@ public class WifiStateMachine extends StateMachine { deferMessage(message); break; /* Ignore */ - case NETWORK_CONNECTION_EVENT: + case WifiMonitor.NETWORK_CONNECTION_EVENT: break; case CMD_STOP_DRIVER: sendMessage(CMD_DISCONNECT); @@ -2901,7 +2836,7 @@ public class WifiStateMachine extends StateMachine { } break; /* Ignore */ - case NETWORK_CONNECTION_EVENT: + case WifiMonitor.NETWORK_CONNECTION_EVENT: break; case CMD_RSSI_POLL: eventLoggingEnabled = false; @@ -2962,7 +2897,7 @@ public class WifiStateMachine extends StateMachine { } break; /* Handle in DisconnectedState */ - case SUPPLICANT_STATE_CHANGE_EVENT: + case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT: deferMessage(message); break; default: @@ -3047,9 +2982,9 @@ public class WifiStateMachine extends StateMachine { } break; /* Ignore network disconnect */ - case NETWORK_DISCONNECTION_EVENT: + case WifiMonitor.NETWORK_DISCONNECTION_EVENT: break; - case SUPPLICANT_STATE_CHANGE_EVENT: + case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT: StateChangeResult stateChangeResult = (StateChangeResult) message.obj; setNetworkDetailedState(WifiInfo.getDetailedStateOf(stateChangeResult.state)); /* ConnectModeState does the rest of the handling */ @@ -3061,7 +2996,7 @@ public class WifiStateMachine extends StateMachine { } /* Handled in parent state */ return NOT_HANDLED; - case SCAN_RESULTS_EVENT: + case WifiMonitor.SCAN_RESULTS_EVENT: /* Re-enable background scan when a pending scan result is received */ if (mEnableBackgroundScan && mScanResultIsPending) { WifiNative.enableBackgroundScanCommand(true); @@ -3104,10 +3039,10 @@ public class WifiStateMachine extends StateMachine { case CMD_ENABLE_NETWORK: case CMD_RECONNECT: case CMD_REASSOCIATE: - case NETWORK_CONNECTION_EVENT: /* Handled after IP & proxy update */ + case WifiMonitor.NETWORK_CONNECTION_EVENT: /* Handled after IP & proxy update */ deferMessage(message); break; - case NETWORK_DISCONNECTION_EVENT: + case WifiMonitor.NETWORK_DISCONNECTION_EVENT: Log.d(TAG,"Network connection lost"); handleNetworkDisconnect(); break; @@ -3166,6 +3101,55 @@ public class WifiStateMachine extends StateMachine { startTethering(available); transitionTo(mTetheredState); break; + case WifiP2pService.P2P_ENABLE_PENDING: + // turn of soft Ap and defer to be handled in DriverUnloadedState + setWifiApEnabled(null, false); + deferMessage(message); + break; + default: + return NOT_HANDLED; + } + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + return HANDLED; + } + } + + class WaitForP2pDisableState extends State { + private int mSavedArg; + @Override + public void enter() { + if (DBG) Log.d(TAG, getName() + "\n"); + EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName()); + + //Preserve the argument arg1 that has information used in DriverLoadingState + mSavedArg = getCurrentMessage().arg1; + } + @Override + public boolean processMessage(Message message) { + if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); + switch(message.what) { + case WifiP2pService.WIFI_ENABLE_PROCEED: + //restore argument from original message (CMD_LOAD_DRIVER) + message.arg1 = mSavedArg; + transitionTo(mDriverLoadingState); + 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: + deferMessage(message); + break; default: return NOT_HANDLED; } diff --git a/wifi/java/android/net/wifi/WpsConfiguration.java b/wifi/java/android/net/wifi/WpsConfiguration.java index 12d951f..2e7689a 100644 --- a/wifi/java/android/net/wifi/WpsConfiguration.java +++ b/wifi/java/android/net/wifi/WpsConfiguration.java @@ -30,13 +30,16 @@ import java.util.BitSet; */ public class WpsConfiguration implements Parcelable { + /* Wi-Fi Protected Setup. www.wi-fi.org/wifi-protected-setup has details */ public enum Setup { - /* Wi-Fi protected setup push button configuration */ + /* Push button configuration */ PBC, - /* Wi-Fi protected setup pin method configuration with pin obtained from access point */ - PIN_FROM_ACCESS_POINT, - /* Wi-Fi protected setup pin method configuration with pin obtained from device */ - PIN_FROM_DEVICE, + /* Display pin method configuration - pin is generated and displayed on device */ + DISPLAY, + /* Keypad pin method configuration - pin is entered on device */ + KEYPAD, + /* Label pin method configuration - pin is obtained from a printed label */ + LABEL, /* Invalid config */ INVALID } diff --git a/wifi/java/android/net/wifi/WpsStateMachine.java b/wifi/java/android/net/wifi/WpsStateMachine.java index 120b228..af089ab 100644 --- a/wifi/java/android/net/wifi/WpsStateMachine.java +++ b/wifi/java/android/net/wifi/WpsStateMachine.java @@ -22,7 +22,7 @@ import com.android.internal.util.StateMachine; import android.content.Context; import android.content.Intent; -import android.net.wifi.WifiStateMachine.StateChangeResult; +import android.net.wifi.StateChangeResult; import android.net.wifi.WpsResult.Status; import android.os.Handler; import android.os.Message; @@ -99,10 +99,10 @@ class WpsStateMachine extends StateMachine { case PBC: result = WifiConfigStore.startWpsPbc(mWpsConfig); break; - case PIN_FROM_ACCESS_POINT: + case KEYPAD: result = WifiConfigStore.startWpsWithPinFromAccessPoint(mWpsConfig); break; - case PIN_FROM_DEVICE: + case DISPLAY: result = WifiConfigStore.startWpsWithPinFromDevice(mWpsConfig); break; default: @@ -139,7 +139,7 @@ class WpsStateMachine extends StateMachine { boolean retValue = HANDLED; if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); switch (message.what) { - case WifiStateMachine.SUPPLICANT_STATE_CHANGE_EVENT: + case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT: StateChangeResult stateChangeResult = (StateChangeResult) message.obj; SupplicantState supState = (SupplicantState) stateChangeResult.state; switch (supState) { @@ -194,7 +194,7 @@ class WpsStateMachine extends StateMachine { if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); switch (message.what) { //Ignore supplicant state changes - case WifiStateMachine.SUPPLICANT_STATE_CHANGE_EVENT: + case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT: break; default: retValue = NOT_HANDLED; diff --git a/wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl b/wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl new file mode 100644 index 0000000..a0c7dd1 --- /dev/null +++ b/wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2008, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.wifi.p2p; + +import android.os.Messenger; + +/** + * Interface that WifiP2pService implements + * + * {@hide} + */ +interface IWifiP2pManager +{ + Messenger getMessenger(); + boolean isP2pSupported(); +} + diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.aidl b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.aidl new file mode 100644 index 0000000..ea3b280 --- /dev/null +++ b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2011, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.wifi.p2p; + +parcelable WifiP2pConfig; diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java new file mode 100644 index 0000000..fff5ee3 --- /dev/null +++ b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.wifi.p2p; + +import android.net.wifi.WpsConfiguration; +import android.net.wifi.WpsConfiguration.Setup; +import android.os.Parcelable; +import android.os.Parcel; + +/** + * A class representing a Wi-Fi P2p configuration + * @hide + */ +public class WifiP2pConfig implements Parcelable { + + /** + * Device name + */ + public String deviceName; + + /** + * Device address + */ + public String deviceAddress; + + /** + * WPS configuration + */ + public WpsConfiguration wpsConfig; + + /** + * This is an integer value between 0 and 15 where 0 indicates the least + * inclination to be a group owner and 15 indicates the highest inclination + * to be a group owner. + */ + public int groupOwnerIntent; + + public boolean isPersistent; + + public boolean joinExistingGroup; + + /** + * Channel frequency in MHz + */ + public int channel; + + public WifiP2pConfig() { + //set defaults + wpsConfig = new WpsConfiguration(); + wpsConfig.setup = Setup.PBC; + } + + /* P2P-GO-NEG-REQUEST 42:fc:89:a8:96:09 dev_passwd_id=4 */ + public WifiP2pConfig(String supplicantEvent) throws IllegalArgumentException { + String[] tokens = supplicantEvent.split(" "); + + if (tokens.length < 2 || !tokens[0].equals("P2P-GO-NEG-REQUEST")) { + throw new IllegalArgumentException("Malformed supplicant event"); + } + + deviceAddress = tokens[1]; + wpsConfig = new WpsConfiguration(); + + if (tokens.length > 2) { + String[] nameVal = tokens[2].split("="); + int devPasswdId; + try { + devPasswdId = Integer.parseInt(nameVal[1]); + } catch (NumberFormatException e) { + devPasswdId = 0; + } + //As defined in wps/wps_defs.h + switch (devPasswdId) { + case 0x00: + wpsConfig.setup = Setup.LABEL; + break; + case 0x01: + wpsConfig.setup = Setup.KEYPAD; + break; + case 0x04: + wpsConfig.setup = Setup.PBC; + break; + case 0x05: + wpsConfig.setup = Setup.DISPLAY; + break; + default: + wpsConfig.setup = Setup.PBC; + break; + } + } + } + + public String toString() { + StringBuffer sbuf = new StringBuffer(); + sbuf.append("Device: ").append(deviceName); + sbuf.append("\n address: ").append(deviceAddress); + sbuf.append("\n wps: ").append(wpsConfig); + sbuf.append("\n groupOwnerIntent: ").append(groupOwnerIntent); + sbuf.append("\n isPersistent: ").append(isPersistent); + sbuf.append("\n joinExistingGroup: ").append(joinExistingGroup); + sbuf.append("\n channel: ").append(channel); + return sbuf.toString(); + } + + /** Implement the Parcelable interface {@hide} */ + public int describeContents() { + return 0; + } + + /** copy constructor {@hide} */ + public WifiP2pConfig(WifiP2pConfig source) { + if (source != null) { + //TODO: implement + } + } + + /** Implement the Parcelable interface {@hide} */ + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(deviceName); + dest.writeString(deviceAddress); + dest.writeParcelable(wpsConfig, flags); + dest.writeInt(groupOwnerIntent); + dest.writeInt(isPersistent ? 1 : 0); + dest.writeInt(joinExistingGroup ? 1 : 0); + dest.writeInt(channel); + } + + /** Implement the Parcelable interface {@hide} */ + public static final Creator<WifiP2pConfig> CREATOR = + new Creator<WifiP2pConfig>() { + public WifiP2pConfig createFromParcel(Parcel in) { + WifiP2pConfig config = new WifiP2pConfig(); + config.deviceName = in.readString(); + config.deviceAddress = in.readString(); + config.wpsConfig = (WpsConfiguration) in.readParcelable(null); + config.groupOwnerIntent = in.readInt(); + config.isPersistent = (in.readInt() == 1); + config.joinExistingGroup = (in.readInt() == 1); + config.channel = in.readInt(); + return config; + } + + public WifiP2pConfig[] newArray(int size) { + return new WifiP2pConfig[size]; + } + }; +} diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pDevice.aidl b/wifi/java/android/net/wifi/p2p/WifiP2pDevice.aidl new file mode 100644 index 0000000..8790c6f --- /dev/null +++ b/wifi/java/android/net/wifi/p2p/WifiP2pDevice.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2011, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.wifi.p2p; + +parcelable WifiP2pDevice; diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java b/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java new file mode 100644 index 0000000..83dc285 --- /dev/null +++ b/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java @@ -0,0 +1,312 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.wifi.p2p; + +import android.os.Parcelable; +import android.os.Parcel; +import android.util.Log; + +import java.util.regex.Pattern; + +/** + * A class representing a Wi-Fi p2p device + * @hide + */ +public class WifiP2pDevice implements Parcelable { + + private static final String TAG = "WifiP2pDevice"; + /** + * Device name + */ + public String deviceName; + + /** + * Device MAC address + */ + public String deviceAddress; + + /** + * interfaceAddress + * + * This address is used during group owner negotiation as the Intended + * P2P Interface Address and the group interface will be created with + * address as the local address in case of successfully completed + * negotiation. + */ + public String interfaceAddress; + + /** + * Primary device type + */ + public String primaryDeviceType; + + /** + * Secondary device type + */ + public String secondaryDeviceType; + + + // These definitions match the ones in wpa_supplicant + /* WPS config methods supported */ + private static final int WPS_CONFIG_USBA = 0x0001; + private static final int WPS_CONFIG_ETHERNET = 0x0002; + private static final int WPS_CONFIG_LABEL = 0x0004; + private static final int WPS_CONFIG_DISPLAY = 0x0008; + private static final int WPS_CONFIG_EXT_NFC_TOKEN = 0x0010; + private static final int WPS_CONFIG_INT_NFC_TOKEN = 0x0020; + private static final int WPS_CONFIG_NFC_INTERFACE = 0x0040; + private static final int WPS_CONFIG_PUSHBUTTON = 0x0080; + private static final int WPS_CONFIG_KEYPAD = 0x0100; + private static final int WPS_CONFIG_VIRT_PUSHBUTTON = 0x0280; + private static final int WPS_CONFIG_PHY_PUSHBUTTON = 0x0480; + private static final int WPS_CONFIG_VIRT_DISPLAY = 0x2008; + private static final int WPS_CONFIG_PHY_DISPLAY = 0x4008; + + /* Device Capability bitmap */ + private static final int DEVICE_CAPAB_SERVICE_DISCOVERY = 1; + private static final int DEVICE_CAPAB_CLIENT_DISCOVERABILITY = 1<<1; + private static final int DEVICE_CAPAB_CONCURRENT_OPER = 1<<2; + private static final int DEVICE_CAPAB_INFRA_MANAGED = 1<<3; + private static final int DEVICE_CAPAB_DEVICE_LIMIT = 1<<4; + private static final int DEVICE_CAPAB_INVITATION_PROCEDURE = 1<<5; + + /* Group Capability bitmap */ + private static final int GROUP_CAPAB_GROUP_OWNER = 1; + private static final int GROUP_CAPAB_PERSISTENT_GROUP = 1<<1; + private static final int GROUP_CAPAB_GROUP_LIMIT = 1<<2; + private static final int GROUP_CAPAB_INTRA_BSS_DIST = 1<<3; + private static final int GROUP_CAPAB_CROSS_CONN = 1<<4; + private static final int GROUP_CAPAB_PERSISTENT_RECONN = 1<<5; + private static final int GROUP_CAPAB_GROUP_FORMATION = 1<<6; + + /** + * WPS config methods supported + */ + public int wpsConfigMethodsSupported; + + /** + * Device capability + */ + public int deviceCapability; + + /** + * Group capability + */ + public int groupCapability; + + public enum Status { + CONNECTED, + INVITED, + FAILED, + AVAILABLE, + UNAVAILABLE, + } + + public Status status = Status.UNAVAILABLE; + + public WifiP2pDevice() { + } + + /** + * @param string formats supported include + * P2P-DEVICE-FOUND fa:7b:7a:42:02:13 p2p_dev_addr=fa:7b:7a:42:02:13 + * pri_dev_type=1-0050F204-1 name='p2p-TEST1' config_methods=0x188 dev_capab=0x27 + * group_capab=0x0 + * + * P2P-DEVICE-LOST p2p_dev_addr=fa:7b:7a:42:02:13 + * + * fa:7b:7a:42:02:13 + * + * P2P-PROV-DISC-PBC-REQ 42:fc:89:e1:e2:27 p2p_dev_addr=42:fc:89:e1:e2:27 + * pri_dev_type=1-0050F204-1 name='p2p-TEST2' config_methods=0x188 dev_capab=0x27 + * group_capab=0x0 + * + * P2P-PROV-DISC-ENTER-PIN 42:fc:89:e1:e2:27 p2p_dev_addr=42:fc:89:e1:e2:27 + * pri_dev_type=1-0050F204-1 name='p2p-TEST2' config_methods=0x188 dev_capab=0x27 + * group_capab=0x0 + * + * P2P-PROV-DISC-SHOW-PIN 42:fc:89:e1:e2:27 44490607 p2p_dev_addr=42:fc:89:e1:e2:27 + * pri_dev_type=1-0050F204-1 name='p2p-TEST2' config_methods=0x188 dev_capab=0x27 + * group_capab=0x0 + * + * Note: The events formats can be looked up in the wpa_supplicant code + */ + public WifiP2pDevice(String string) throws IllegalArgumentException { + String[] tokens = string.split(" "); + + if (tokens.length < 1) { + throw new IllegalArgumentException("Malformed supplicant event"); + } + + /* Just a device address */ + if (tokens.length == 1) { + deviceAddress = string; + return; + } + + Pattern p = Pattern.compile("(?:[0-9a-f]{2}:){5}[0-9a-f]{2}", Pattern.CASE_INSENSITIVE); + if (p.matcher(tokens[1]).matches()) interfaceAddress = tokens[1]; + + for (String token : tokens) { + String[] nameValue = token.split("="); + if (nameValue.length != 2) continue; + + if (nameValue[0].equals("p2p_dev_addr")) { + deviceAddress = nameValue[1]; + continue; + } + + if (nameValue[0].equals("pri_dev_type")) { + primaryDeviceType = nameValue[1]; + continue; + } + + if (nameValue[0].equals("name")) { + deviceName = trimQuotes(nameValue[1]); + } + + if (nameValue[0].equals("config_methods")) { + wpsConfigMethodsSupported = parseHex(nameValue[1]); + continue; + } + + if (nameValue[0].equals("dev_capab")) { + deviceCapability = parseHex(nameValue[1]); + continue; + } + + if (nameValue[0].equals("group_capab")) { + groupCapability = parseHex(nameValue[1]); + continue; + } + } + + if (tokens[0].startsWith("P2P-DEVICE-FOUND")) { + status = Status.AVAILABLE; + } + } + + public boolean isGroupOwner() { + return (groupCapability & GROUP_CAPAB_GROUP_OWNER) != 0; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof WifiP2pDevice)) return false; + + WifiP2pDevice other = (WifiP2pDevice) obj; + if (other == null || other.deviceAddress == null) { + return (deviceAddress == null); + } + //STOPSHIP: fix later + //return other.deviceAddress.equals(deviceAddress); + return other.deviceAddress.startsWith(deviceAddress.substring(0,8)); + } + + public String toString() { + StringBuffer sbuf = new StringBuffer(); + sbuf.append("Device: ").append(deviceName); + sbuf.append("\n deviceAddress: ").append(deviceAddress); + sbuf.append("\n interfaceAddress: ").append(interfaceAddress); + sbuf.append("\n primary type: ").append(primaryDeviceType); + sbuf.append("\n secondary type: ").append(secondaryDeviceType); + sbuf.append("\n wps: ").append(wpsConfigMethodsSupported); + sbuf.append("\n grpcapab: ").append(groupCapability); + sbuf.append("\n devcapab: ").append(deviceCapability); + sbuf.append("\n status: ").append(status); + return sbuf.toString(); + } + + /** Implement the Parcelable interface {@hide} */ + public int describeContents() { + return 0; + } + + /** copy constructor {@hide} */ + public WifiP2pDevice(WifiP2pDevice source) { + if (source != null) { + deviceName = source.deviceName; + deviceAddress = source.deviceAddress; + interfaceAddress = source.interfaceAddress; + primaryDeviceType = source.primaryDeviceType; + secondaryDeviceType = source.secondaryDeviceType; + wpsConfigMethodsSupported = source.wpsConfigMethodsSupported; + deviceCapability = source.deviceCapability; + groupCapability = source.groupCapability; + status = source.status; + } + } + + /** Implement the Parcelable interface {@hide} */ + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(deviceName); + dest.writeString(deviceAddress); + dest.writeString(interfaceAddress); + dest.writeString(primaryDeviceType); + dest.writeString(secondaryDeviceType); + dest.writeInt(wpsConfigMethodsSupported); + dest.writeInt(deviceCapability); + dest.writeInt(groupCapability); + dest.writeString(status.name()); + } + + /** Implement the Parcelable interface {@hide} */ + public static final Creator<WifiP2pDevice> CREATOR = + new Creator<WifiP2pDevice>() { + public WifiP2pDevice createFromParcel(Parcel in) { + WifiP2pDevice device = new WifiP2pDevice(); + device.deviceName = in.readString(); + device.deviceAddress = in.readString(); + device.interfaceAddress = in.readString(); + device.primaryDeviceType = in.readString(); + device.secondaryDeviceType = in.readString(); + device.wpsConfigMethodsSupported = in.readInt(); + device.deviceCapability = in.readInt(); + device.groupCapability = in.readInt(); + device.status = Status.valueOf(in.readString()); + return device; + } + + public WifiP2pDevice[] newArray(int size) { + return new WifiP2pDevice[size]; + } + }; + + private String trimQuotes(String str) { + str = str.trim(); + if (str.startsWith("'") && str.endsWith("'")) { + return str.substring(1, str.length()-1); + } + return str; + } + + //supported formats: 0x1abc, 0X1abc, 1abc + private int parseHex(String hexString) { + int num = 0; + if (hexString.startsWith("0x") || hexString.startsWith("0X")) { + hexString = hexString.substring(2); + } + + try { + num = Integer.parseInt(hexString, 16); + } catch(NumberFormatException e) { + Log.e(TAG, "Failed to parse hex string " + hexString); + } + return num; + } +} diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pDeviceList.aidl b/wifi/java/android/net/wifi/p2p/WifiP2pDeviceList.aidl new file mode 100644 index 0000000..6c79009 --- /dev/null +++ b/wifi/java/android/net/wifi/p2p/WifiP2pDeviceList.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2011, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.wifi.p2p; + +parcelable WifiP2pDeviceList; diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pDeviceList.java b/wifi/java/android/net/wifi/p2p/WifiP2pDeviceList.java new file mode 100644 index 0000000..4ec23b8 --- /dev/null +++ b/wifi/java/android/net/wifi/p2p/WifiP2pDeviceList.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.wifi.p2p; + +import android.os.Parcelable; +import android.os.Parcel; +import android.net.wifi.p2p.WifiP2pDevice; +import android.util.Log; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; + +/** + * A class representing a Wi-Fi P2p device list + * @hide + */ +public class WifiP2pDeviceList implements Parcelable { + + private Collection<WifiP2pDevice> mDevices; + + public WifiP2pDeviceList() { + mDevices = new ArrayList<WifiP2pDevice>(); + } + + //copy constructor + public WifiP2pDeviceList(WifiP2pDeviceList source) { + if (source != null) { + mDevices = source.getDeviceList(); + } + } + + public WifiP2pDeviceList(ArrayList<WifiP2pDevice> devices) { + mDevices = new ArrayList<WifiP2pDevice>(); + for (WifiP2pDevice device : devices) { + mDevices.add(device); + } + } + + public void clear() { + mDevices.clear(); + } + + public void add(WifiP2pDevice device) { + if (device == null) return; + if (mDevices.contains(device)) return; + mDevices.add(device); + } + + public boolean remove(WifiP2pDevice device) { + if (device == null) return false; + return mDevices.remove(device); + } + + public Collection<WifiP2pDevice> getDeviceList() { + return Collections.unmodifiableCollection(mDevices); + } + + public String toString() { + StringBuffer sbuf = new StringBuffer(); + for (WifiP2pDevice device : mDevices) { + sbuf.append("\n").append(device); + } + return sbuf.toString(); + } + + /** Implement the Parcelable interface {@hide} */ + public int describeContents() { + return 0; + } + + /** Implement the Parcelable interface {@hide} */ + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mDevices.size()); + for(WifiP2pDevice device : mDevices) { + dest.writeParcelable(device, flags); + } + } + + /** Implement the Parcelable interface {@hide} */ + public static final Creator<WifiP2pDeviceList> CREATOR = + new Creator<WifiP2pDeviceList>() { + public WifiP2pDeviceList createFromParcel(Parcel in) { + WifiP2pDeviceList deviceList = new WifiP2pDeviceList(); + + int deviceCount = in.readInt(); + for (int i = 0; i < deviceCount; i++) { + deviceList.add((WifiP2pDevice)in.readParcelable(null)); + } + return deviceList; + } + + public WifiP2pDeviceList[] newArray(int size) { + return new WifiP2pDeviceList[size]; + } + }; +} diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pGroup.aidl b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.aidl new file mode 100644 index 0000000..403f2b1 --- /dev/null +++ b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2011, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.wifi.p2p; + +parcelable WifiP2pGroup; diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java new file mode 100644 index 0000000..ca6e4d5 --- /dev/null +++ b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.wifi.p2p; + +import android.os.Parcelable; +import android.os.Parcel; + +import java.util.ArrayList; +import java.util.List; +import java.util.Collection; +import java.util.Collections; + +/** + * A class representing a Wi-Fi P2p group + * @hide + */ +public class WifiP2pGroup implements Parcelable { + + /** The network name */ + private String mNetworkName; + + /** The network bssid */ + private String mNetworkBssid; + + /** Group owner */ + private WifiP2pDevice mOwner; + + /** Device is group owner */ + private boolean mIsGroupOwner; + + /** Group clients */ + private List<WifiP2pDevice> mClients = new ArrayList<WifiP2pDevice>(); + + private int mChannel; + + /** + * The network passphrase + * <p/> + * The passphrase used for WPA2-PSK + */ + private String mPassphrase; + + /** + * TODO: fix + * Sometimes supplicant sends a psk + */ + private String mPsk; + + /** Indicates that the group is persistent */ + private boolean mIsPersistent; + + private String mInterface; + + public WifiP2pGroup() { + } + + /** + * @param string formats supported include + * + * P2P-GROUP-STARTED p2p-wlan0-0 [client|GO] ssid="DIRECT-W8" freq=2437 + * [psk=2182b2e50e53f260d04f3c7b25ef33c965a3291b9b36b455a82d77fd82ca15bc| + * passphrase="fKG4jMe3"] go_dev_addr=fa:7b:7a:42:02:13 + * + * P2P-GROUP-REMOVED p2p-wlan0-0 [client|GO] reason=REQUESTED + * + * P2P-INVITATION-RECEIVED sa=fa:7b:7a:42:02:13 go_dev_addr=f8:7b:7a:42:02:13 + * bssid=fa:7b:7a:42:82:13 unknown-network + * + * Note: The events formats can be looked up in the wpa_supplicant code + */ + public WifiP2pGroup(String supplicantEvent) throws IllegalArgumentException { + + String[] tokens = supplicantEvent.split(" "); + + if (tokens.length < 3) { + throw new IllegalArgumentException("Malformed supplicant event"); + } + + if (tokens[0].startsWith("P2P-GROUP")) { + mInterface = tokens[1]; + mIsGroupOwner = tokens[2].equals("GO"); + + for (String token : tokens) { + String[] nameValue = token.split("="); + if (nameValue.length != 2) continue; + + if (nameValue[0].equals("ssid")) { + mNetworkName = nameValue[1]; + continue; + } + + if (nameValue[0].equals("freq")) { + try { + mChannel = Integer.parseInt(nameValue[1]); + } catch (NumberFormatException e) { + mChannel = 0; //invalid + } + continue; + } + + if (nameValue[0].equals("psk")) { + mPsk = nameValue[1]; + continue; + } + + if (nameValue[0].equals("passphrase")) { + mPassphrase = nameValue[1]; + continue; + } + + if (nameValue[0].equals("go_dev_addr")) { + mOwner = new WifiP2pDevice(nameValue[1]); + } + } + } else if (tokens[0].equals("P2P-INVITATION-RECEIVED")) { + for (String token : tokens) { + String[] nameValue = token.split("="); + if (nameValue.length != 2) continue; + + if (nameValue[0].equals("go_dev_addr")) { + mOwner = new WifiP2pDevice(nameValue[1]); + continue; + } + + if (nameValue[0].equals("bssid")) { + mNetworkBssid = nameValue[1]; + } + } + } else { + throw new IllegalArgumentException("Malformed supplicant event"); + } + } + + public boolean isGroupOwner() { + return mIsGroupOwner; + } + + public WifiP2pDevice getOwner() { + return mOwner; + } + + public void addClient(String address) { + addClient(new WifiP2pDevice(address)); + } + + public void addClient(WifiP2pDevice device) { + for (WifiP2pDevice client : mClients) { + if (client.equals(device)) return; + } + mClients.add(device); + } + + public boolean removeClient(String address) { + return mClients.remove(new WifiP2pDevice(address)); + } + + public boolean removeClient(WifiP2pDevice device) { + return mClients.remove(device); + } + + public boolean isClientListEmpty() { + return mClients.size() == 0; + } + + public Collection<WifiP2pDevice> getClientList() { + return Collections.unmodifiableCollection(mClients); + } + + public String getInterface() { + return mInterface; + } + + // TODO: implement + public String toString() { + StringBuffer sbuf = new StringBuffer(); + //sbuf.append("SSID: ").append(SSID); + //sbuf.append("\n passphrase: ").append(passphrase); + return sbuf.toString(); + } + + /** Implement the Parcelable interface {@hide} */ + public int describeContents() { + return 0; + } + + /** copy constructor {@hide} */ + // TODO: implement + public WifiP2pGroup(WifiP2pGroup source) { + if (source != null) { + } + } + + /** Implement the Parcelable interface {@hide} */ + // STOPSHIP: implement + public void writeToParcel(Parcel dest, int flags) { + } + + /** Implement the Parcelable interface {@hide} */ + public static final Creator<WifiP2pGroup> CREATOR = + new Creator<WifiP2pGroup>() { + public WifiP2pGroup createFromParcel(Parcel in) { + WifiP2pGroup group = new WifiP2pGroup(); + return group; + } + + public WifiP2pGroup[] newArray(int size) { + return new WifiP2pGroup[size]; + } + }; +} diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java new file mode 100644 index 0000000..ea212ac --- /dev/null +++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java @@ -0,0 +1,344 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.wifi.p2p; + +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; +import android.content.Context; +import android.os.Binder; +import android.os.IBinder; +import android.os.Handler; +import android.os.Message; +import android.os.RemoteException; +import android.os.WorkSource; +import android.os.Messenger; + +import com.android.internal.util.AsyncChannel; +import com.android.internal.util.Protocol; + +/** + * This class provides the API for managing Wi-Fi p2p + * connectivity. Get an instance of this class by calling + * {@link android.content.Context#getSystemService(String) + * Context.getSystemService(Context.WIFI_P2P_SERVICE)}. + * + * It deals with the following: + * <ul> + * <li>Wi-Fi peer discovery and connection setup. Allows applications to initiate a discovery to + * find available peers and then setup a connection </li> + * <li>Configuration and status query. Allows applications to fetch the current list + * of available and connected peers and query connection status </li> + * <li>Intent actions that are broadcast to track operations + * on a p2p connection</li> + * </ul> + * @hide + */ +public class WifiP2pManager { + /** + * Broadcast intent action to indicate whether Wi-Fi p2p is enabled or disabled. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String WIFI_P2P_STATE_CHANGED_ACTION = + "android.net.wifi.P2P_STATE_CHANGED"; + + /** + * The lookup key for an int that indicates whether Wi-Fi p2p is enabled or disabled. + * Retrieve it with {@link android.content.Intent#getIntExtra(String,int)}. + * + * @see #WIFI_P2P_STATE_DISABLED + * @see #WIFI_P2P_STATE_ENABLED + */ + public static final String EXTRA_WIFI_STATE = "wifi_p2p_state"; + + /** + * Wi-Fi p2p is disabled. + * + * @see #WIFI_P2P_STATE_CHANGED_ACTION + * @see #getWifiP2pState() + */ + public static final int WIFI_P2P_STATE_DISABLED = 1; + + /** + * Wi-Fi p2p is enabled. + * + * @see #WIFI_P2P_STATE_CHANGED_ACTION + * @see #getWifiP2pState() + */ + public static final int WIFI_P2P_STATE_ENABLED = 2; + + /** + * Broadcast intent action indicating that the state of Wi-Fi p2p connectivity + * has changed. One extra provides the new state + * in the form of a {@link android.net.NetworkInfo} object. + * @see #EXTRA_NETWORK_INFO + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String WIFI_P2P_CONNECTION_CHANGED_ACTION = + "android.net.wifi.CONNECTION_STATE_CHANGE"; + + /** + * The lookup key for a {@link android.net.NetworkInfo} object associated with the + * Wi-Fi network. Retrieve with + * {@link android.content.Intent#getParcelableExtra(String)}. + */ + public static final String EXTRA_NETWORK_INFO = "networkInfo"; + + /** + * Broadcast intent action indicating that the available peer list has changed + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String WIFI_P2P_PEERS_CHANGED_ACTION = + "android.net.wifi.PEERS_CHANGED"; + + /** + * Activity Action: Pick a Wi-Fi p2p network to connect to. + * <p>Input: Nothing. + * <p>Output: Nothing. + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_PICK_WIFI_P2P_NETWORK = + "android.net.wifi.PICK_WIFI_P2P_NETWORK"; + + IWifiP2pManager mService; + + /* For communication with WifiP2pService */ + private AsyncChannel mAsyncChannel = new AsyncChannel(); + + /* AsyncChannel notifications to apps */ + public static final int HANDLER_CONNECTION = AsyncChannel.CMD_CHANNEL_HALF_CONNECTED; + public static final int HANDLER_DISCONNECTION = AsyncChannel.CMD_CHANNEL_DISCONNECTED; + + private static final int BASE = Protocol.BASE_WIFI_P2P_MANAGER; + + public static final int ENABLE_P2P = BASE + 1; + public static final int ENABLE_P2P_FAILED = BASE + 2; + public static final int ENABLE_P2P_SUCCEEDED = BASE + 3; + + /* arg1 on ENABLE_P2P_FAILED indicates a reason for failure */ + public static final int P2P_UNSUPPORTED = 1; + + public static final int DISABLE_P2P = BASE + 5; + public static final int DISABLE_P2P_FAILED = BASE + 6; + public static final int DISABLE_P2P_SUCCEEDED = BASE + 7; + + public static final int START_LISTEN_MODE = BASE + 9; + public static final int START_LISTEN_FAILED = BASE + 10; + public static final int START_LISTEN_SUCCEEDED = BASE + 11; + + public static final int DISCOVER_PEERS = BASE + 13; + public static final int DISCOVER_PEERS_FAILED = BASE + 14; + public static final int DISCOVER_PEERS_SUCCEDED = BASE + 15; + + public static final int CANCEL_DISCOVER_PEERS = BASE + 17; + public static final int CANCEL_DISCOVER_PEERS_FAILED = BASE + 18; + public static final int CANCEL_DISCOVER_PEERS_SUCCEDED = BASE + 19; + + public static final int CONNECT = BASE + 21; + public static final int CONNECT_FAILED = BASE + 22; + public static final int CONNECT_SUCCEEDED = BASE + 23; + + public static final int CANCEL_CONNECT = BASE + 25; + public static final int CANCEL_CONNECT_FAILED = BASE + 26; + public static final int CANCEL_CONNECT_SUCCEDED = BASE + 27; + + public static final int REJECT = BASE + 28; + public static final int REJECT_FAILED = BASE + 29; + public static final int REJECT_SUCCEEDED = BASE + 30; + + public static final int CREATE_GROUP = BASE + 31; + public static final int CREATE_GROUP_FAILED = BASE + 32; + public static final int CREATE_GROUP_SUCCEEDED = BASE + 33; + + public static final int REMOVE_GROUP = BASE + 34; + public static final int REMOVE_GROUP_FAILED = BASE + 35; + public static final int REMOVE_GROUP_SUCCEEDED = BASE + 36; + + public static final int REQUEST_SETTINGS = BASE + 37; + public static final int RESPONSE_SETTINGS = BASE + 38; + + public static final int REQUEST_PEERS = BASE + 39; + public static final int RESPONSE_PEERS = BASE + 40; + + public static final int REQUEST_CONNECTION_STATUS = BASE + 41; + public static final int RESPONSE_CONNECTION_STATUS = BASE + 42; + + public static final int WPS_PBC = BASE + 43; + public static final int WPS_PIN = BASE + 44; + public static final int WPS_PIN_AVAILABLE = BASE + 45; + + /** + * Create a new WifiP2pManager instance. Applications use + * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve + * the standard {@link android.content.Context#WIFI_P2P_SERVICE Context.WIFI_P2P_SERVICE}. + * @param service the Binder interface + * @param handler target for messages + * @hide - hide this because it takes in a parameter of type IWifiP2pManager, which + * is a system private class. + */ + public WifiP2pManager(IWifiP2pManager service) { + mService = service; + } + + /** + * Registers the application handler with the Wi-Fi framework. + * This function must be the first to be called before any p2p control + * or query operations can be performed. + * @param srcContext is the context of the source + * @param srcHandler is the handler on which the source receives messages + * @return {@code true} if the operation succeeded + */ + public boolean connectHandler(Context srcContext, Handler srcHandler) { + Messenger messenger = getMessenger(); + if (messenger == null) return false; + return mAsyncChannel.connectSync(srcContext, srcHandler, messenger) + == AsyncChannel.STATUS_SUCCESSFUL; + } + + public boolean isP2pSupported() { + try { + return mService.isP2pSupported(); + } catch (RemoteException e) { + return false; + } + } + + /** + * Sends in a request to the system to enable p2p. This will pop up a dialog + * to the user and upon authorization will enable p2p. + */ + public void enableP2p() { + mAsyncChannel.sendMessage(ENABLE_P2P); + } + + /** + * Sends in a request to the system to disable p2p. This will pop up a dialog + * to the user and upon authorization will enable p2p. + */ + public void disableP2p() { + mAsyncChannel.sendMessage(DISABLE_P2P); + } + + /** + * Set device in listen mode. This will make the device discoverable by + * another peer. + * A dialog to the user is thrown to request his permission since it can + * have a significant impact on power consumption + */ + public void setListenState(int timeout) { + mAsyncChannel.sendMessage(START_LISTEN_MODE, timeout); + } + + /** + * Initiates peer discovery + */ + public void discoverPeers() { + mAsyncChannel.sendMessage(DISCOVER_PEERS); + } + + /** + * Initiates peer discovery with a timeout + */ + public void discoverPeers(int timeout) { + mAsyncChannel.sendMessage(DISCOVER_PEERS, timeout); + } + + /** + * Cancel any existing peer discovery operation + */ + public void cancelPeerDiscovery() { + mAsyncChannel.sendMessage(CANCEL_DISCOVER_PEERS); + } + + /** + * Start a p2p connection + * + * @param peer Configuration described in a {@link WifiP2pConfig} object. + */ + public void connect(WifiP2pConfig config) { + mAsyncChannel.sendMessage(CONNECT, config); + } + + /** + * Cancel any ongoing negotiation or disconnect from an existing group + */ + public void disconnect() { + mAsyncChannel.sendMessage(CANCEL_CONNECT); + } + + /** + * Create a p2p group. This is essentially an access point that can accept + * client connections. + */ + public void createGroup() { + mAsyncChannel.sendMessage(CREATE_GROUP); + } + + /** + * Remove the current group. This also removes the p2p interface created + * during group formation. + */ + public void removeGroup() { + mAsyncChannel.sendMessage(REMOVE_GROUP); + } + + /** + * Request current p2p settings. This returns a RESPONSE_SETTINGS on the source + * handler. + */ + public void requestP2pSettings() { + mAsyncChannel.sendMessage(REQUEST_SETTINGS); + } + + /** + * Request the list of peers. This returns a RESPONSE_PEERS on the source + * handler. + */ + public void requestPeers() { + mAsyncChannel.sendMessage(REQUEST_PEERS); + } + + /** + * Fetch device list from a RESPONSE_PEERS message + */ + public WifiP2pDeviceList peersInResponse(Message msg) { + return (WifiP2pDeviceList) msg.obj; + } + + /** + * Request device connection status. This returns a RESPONSE_CONNECTION_STATUS on + * the source handler. + */ + public void requestConnectionStatus() { + mAsyncChannel.sendMessage(REQUEST_CONNECTION_STATUS); + } + + + /** + * Get a reference to WifiP2pService handler. This is used to establish + * an AsyncChannel communication with WifiService + * + * @return Messenger pointing to the WifiP2pService handler + * @hide + */ + public Messenger getMessenger() { + try { + return mService.getMessenger(); + } catch (RemoteException e) { + return null; + } + } +} diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pService.java b/wifi/java/android/net/wifi/p2p/WifiP2pService.java new file mode 100644 index 0000000..4988f0b --- /dev/null +++ b/wifi/java/android/net/wifi/p2p/WifiP2pService.java @@ -0,0 +1,880 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.server; + +import android.app.AlertDialog; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.net.wifi.WifiManager; +import android.net.wifi.WifiMonitor; +import android.net.wifi.WifiNative; +import android.net.wifi.WifiStateMachine; +import android.net.wifi.WpsConfiguration; +import android.net.wifi.WpsConfiguration.Setup; +import android.net.wifi.p2p.IWifiP2pManager; +import android.net.wifi.p2p.WifiP2pConfig; +import android.net.wifi.p2p.WifiP2pDevice; +import android.net.wifi.p2p.WifiP2pDevice.Status; +import android.net.wifi.p2p.WifiP2pDeviceList; +import android.net.wifi.p2p.WifiP2pGroup; +import android.net.wifi.p2p.WifiP2pManager; +import android.os.Binder; +import android.os.IBinder; +import android.os.Handler; +import android.os.Messenger; +import android.os.HandlerThread; +import android.os.IBinder; +import android.os.Message; +import android.util.Slog; +import android.view.LayoutInflater; +import android.view.View; +import android.view.WindowManager; +import android.widget.EditText; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.Collection; + +import com.android.internal.util.AsyncChannel; +import com.android.internal.util.Protocol; +import com.android.internal.R; +import com.android.internal.util.State; +import com.android.internal.util.StateMachine; + +/** + * WifiP2pService inclues a state machine to perform Wi-Fi p2p operations. Applications + * communicate with this service to issue device discovery and connectivity requests + * through the WifiP2pManager interface. The state machine communicates with the wifi + * driver through wpa_supplicant and handles the event responses through WifiMonitor. + * + * Note that the term Wifi when used without a p2p suffix refers to the client mode + * of Wifi operation + * @hide + */ +public class WifiP2pService extends IWifiP2pManager.Stub { + private static final String TAG = "WifiP2pService"; + private static final boolean DBG = true; + + private Context mContext; + + // Tracked to notify the user about wifi client/hotspot being shut down + // during p2p bring up + private int mWifiState = WifiManager.WIFI_STATE_DISABLED; + private int mWifiApState = WifiManager.WIFI_AP_STATE_DISABLED; + + private P2pStateMachine mP2pStateMachine; + private AsyncChannel mReplyChannel = new AsyncChannel();; + private AsyncChannel mWifiChannel; + + private static final int BASE = Protocol.BASE_WIFI_P2P_SERVICE; + + /* Message sent to WifiStateMachine to indicate p2p enable is pending */ + public static final int P2P_ENABLE_PENDING = BASE + 1; + /* Message sent to WifiStateMachine to indicate Wi-Fi client/hotspot operation can proceed */ + public static final int WIFI_ENABLE_PROCEED = BASE + 2; + + /* User accepted to disable Wi-Fi in order to enable p2p */ + private static final int WIFI_DISABLE_USER_ACCEPT = BASE + 11; + + private final boolean mP2pSupported; + + public WifiP2pService(Context context) { + mContext = context; + + mP2pSupported = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_wifi_p2p_support); + + mP2pStateMachine = new P2pStateMachine(TAG, mP2pSupported); + mP2pStateMachine.start(); + + // broadcasts + IntentFilter filter = new IntentFilter(); + filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); + filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); + mContext.registerReceiver(new WifiStateReceiver(), filter); + + } + + private class WifiStateReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getAction().equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) { + mWifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, + WifiManager.WIFI_STATE_DISABLED); + } else if (intent.getAction().equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) { + mWifiApState = intent.getIntExtra(WifiManager.EXTRA_WIFI_AP_STATE, + WifiManager.WIFI_AP_STATE_DISABLED); + } + } + } + + private void enforceAccessPermission() { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE, + "WifiP2pService"); + } + + private void enforceChangePermission() { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_WIFI_STATE, + "WifiP2pService"); + } + + /** + * Get a reference to handler. This is used by a client to establish + * an AsyncChannel communication with WifiP2pService + */ + public Messenger getMessenger() { + enforceAccessPermission(); + enforceChangePermission(); + return new Messenger(mP2pStateMachine.getHandler()); + } + + /** + * Return if p2p is supported + */ + public boolean isP2pSupported() { + enforceAccessPermission(); + return mP2pSupported; + } + + @Override + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) + != PackageManager.PERMISSION_GRANTED) { + pw.println("Permission Denial: can't dump WifiP2pService from from pid=" + + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid()); + return; + } + } + + + /** + * Handles interaction with WifiStateMachine + */ + private class P2pStateMachine extends StateMachine { + + private DefaultState mDefaultState = new DefaultState(); + private P2pNotSupportedState mP2pNotSupportedState = new P2pNotSupportedState(); + private P2pDisablingState mP2pDisablingState = new P2pDisablingState(); + private P2pDisabledState mP2pDisabledState = new P2pDisabledState(); + private WaitForWifiDisableState mWaitForWifiDisableState = new WaitForWifiDisableState(); + private P2pEnablingState mP2pEnablingState = new P2pEnablingState(); + private P2pEnabledState mP2pEnabledState = new P2pEnabledState(); + // Inactive is when p2p is enabled with no connectivity + private InactiveState mInactiveState = new InactiveState(); + private GroupNegotiationState mGroupNegotiationState = new GroupNegotiationState(); + private GroupCreatedState mGroupCreatedState = new GroupCreatedState(); + + private WifiMonitor mWifiMonitor = new WifiMonitor(this); + + private WifiP2pDeviceList mPeers = new WifiP2pDeviceList(); + private WifiP2pGroup mGroup; + + // Saved enable request message so the state machine can send an appropriate response + private Message mSavedEnableRequestMessage; + + // Saved WifiP2pConfig from GO negotiation request + private WifiP2pConfig mSavedGoNegotiationConfig; + + // Saved WifiP2pConfig from connect request + private WifiP2pConfig mSavedConnectConfig; + + // Saved WifiP2pGroup from invitation request + private WifiP2pGroup mSavedP2pGroup; + + P2pStateMachine(String name, boolean p2pSupported) { + super(name); + + addState(mDefaultState); + addState(mP2pNotSupportedState, mDefaultState); + addState(mP2pDisablingState, mDefaultState); + addState(mP2pDisabledState, mDefaultState); + addState(mWaitForWifiDisableState, mDefaultState); + addState(mP2pEnablingState, mDefaultState); + addState(mP2pEnabledState, mDefaultState); + addState(mInactiveState, mP2pEnabledState); + addState(mGroupNegotiationState, mP2pEnabledState); + addState(mGroupCreatedState, mP2pEnabledState); + + if (p2pSupported) { + setInitialState(mP2pDisabledState); + } else { + setInitialState(mP2pNotSupportedState); + } + } + + // TODO: Respond to every p2p request with success/failure + class DefaultState extends State { + @Override + public boolean processMessage(Message message) { + if (DBG) Slog.d(TAG, getName() + message.toString()); + switch (message.what) { + case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: + if (message.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { + if (DBG) Slog.d(TAG, "Full connection with WifiStateMachine established"); + mWifiChannel = (AsyncChannel) message.obj; + } else { + Slog.e(TAG, "Full connection failure, error = " + message.arg1); + mWifiChannel = null; + } + break; + + case AsyncChannel.CMD_CHANNEL_DISCONNECTED: + if (message.arg1 == AsyncChannel.STATUS_SEND_UNSUCCESSFUL) { + Slog.e(TAG, "Send failed, client connection lost"); + } else { + Slog.e(TAG, "Client connection lost with reason: " + message.arg1); + } + mWifiChannel = null; + break; + + case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: + AsyncChannel ac = new AsyncChannel(); + ac.connect(mContext, getHandler(), message.replyTo); + break; + case WifiStateMachine.WIFI_ENABLE_PENDING: + // Disable p2p operation before we can respond + sendMessage(WifiP2pManager.DISABLE_P2P); + deferMessage(message); + break; + case WifiP2pManager.ENABLE_P2P: + mReplyChannel.replyToMessage(message, WifiP2pManager.ENABLE_P2P_FAILED); + break; + case WifiP2pManager.DISABLE_P2P: + mReplyChannel.replyToMessage(message, WifiP2pManager.DISABLE_P2P_FAILED); + break; + case WifiP2pManager.START_LISTEN_MODE: + mReplyChannel.replyToMessage(message, WifiP2pManager.START_LISTEN_FAILED); + break; + case WifiP2pManager.DISCOVER_PEERS: + mReplyChannel.replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED); + break; + case WifiP2pManager.CANCEL_DISCOVER_PEERS: + mReplyChannel.replyToMessage(message, + WifiP2pManager.CANCEL_DISCOVER_PEERS_FAILED); + break; + case WifiP2pManager.CONNECT: + mReplyChannel.replyToMessage(message, WifiP2pManager.CONNECT_FAILED); + break; + case WifiP2pManager.CANCEL_CONNECT: + mReplyChannel.replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_FAILED); + break; + case WifiP2pManager.REJECT: + mReplyChannel.replyToMessage(message, WifiP2pManager.REJECT_FAILED); + break; + case WifiP2pManager.CREATE_GROUP: + mReplyChannel.replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED); + break; + case WifiP2pManager.REMOVE_GROUP: + mReplyChannel.replyToMessage(message, WifiP2pManager.REMOVE_GROUP_FAILED); + break; + // TODO: fix + case WifiP2pManager.REQUEST_SETTINGS: + case WifiP2pManager.REQUEST_PEERS: + case WifiP2pManager.REQUEST_CONNECTION_STATUS: + break; + // Ignore + case WIFI_DISABLE_USER_ACCEPT: + break; + default: + Slog.e(TAG, "Unhandled message " + message); + return NOT_HANDLED; + } + return HANDLED; + } + } + + class P2pNotSupportedState extends State { + @Override + public boolean processMessage(Message message) { + switch (message.what) { + // Allow Wi-Fi to proceed + case WifiStateMachine.WIFI_ENABLE_PENDING: + mReplyChannel.replyToMessage(message, WIFI_ENABLE_PROCEED); + break; + case WifiP2pManager.ENABLE_P2P: + mReplyChannel.replyToMessage(message, WifiP2pManager.ENABLE_P2P_FAILED, + WifiP2pManager.P2P_UNSUPPORTED); + break; + case WifiP2pManager.DISABLE_P2P: + mReplyChannel.replyToMessage(message, WifiP2pManager.DISABLE_P2P_FAILED, + WifiP2pManager.P2P_UNSUPPORTED); + break; + default: + return NOT_HANDLED; + } + return HANDLED; + } + } + + class P2pDisablingState extends State { + @Override + public void enter() { + if (DBG) Slog.d(TAG, getName()); + // TODO: fix later + WifiNative.unloadDriver(); + transitionTo(mP2pDisabledState); + } + + @Override + public boolean processMessage(Message message) { + if (DBG) Slog.d(TAG, getName() + message.toString()); + switch (message.what) { + case WifiMonitor.SUP_DISCONNECTION_EVENT: + WifiNative.unloadDriver(); + transitionTo(mP2pDisabledState); + break; + default: + return NOT_HANDLED; + } + return HANDLED; + } + } + + + class P2pDisabledState extends State { + @Override + public void enter() { + if (DBG) Slog.d(TAG, getName()); + } + + @Override + public boolean processMessage(Message message) { + if (DBG) Slog.d(TAG, getName() + message.toString()); + switch (message.what) { + case WifiP2pManager.ENABLE_P2P: + mSavedEnableRequestMessage = Message.obtain(message); + OnClickListener listener = new OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + if (which == DialogInterface.BUTTON_POSITIVE) { + sendMessage(WIFI_DISABLE_USER_ACCEPT); + } else { + mReplyChannel.replyToMessage(mSavedEnableRequestMessage, + WifiP2pManager.ENABLE_P2P_FAILED); + } + } + }; + + // Show a user request dialog if we know Wi-Fi client/hotspot is in operation + if (mWifiState != WifiManager.WIFI_STATE_DISABLED || + mWifiApState != WifiManager.WIFI_AP_STATE_DISABLED) { + Resources r = Resources.getSystem(); + AlertDialog dialog = new AlertDialog.Builder(mContext) + .setTitle(r.getString(R.string.wifi_p2p_dialog_title)) + .setMessage(r.getString(R.string.wifi_p2p_turnon_message)) + .setPositiveButton(r.getString(R.string.ok), listener) + .setNegativeButton(r.getString(R.string.cancel), listener) + .create(); + dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); + dialog.show(); + } else { + mWifiChannel.sendMessage(P2P_ENABLE_PENDING); + transitionTo(mWaitForWifiDisableState); + } + break; + case WIFI_DISABLE_USER_ACCEPT: + mWifiChannel.sendMessage(P2P_ENABLE_PENDING); + transitionTo(mWaitForWifiDisableState); + break; + case WifiStateMachine.WIFI_ENABLE_PENDING: + mReplyChannel.replyToMessage(message, WIFI_ENABLE_PROCEED); + break; + default: + return NOT_HANDLED; + } + return HANDLED; + } + } + + class WaitForWifiDisableState extends State { + @Override + public void enter() { + if (DBG) Slog.d(TAG, getName()); + } + + @Override + public boolean processMessage(Message message) { + if (DBG) Slog.d(TAG, getName() + message.toString()); + switch (message.what) { + case WifiStateMachine.P2P_ENABLE_PROCEED: + // TODO: fix this for p2p + if (WifiNative.loadDriver() && + WifiNative.startSupplicant()) { + Slog.d(TAG, "Wi-fi Direct start successful"); + mWifiMonitor.startMonitoring(); + transitionTo(mP2pEnablingState); + } else { + notifyP2pEnableFailure(); + mReplyChannel.replyToMessage(mSavedEnableRequestMessage, + WifiP2pManager.ENABLE_P2P_FAILED); + transitionTo(mP2pDisabledState); + } + break; + default: + return NOT_HANDLED; + } + return HANDLED; + } + } + + class P2pEnablingState extends State { + @Override + public void enter() { + if (DBG) Slog.d(TAG, getName()); + } + + @Override + public boolean processMessage(Message message) { + if (DBG) Slog.d(TAG, getName() + message.toString()); + switch (message.what) { + case WifiMonitor.SUP_CONNECTION_EVENT: + mReplyChannel.replyToMessage(mSavedEnableRequestMessage, + WifiP2pManager.ENABLE_P2P_SUCCEEDED); + transitionTo(mInactiveState); + break; + default: + return NOT_HANDLED; + } + return HANDLED; + } + } + + class P2pEnabledState extends State { + @Override + public void enter() { + if (DBG) Slog.d(TAG, getName()); + sendP2pStateChangedBroadcast(true); + } + + @Override + public boolean processMessage(Message message) { + if (DBG) Slog.d(TAG, getName() + message.toString()); + switch (message.what) { + case WifiP2pManager.DISABLE_P2P: + // TODO: use stopSupplicant after control channel fixed + WifiNative.killSupplicant(); + transitionTo(mP2pDisablingState); + break; + case WifiP2pManager.DISCOVER_PEERS: + int timeout = message.arg1; + WifiNative.p2pFlush(); + WifiNative.p2pFind(timeout); + break; + case WifiP2pManager.REQUEST_PEERS: + mReplyChannel.replyToMessage(message, WifiP2pManager.RESPONSE_PEERS, mPeers); + break; + case WifiMonitor.P2P_DEVICE_FOUND_EVENT: + WifiP2pDevice device = (WifiP2pDevice) message.obj; + mPeers.add(device); + sendP2pPeersChangedBroadcast(); + break; + case WifiMonitor.P2P_DEVICE_LOST_EVENT: + device = (WifiP2pDevice) message.obj; + if (mPeers.remove(device)) sendP2pPeersChangedBroadcast(); + break; + case WifiP2pManager.CONNECT: + if (DBG) Slog.d(TAG, getName() + " sending connect"); + mSavedConnectConfig = (WifiP2pConfig) message.obj; + String pin = WifiNative.p2pConnect(mSavedConnectConfig); + try { + Integer.parseInt(pin); + notifyWpsPin(pin, mSavedConnectConfig.deviceAddress); + } catch (NumberFormatException ignore) { + // do nothing if p2pConnect did not return a pin + } + updateDeviceStatus(mSavedConnectConfig.deviceAddress, Status.INVITED); + sendP2pPeersChangedBroadcast(); + transitionTo(mGroupNegotiationState); + break; + case WifiP2pManager.REJECT: + if (DBG) Slog.d(TAG, getName() + " sending reject"); + WifiNative.p2pReject((String) message.obj); + break; + default: + return NOT_HANDLED; + } + return HANDLED; + } + + @Override + public void exit() { + sendP2pStateChangedBroadcast(false); + } + } + + class InactiveState extends State { + @Override public void enter() { + if (DBG) Slog.d(TAG, getName()); + } + + @Override + public boolean processMessage(Message message) { + if (DBG) Slog.d(TAG, getName() + message.toString()); + switch (message.what) { + case WifiMonitor.P2P_GO_NEGOTIATION_REQUEST_EVENT: + mSavedGoNegotiationConfig = (WifiP2pConfig) message.obj; + notifyP2pGoNegotationRequest(mSavedGoNegotiationConfig); + break; + case WifiP2pManager.CREATE_GROUP: + WifiNative.p2pGroupAdd(); + transitionTo(mGroupNegotiationState); + break; + case WifiMonitor.P2P_INVITATION_RECEIVED_EVENT: + WifiP2pGroup group = (WifiP2pGroup) message.obj; + notifyP2pInvitationReceived(group); + break; + default: + return NOT_HANDLED; + } + return HANDLED; + } + } + + class GroupNegotiationState extends State { + @Override public void enter() { + if (DBG) Slog.d(TAG, getName()); + } + + @Override + public boolean processMessage(Message message) { + if (DBG) Slog.d(TAG, getName() + message.toString()); + switch (message.what) { + // We ignore these right now, since we get a GROUP_STARTED notification + // afterwards + case WifiMonitor.P2P_GO_NEGOTIATION_SUCCESS_EVENT: + case WifiMonitor.P2P_GROUP_FORMATION_SUCCESS_EVENT: + if (DBG) Slog.d(TAG, getName() + " go success"); + break; + case WifiMonitor.P2P_GO_NEGOTIATION_FAILURE_EVENT: + case WifiMonitor.P2P_GROUP_FORMATION_FAILURE_EVENT: + if (DBG) Slog.d(TAG, getName() + " go failure"); + updateDeviceStatus(mSavedConnectConfig.deviceAddress, Status.FAILED); + mSavedConnectConfig = null; + transitionTo(mInactiveState); + break; + case WifiMonitor.P2P_GROUP_STARTED_EVENT: + mGroup = (WifiP2pGroup) message.obj; + if (DBG) Slog.d(TAG, getName() + " group started"); + // If this device is GO, do nothing since there is a follow up + // AP_STA_CONNECTED event + if (!mGroup.isGroupOwner()) { + WifiP2pDevice groupOwner = mGroup.getOwner(); + updateDeviceStatus(groupOwner.deviceAddress, Status.CONNECTED); + sendP2pPeersChangedBroadcast(); + } + transitionTo(mGroupCreatedState); + break; + case WifiP2pManager.CANCEL_CONNECT: + // TODO: fix + break; + default: + return NOT_HANDLED; + } + return HANDLED; + } + } + + class GroupCreatedState extends State { + @Override + public void enter() { + if (DBG) Slog.d(TAG, getName()); + } + + @Override + public boolean processMessage(Message message) { + if (DBG) Slog.d(TAG, getName() + message.toString()); + switch (message.what) { + case WifiMonitor.AP_STA_CONNECTED_EVENT: + String address = (String) message.obj; + mGroup.addClient(address); + updateDeviceStatus(address, Status.CONNECTED); + if (DBG) Slog.d(TAG, getName() + " ap sta connected"); + sendP2pPeersChangedBroadcast(); + break; + case WifiMonitor.AP_STA_DISCONNECTED_EVENT: + address = (String) message.obj; + updateDeviceStatus(address, Status.AVAILABLE); + if (mGroup.removeClient(address)) { + if (DBG) Slog.d(TAG, "Removed client " + address); + if (mGroup.isClientListEmpty()) { + Slog.d(TAG, "Client list empty, killing p2p connection"); + sendMessage(WifiP2pManager.REMOVE_GROUP); + } else { + // Just send a notification + sendP2pPeersChangedBroadcast(); + } + } else { + if (DBG) Slog.d(TAG, "Failed to remove client " + address); + for (WifiP2pDevice c : mGroup.getClientList()) { + if (DBG) Slog.d(TAG,"client " + c.deviceAddress); + } + } + if (DBG) Slog.e(TAG, getName() + " ap sta disconnected"); + break; + // Disconnect & remove group have same effect when connected + case WifiP2pManager.CANCEL_CONNECT: + case WifiP2pManager.REMOVE_GROUP: + if (DBG) Slog.e(TAG, getName() + " remove group"); + WifiNative.p2pFlush(); + WifiNative.p2pGroupRemove(mGroup.getInterface()); + break; + case WifiMonitor.P2P_GROUP_REMOVED_EVENT: + if (DBG) Slog.e(TAG, getName() + " group removed"); + Collection <WifiP2pDevice> devices = mGroup.getClientList(); + boolean changed = false; + for (WifiP2pDevice d : mPeers.getDeviceList()) { + if (devices.contains(d) || mGroup.getOwner().equals(d)) { + d.status = Status.AVAILABLE; + changed = true; + } + } + mGroup = null; + if (changed) sendP2pPeersChangedBroadcast(); + transitionTo(mInactiveState); + break; + case WifiMonitor.P2P_DEVICE_LOST_EVENT: + WifiP2pDevice device = (WifiP2pDevice) message.obj; + if (device.equals(mGroup.getOwner())) { + Slog.d(TAG, "Lost the group owner, killing p2p connection"); + sendMessage(WifiP2pManager.REMOVE_GROUP); + } else if (mGroup.removeClient(device) && mGroup.isClientListEmpty()) { + Slog.d(TAG, "Client list empty, killing p2p connection"); + sendMessage(WifiP2pManager.REMOVE_GROUP); + } + return NOT_HANDLED; // Do the regular device lost handling + case WifiP2pManager.DISABLE_P2P: + sendMessage(WifiP2pManager.REMOVE_GROUP); + deferMessage(message); + break; + case WifiP2pManager.DISCOVER_PEERS: + int timeout = message.arg1; + WifiNative.p2pFind(timeout); + break; + case WifiP2pManager.CONNECT: + WifiP2pConfig config = (WifiP2pConfig) message.obj; + Slog.d(TAG, "Inviting device : " + config.deviceAddress); + WifiNative.p2pInvite(mGroup, config.deviceAddress); + updateDeviceStatus(config.deviceAddress, Status.INVITED); + sendP2pPeersChangedBroadcast(); + // TODO: figure out updating the status to declined when invitation is rejected + break; + case WifiMonitor.P2P_INVITATION_RESULT_EVENT: + Slog.d(TAG,"===> INVITATION RESULT EVENT : " + message.obj); + break; + case WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT: + notifyP2pProvDiscPbcRequest((WifiP2pDevice) message.obj); + break; + case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT: + notifyP2pProvDiscPinRequest((WifiP2pDevice) message.obj); + break; + case WifiP2pManager.WPS_PBC: + WifiNative.p2pWpsPbc(); + break; + case WifiP2pManager.WPS_PIN: + WifiNative.p2pWpsPin((String) message.obj); + break; + default: + return NOT_HANDLED; + } + return HANDLED; + } + } + + private void sendP2pStateChangedBroadcast(boolean enabled) { + final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + if (enabled) { + intent.putExtra(WifiP2pManager.EXTRA_WIFI_STATE, + WifiP2pManager.WIFI_P2P_STATE_ENABLED); + } else { + intent.putExtra(WifiP2pManager.EXTRA_WIFI_STATE, + WifiP2pManager.WIFI_P2P_STATE_DISABLED); + } + mContext.sendStickyBroadcast(intent); + } + + private void sendP2pPeersChangedBroadcast() { + final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + mContext.sendBroadcast(intent); + } + + private void notifyP2pEnableFailure() { + Resources r = Resources.getSystem(); + AlertDialog dialog = new AlertDialog.Builder(mContext) + .setTitle(r.getString(R.string.wifi_p2p_dialog_title)) + .setMessage(r.getString(R.string.wifi_p2p_failed_message)) + .setPositiveButton(r.getString(R.string.ok), null) + .create(); + dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); + dialog.show(); + } + + private void notifyWpsPin(String pin, String peerAddress) { + Resources r = Resources.getSystem(); + AlertDialog dialog = new AlertDialog.Builder(mContext) + .setTitle(r.getString(R.string.wifi_p2p_dialog_title)) + .setMessage(r.getString(R.string.wifi_p2p_pin_display_message, pin, peerAddress)) + .setPositiveButton(r.getString(R.string.ok), null) + .create(); + dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); + dialog.show(); + } + + private void notifyP2pGoNegotationRequest(WifiP2pConfig config) { + Resources r = Resources.getSystem(); + WpsConfiguration wpsConfig = config.wpsConfig; + final View textEntryView = LayoutInflater.from(mContext) + .inflate(R.layout.wifi_p2p_go_negotiation_request_alert, null); + final EditText pin = (EditText) textEntryView .findViewById(R.id.wifi_p2p_wps_pin); + + AlertDialog dialog = new AlertDialog.Builder(mContext) + .setTitle(r.getString(R.string.wifi_p2p_dialog_title)) + .setView(textEntryView) + .setPositiveButton(r.getString(R.string.ok), new OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + if (DBG) Slog.d(TAG, getName() + " connect " + pin.getText()); + mSavedGoNegotiationConfig.wpsConfig.setup = Setup.KEYPAD; + mSavedGoNegotiationConfig.wpsConfig.pin = pin.getText().toString(); + sendMessage(WifiP2pManager.CONNECT, mSavedGoNegotiationConfig); + mSavedGoNegotiationConfig = null; + } + }) + .setNegativeButton(r.getString(R.string.cancel), new OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + if (DBG) Slog.d(TAG, getName() + " reject"); + sendMessage(WifiP2pManager.REJECT, + mSavedGoNegotiationConfig.deviceAddress); + mSavedGoNegotiationConfig = null; + } + }) + .create(); + + if (wpsConfig.setup == Setup.PBC) { + pin.setVisibility(View.GONE); + dialog.setMessage(r.getString(R.string.wifi_p2p_pbc_go_negotiation_request_message, + config.deviceAddress)); + } else { + dialog.setMessage(r.getString(R.string.wifi_p2p_pin_go_negotiation_request_message, + config.deviceAddress)); + } + + dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); + dialog.show(); + } + + private void notifyP2pProvDiscPbcRequest(WifiP2pDevice peer) { + Resources r = Resources.getSystem(); + final View textEntryView = LayoutInflater.from(mContext) + .inflate(R.layout.wifi_p2p_go_negotiation_request_alert, null); + final EditText pin = (EditText) textEntryView .findViewById(R.id.wifi_p2p_wps_pin); + + AlertDialog dialog = new AlertDialog.Builder(mContext) + .setTitle(r.getString(R.string.wifi_p2p_dialog_title)) + .setView(textEntryView) + .setPositiveButton(r.getString(R.string.ok), new OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + if (DBG) Slog.d(TAG, getName() + " wps_pbc"); + sendMessage(WifiP2pManager.WPS_PBC); + } + }) + .setNegativeButton(r.getString(R.string.cancel), null) + .create(); + + pin.setVisibility(View.GONE); + dialog.setMessage(r.getString(R.string.wifi_p2p_pbc_go_negotiation_request_message, + peer.deviceAddress)); + + dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); + dialog.show(); + } + + private void notifyP2pProvDiscPinRequest(WifiP2pDevice peer) { + Resources r = Resources.getSystem(); + final View textEntryView = LayoutInflater.from(mContext) + .inflate(R.layout.wifi_p2p_go_negotiation_request_alert, null); + final EditText pin = (EditText) textEntryView .findViewById(R.id.wifi_p2p_wps_pin); + + AlertDialog dialog = new AlertDialog.Builder(mContext) + .setTitle(r.getString(R.string.wifi_p2p_dialog_title)) + .setView(textEntryView) + .setPositiveButton(r.getString(R.string.ok), new OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + if (DBG) Slog.d(TAG, getName() + " wps_pin"); + sendMessage(WifiP2pManager.WPS_PIN, pin.getText().toString()); + } + }) + .setNegativeButton(r.getString(R.string.cancel), null) + .create(); + + dialog.setMessage(r.getString(R.string.wifi_p2p_pin_go_negotiation_request_message, + peer.deviceAddress)); + + dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); + dialog.show(); + } + + private void notifyP2pInvitationReceived(WifiP2pGroup group) { + mSavedP2pGroup = group; + Resources r = Resources.getSystem(); + final View textEntryView = LayoutInflater.from(mContext) + .inflate(R.layout.wifi_p2p_go_negotiation_request_alert, null); + final EditText pin = (EditText) textEntryView .findViewById(R.id.wifi_p2p_wps_pin); + + AlertDialog dialog = new AlertDialog.Builder(mContext) + .setTitle(r.getString(R.string.wifi_p2p_dialog_title)) + .setView(textEntryView) + .setPositiveButton(r.getString(R.string.ok), new OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + WifiP2pConfig config = new WifiP2pConfig(); + config.deviceAddress = mSavedP2pGroup.getOwner().deviceAddress; + config.joinExistingGroup = true; + if (DBG) Slog.d(TAG, getName() + " connect to invited group"); + sendMessage(WifiP2pManager.CONNECT, config); + mSavedP2pGroup = null; + } + }) + .setNegativeButton(r.getString(R.string.cancel), null) + .create(); + + pin.setVisibility(View.GONE); + dialog.setMessage(r.getString(R.string.wifi_p2p_pbc_go_negotiation_request_message, + group.getOwner().deviceAddress)); + + dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); + dialog.show(); + } + + private void updateDeviceStatus(String deviceAddress, Status status) { + for (WifiP2pDevice d : mPeers.getDeviceList()) { + // TODO: fix later + // if (d.deviceAddress.equals(deviceAddress)) { + if (d.deviceAddress.startsWith(deviceAddress.substring(0, 8))) { + d.status = status; + } + } + } + } +} diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pStatus.aidl b/wifi/java/android/net/wifi/p2p/WifiP2pStatus.aidl new file mode 100644 index 0000000..7bab5d3 --- /dev/null +++ b/wifi/java/android/net/wifi/p2p/WifiP2pStatus.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2011, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.wifi.p2p; + +parcelable WifiP2pStatus; diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pStatus.java b/wifi/java/android/net/wifi/p2p/WifiP2pStatus.java new file mode 100644 index 0000000..1c9b76c --- /dev/null +++ b/wifi/java/android/net/wifi/p2p/WifiP2pStatus.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.wifi.p2p; + +import android.os.Parcelable; +import android.os.Parcel; + +/** + * A class representing Wi-fi P2p status + * @hide + */ +public class WifiP2pStatus implements Parcelable { + + //Comes from the wpa_supplicant + enum p2p_status_code { + SUCCESS, + FAIL_INFO_CURRENTLY_UNAVAILABLE, + FAIL_INCOMPATIBLE_PARAMS, + FAIL_LIMIT_REACHED, + FAIL_INVALID_PARAMS, + FAIL_UNABLE_TO_ACCOMMODATE, + FAIL_PREV_PROTOCOL_ERROR, + FAIL_NO_COMMON_CHANNELS, + FAIL_UNKNOWN_GROUP, + FAIL_BOTH_GO_INTENT_15, + FAIL_INCOMPATIBLE_PROV_METHOD, + FAIL_REJECTED_BY_USER + }; + + public WifiP2pStatus() { + } + + //TODO: add support + public String toString() { + StringBuffer sbuf = new StringBuffer(); + return sbuf.toString(); + } + + /** Implement the Parcelable interface {@hide} */ + public int describeContents() { + return 0; + } + + /** copy constructor {@hide} */ + //TODO: implement + public WifiP2pStatus(WifiP2pStatus source) { + if (source != null) { + } + } + + /** Implement the Parcelable interface {@hide} */ + // STOPSHIP: implement + public void writeToParcel(Parcel dest, int flags) { + } + + /** Implement the Parcelable interface {@hide} */ + public static final Creator<WifiP2pStatus> CREATOR = + new Creator<WifiP2pStatus>() { + public WifiP2pStatus createFromParcel(Parcel in) { + WifiP2pStatus status = new WifiP2pStatus(); + return status; + } + + public WifiP2pStatus[] newArray(int size) { + return new WifiP2pStatus[size]; + } + }; +} |