Merge "P2p cleanup"
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
index b1501ed..482d9cb 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
@@ -30,7 +30,7 @@
     /**
      * The device MAC address uniquely identifies a Wi-Fi p2p device
      */
-    public String deviceAddress;
+    public String deviceAddress = "";
 
     /**
      * Wi-Fi Protected Setup information
@@ -60,6 +60,10 @@
         wps.setup = WpsInfo.PBC;
     }
 
+    void invalidate() {
+        deviceAddress = "";
+    }
+
     /** P2P-GO-NEG-REQUEST 42:fc:89:a8:96:09 dev_passwd_id=4 {@hide}*/
     public WifiP2pConfig(String supplicantEvent) throws IllegalArgumentException {
         String[] tokens = supplicantEvent.split(" ");
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pDeviceList.java b/wifi/java/android/net/wifi/p2p/WifiP2pDeviceList.java
index f7bceac..4f40ebc 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pDeviceList.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pDeviceList.java
@@ -58,6 +58,19 @@
         }
     }
 
+    private void validateDevice(WifiP2pDevice device) {
+        if (device == null) throw new IllegalArgumentException("Null device");
+        if (TextUtils.isEmpty(device.deviceAddress)) {
+            throw new IllegalArgumentException("Empty deviceAddress");
+        }
+    }
+
+    private void validateDeviceAddress(String deviceAddress) {
+        if (TextUtils.isEmpty(deviceAddress)) {
+            throw new IllegalArgumentException("Empty deviceAddress");
+        }
+    }
+
     /** Clear the list @hide */
     public boolean clear() {
         if (mDevices.isEmpty()) return false;
@@ -72,14 +85,13 @@
      * @hide
      */
     public void update(WifiP2pDevice device) {
-        if (device == null || device.deviceAddress == null) return;
         updateSupplicantDetails(device);
         mDevices.get(device.deviceAddress).status = device.status;
     }
 
     /** Only updates details fetched from the supplicant @hide */
     void updateSupplicantDetails(WifiP2pDevice device) {
-        if (device == null || device.deviceAddress == null) return;
+        validateDevice(device);
         WifiP2pDevice d = mDevices.get(device.deviceAddress);
         if (d != null) {
             d.deviceName = device.deviceName;
@@ -97,7 +109,7 @@
 
     /** @hide */
     void updateGroupCapability(String deviceAddress, int groupCapab) {
-        if (TextUtils.isEmpty(deviceAddress)) return;
+        validateDeviceAddress(deviceAddress);
         WifiP2pDevice d = mDevices.get(deviceAddress);
         if (d != null) {
             d.groupCapability = groupCapab;
@@ -106,7 +118,7 @@
 
     /** @hide */
     void updateStatus(String deviceAddress, int status) {
-        if (TextUtils.isEmpty(deviceAddress)) return;
+        validateDeviceAddress(deviceAddress);
         WifiP2pDevice d = mDevices.get(deviceAddress);
         if (d != null) {
             d.status = status;
@@ -119,14 +131,13 @@
      * @return WifiP2pDevice device found, or null if none found
      */
     public WifiP2pDevice get(String deviceAddress) {
-        if (deviceAddress == null) return null;
-
+        validateDeviceAddress(deviceAddress);
         return mDevices.get(deviceAddress);
     }
 
     /** @hide */
     public boolean remove(WifiP2pDevice device) {
-        if (device == null || device.deviceAddress == null) return false;
+        validateDevice(device);
         return mDevices.remove(device.deviceAddress) != null;
     }
 
@@ -137,7 +148,7 @@
      * @hide
      */
     public WifiP2pDevice remove(String deviceAddress) {
-        if (deviceAddress == null) return null;
+        validateDeviceAddress(deviceAddress);
         return mDevices.remove(deviceAddress);
     }
 
@@ -157,11 +168,12 @@
 
     /** @hide */
     public boolean isGroupOwner(String deviceAddress) {
-        if (deviceAddress != null) {
-            WifiP2pDevice device = mDevices.get(deviceAddress);
-            if (device != null) return device.isGroupOwner();
+        validateDeviceAddress(deviceAddress);
+        WifiP2pDevice device = mDevices.get(deviceAddress);
+        if (device == null) {
+            throw new IllegalArgumentException("Device not found " + deviceAddress);
         }
-        return false;
+        return device.isGroupOwner();
     }
 
     public String toString() {
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index 5bd0349..2e80064 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.IConnectivityManager;
+import android.net.wifi.WpsInfo;
 import android.net.wifi.p2p.nsd.WifiP2pDnsSdServiceInfo;
 import android.net.wifi.p2p.nsd.WifiP2pDnsSdServiceResponse;
 import android.net.wifi.p2p.nsd.WifiP2pServiceInfo;
@@ -37,6 +38,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.WorkSource;
+import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.internal.util.AsyncChannel;
@@ -421,6 +423,13 @@
     /** @hide */
     public static final int SET_WFD_INFO_SUCCEEDED                  = BASE + 61;
 
+    /** @hide */
+    public static final int START_WPS                               = BASE + 62;
+    /** @hide */
+    public static final int START_WPS_FAILED                        = BASE + 63;
+    /** @hide */
+    public static final int START_WPS_SUCCEEDED                     = BASE + 64;
+
     /**
      * Create a new WifiP2pManager instance. Applications use
      * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
@@ -641,70 +650,72 @@
                         }
                         break;
                     /* ActionListeners grouped together */
-                    case WifiP2pManager.DISCOVER_PEERS_FAILED:
-                    case WifiP2pManager.STOP_DISCOVERY_FAILED:
-                    case WifiP2pManager.DISCOVER_SERVICES_FAILED:
-                    case WifiP2pManager.CONNECT_FAILED:
-                    case WifiP2pManager.CANCEL_CONNECT_FAILED:
-                    case WifiP2pManager.CREATE_GROUP_FAILED:
-                    case WifiP2pManager.REMOVE_GROUP_FAILED:
-                    case WifiP2pManager.ADD_LOCAL_SERVICE_FAILED:
-                    case WifiP2pManager.REMOVE_LOCAL_SERVICE_FAILED:
-                    case WifiP2pManager.CLEAR_LOCAL_SERVICES_FAILED:
-                    case WifiP2pManager.ADD_SERVICE_REQUEST_FAILED:
-                    case WifiP2pManager.REMOVE_SERVICE_REQUEST_FAILED:
-                    case WifiP2pManager.CLEAR_SERVICE_REQUESTS_FAILED:
-                    case WifiP2pManager.SET_DEVICE_NAME_FAILED:
-                    case WifiP2pManager.DELETE_PERSISTENT_GROUP_FAILED:
-                    case WifiP2pManager.SET_WFD_INFO_FAILED:
+                    case DISCOVER_PEERS_FAILED:
+                    case STOP_DISCOVERY_FAILED:
+                    case DISCOVER_SERVICES_FAILED:
+                    case CONNECT_FAILED:
+                    case CANCEL_CONNECT_FAILED:
+                    case CREATE_GROUP_FAILED:
+                    case REMOVE_GROUP_FAILED:
+                    case ADD_LOCAL_SERVICE_FAILED:
+                    case REMOVE_LOCAL_SERVICE_FAILED:
+                    case CLEAR_LOCAL_SERVICES_FAILED:
+                    case ADD_SERVICE_REQUEST_FAILED:
+                    case REMOVE_SERVICE_REQUEST_FAILED:
+                    case CLEAR_SERVICE_REQUESTS_FAILED:
+                    case SET_DEVICE_NAME_FAILED:
+                    case DELETE_PERSISTENT_GROUP_FAILED:
+                    case SET_WFD_INFO_FAILED:
+                    case START_WPS_FAILED:
                         if (listener != null) {
                             ((ActionListener) listener).onFailure(message.arg1);
                         }
                         break;
                     /* ActionListeners grouped together */
-                    case WifiP2pManager.DISCOVER_PEERS_SUCCEEDED:
-                    case WifiP2pManager.STOP_DISCOVERY_SUCCEEDED:
-                    case WifiP2pManager.DISCOVER_SERVICES_SUCCEEDED:
-                    case WifiP2pManager.CONNECT_SUCCEEDED:
-                    case WifiP2pManager.CANCEL_CONNECT_SUCCEEDED:
-                    case WifiP2pManager.CREATE_GROUP_SUCCEEDED:
-                    case WifiP2pManager.REMOVE_GROUP_SUCCEEDED:
-                    case WifiP2pManager.ADD_LOCAL_SERVICE_SUCCEEDED:
-                    case WifiP2pManager.REMOVE_LOCAL_SERVICE_SUCCEEDED:
-                    case WifiP2pManager.CLEAR_LOCAL_SERVICES_SUCCEEDED:
-                    case WifiP2pManager.ADD_SERVICE_REQUEST_SUCCEEDED:
-                    case WifiP2pManager.REMOVE_SERVICE_REQUEST_SUCCEEDED:
-                    case WifiP2pManager.CLEAR_SERVICE_REQUESTS_SUCCEEDED:
-                    case WifiP2pManager.SET_DEVICE_NAME_SUCCEEDED:
-                    case WifiP2pManager.DELETE_PERSISTENT_GROUP_SUCCEEDED:
-                    case WifiP2pManager.SET_WFD_INFO_SUCCEEDED:
+                    case DISCOVER_PEERS_SUCCEEDED:
+                    case STOP_DISCOVERY_SUCCEEDED:
+                    case DISCOVER_SERVICES_SUCCEEDED:
+                    case CONNECT_SUCCEEDED:
+                    case CANCEL_CONNECT_SUCCEEDED:
+                    case CREATE_GROUP_SUCCEEDED:
+                    case REMOVE_GROUP_SUCCEEDED:
+                    case ADD_LOCAL_SERVICE_SUCCEEDED:
+                    case REMOVE_LOCAL_SERVICE_SUCCEEDED:
+                    case CLEAR_LOCAL_SERVICES_SUCCEEDED:
+                    case ADD_SERVICE_REQUEST_SUCCEEDED:
+                    case REMOVE_SERVICE_REQUEST_SUCCEEDED:
+                    case CLEAR_SERVICE_REQUESTS_SUCCEEDED:
+                    case SET_DEVICE_NAME_SUCCEEDED:
+                    case DELETE_PERSISTENT_GROUP_SUCCEEDED:
+                    case SET_WFD_INFO_SUCCEEDED:
+                    case START_WPS_SUCCEEDED:
                         if (listener != null) {
                             ((ActionListener) listener).onSuccess();
                         }
                         break;
-                    case WifiP2pManager.RESPONSE_PEERS:
+                    case RESPONSE_PEERS:
                         WifiP2pDeviceList peers = (WifiP2pDeviceList) message.obj;
                         if (listener != null) {
                             ((PeerListListener) listener).onPeersAvailable(peers);
                         }
                         break;
-                    case WifiP2pManager.RESPONSE_CONNECTION_INFO:
+                    case RESPONSE_CONNECTION_INFO:
                         WifiP2pInfo wifiP2pInfo = (WifiP2pInfo) message.obj;
                         if (listener != null) {
                             ((ConnectionInfoListener) listener).onConnectionInfoAvailable(wifiP2pInfo);
                         }
                         break;
-                    case WifiP2pManager.RESPONSE_GROUP_INFO:
+                    case RESPONSE_GROUP_INFO:
                         WifiP2pGroup group = (WifiP2pGroup) message.obj;
                         if (listener != null) {
                             ((GroupInfoListener) listener).onGroupInfoAvailable(group);
                         }
                         break;
-                    case WifiP2pManager.RESPONSE_SERVICE:
+                    case RESPONSE_SERVICE:
                         WifiP2pServiceResponse resp = (WifiP2pServiceResponse) message.obj;
                         handleServiceResponse(resp);
                         break;
-                    case WifiP2pManager.RESPONSE_PERSISTENT_GROUP_INFO:
+                    case RESPONSE_PERSISTENT_GROUP_INFO:
                         WifiP2pGroupList groups = (WifiP2pGroupList) message.obj;
                         if (listener != null) {
                             ((PersistentGroupInfoListener) listener).
@@ -790,6 +801,13 @@
         if (req == null) throw new IllegalArgumentException("service request is null");
     }
 
+    private static void checkP2pConfig(WifiP2pConfig c) {
+        if (c == null) throw new IllegalArgumentException("config cannot be null");
+        if (TextUtils.isEmpty(c.deviceAddress)) {
+            throw new IllegalArgumentException("deviceAddress cannot be empty");
+        }
+    }
+
     /**
      * Registers the application with the Wi-Fi framework. This function
      * must be the first to be called before any p2p operations are performed.
@@ -876,6 +894,7 @@
      */
     public void connect(Channel c, WifiP2pConfig config, ActionListener listener) {
         checkChannel(c);
+        checkP2pConfig(config);
         c.mAsyncChannel.sendMessage(CONNECT, 0, c.putListener(listener), config);
     }
 
@@ -937,6 +956,21 @@
     }
 
     /**
+     * Start a Wi-Fi Protected Setup (WPS) session.
+     *
+     * <p> The function call immediately returns after sending a request to start a
+     * WPS session. Currently, this is only valid if the current device is running
+     * as a group owner to allow any new clients to join the group. The application
+     * is notified of a success or failure to initiate WPS through listener callbacks
+     * {@link ActionListener#onSuccess} or {@link ActionListener#onFailure}.
+     * @hide
+     */
+    public void startWps(Channel c, WpsInfo wps, ActionListener listener) {
+        checkChannel(c);
+        c.mAsyncChannel.sendMessage(START_WPS, 0, c.putListener(listener), wps);
+    }
+
+    /**
      * Register a local service for service discovery. If a local service is registered,
      * the framework automatically responds to a service discovery request from a peer.
      *
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pService.java b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
index c1d177d..debf988 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pService.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
@@ -114,16 +114,9 @@
     private static final Boolean JOIN_GROUP = true;
     private static final Boolean FORM_GROUP = false;
 
-    private static final Boolean TRY_REINVOCATION = true;;
-    private static final Boolean NO_REINVOCATION = false;
-
     private static final Boolean RELOAD = true;
     private static final Boolean NO_RELOAD = false;
 
-    private static final int CONNECT_FAILURE = -1;
-    private static final int CONNECT_SUCCESS = 0;
-    private static final int NEEDS_PROVISION_REQ = 1;
-
     /* Two minutes comes from the wpa_supplicant setting */
     private static final int GROUP_CREATING_WAIT_TIME_MS = 120 * 1000;
     private static int mGroupCreatingTimeoutIndex = 0;
@@ -361,8 +354,10 @@
         // Inactive is when p2p is enabled with no connectivity
         private InactiveState mInactiveState = new InactiveState();
         private GroupCreatingState mGroupCreatingState = new GroupCreatingState();
-        private UserAuthorizingInvitationState mUserAuthorizingInvitationState
-                = new UserAuthorizingInvitationState();
+        private UserAuthorizingInviteRequestState mUserAuthorizingInviteRequestState
+                = new UserAuthorizingInviteRequestState();
+        private UserAuthorizingNegotiationRequestState mUserAuthorizingNegotiationRequestState
+                = new UserAuthorizingNegotiationRequestState();
         private ProvisionDiscoveryState mProvisionDiscoveryState = new ProvisionDiscoveryState();
         private GroupNegotiationState mGroupNegotiationState = new GroupNegotiationState();
         private FrequencyConflictState mFrequencyConflictState =new FrequencyConflictState();
@@ -397,8 +392,10 @@
         private final WifiP2pInfo mWifiP2pInfo = new WifiP2pInfo();
         private WifiP2pGroup mGroup;
 
-        // Saved WifiP2pConfig for a peer connection
-        private WifiP2pConfig mSavedPeerConfig;
+        // Saved WifiP2pConfig for an ongoing peer connection. This will never be null.
+        // The deviceAddress will be an empty string when the device is inactive
+        // or if it is connected without any ongoing join request
+        private WifiP2pConfig mSavedPeerConfig = new WifiP2pConfig();
 
         // Saved WifiP2pGroup from invitation request
         private WifiP2pGroup mSavedP2pGroup;
@@ -414,7 +411,8 @@
                 addState(mP2pEnabledState, mDefaultState);
                     addState(mInactiveState, mP2pEnabledState);
                     addState(mGroupCreatingState, mP2pEnabledState);
-                        addState(mUserAuthorizingInvitationState, mGroupCreatingState);
+                        addState(mUserAuthorizingInviteRequestState, mGroupCreatingState);
+                        addState(mUserAuthorizingNegotiationRequestState, mGroupCreatingState);
                         addState(mProvisionDiscoveryState, mGroupCreatingState);
                         addState(mGroupNegotiationState, mGroupCreatingState);
                         addState(mFrequencyConflictState, mGroupCreatingState);
@@ -541,6 +539,10 @@
                     replyToMessage(message, WifiP2pManager.RESPONSE_PERSISTENT_GROUP_INFO,
                             new WifiP2pGroupList(mGroups, null));
                     break;
+                case WifiP2pManager.START_WPS:
+                    replyToMessage(message, WifiP2pManager.START_WPS_FAILED,
+                        WifiP2pManager.BUSY);
+                    break;
                     // Ignore
                 case WifiMonitor.P2P_INVITATION_RESULT_EVENT:
                 case WifiMonitor.SCAN_RESULTS_EVENT:
@@ -667,6 +669,10 @@
                     replyToMessage(message, WifiP2pManager.SET_WFD_INFO_FAILED,
                             WifiP2pManager.P2P_UNSUPPORTED);
                     break;
+                case WifiP2pManager.START_WPS:
+                    replyToMessage(message, WifiP2pManager.START_WPS_FAILED,
+                            WifiP2pManager.P2P_UNSUPPORTED);
+                    break;
                default:
                     return NOT_HANDLED;
             }
@@ -943,9 +949,7 @@
         @Override
         public void enter() {
             if (DBG) logd(getName());
-            //Start listening every time we get inactive
-            //TODO: Fix listen after driver behavior is fixed
-            //mWifiNative.p2pListen();
+            mSavedPeerConfig.invalidate();
         }
 
         @Override
@@ -955,25 +959,23 @@
                 case WifiP2pManager.CONNECT:
                     if (DBG) logd(getName() + " sending connect");
                     WifiP2pConfig config = (WifiP2pConfig) message.obj;
-                    mAutonomousGroup = false;
-
-                    /* Update group capability before connect */
-                    int gc = mWifiNative.getGroupCapability(config.deviceAddress);
-                    mPeers.updateGroupCapability(config.deviceAddress, gc);
-                    int connectRet = connect(config, TRY_REINVOCATION);
-                    if (connectRet == CONNECT_FAILURE) {
+                    if (isConfigInvalid(config)) {
+                        loge("Dropping connect requeset " + config);
                         replyToMessage(message, WifiP2pManager.CONNECT_FAILED);
                         break;
                     }
+
+                    mAutonomousGroup = false;
+                    mWifiNative.p2pStopFind();
+                    if (reinvokePersistentGroup(config)) {
+                        transitionTo(mGroupNegotiationState);
+                    } else {
+                        transitionTo(mProvisionDiscoveryState);
+                    }
+                    mSavedPeerConfig = config;
                     mPeers.updateStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.INVITED);
                     sendPeersChangedBroadcast();
                     replyToMessage(message, WifiP2pManager.CONNECT_SUCCEEDED);
-                    if (connectRet == NEEDS_PROVISION_REQ) {
-                        if (DBG) logd("Sending prov disc");
-                        transitionTo(mProvisionDiscoveryState);
-                        break;
-                    }
-                    transitionTo(mGroupNegotiationState);
                     break;
                 case WifiP2pManager.STOP_DISCOVERY:
                     if (mWifiNative.p2pStopFind()) {
@@ -988,22 +990,33 @@
                     }
                     break;
                 case WifiMonitor.P2P_GO_NEGOTIATION_REQUEST_EVENT:
-                    mSavedPeerConfig = (WifiP2pConfig) message.obj;
+                    config = (WifiP2pConfig) message.obj;
+                    if (isConfigInvalid(config)) {
+                        loge("Dropping GO neg request " + config);
+                        break;
+                    }
+                    mSavedPeerConfig = config;
                     mAutonomousGroup = false;
                     mJoinExistingGroup = false;
-                    transitionTo(mUserAuthorizingInvitationState);
+                    transitionTo(mUserAuthorizingNegotiationRequestState);
                     break;
                 case WifiMonitor.P2P_INVITATION_RECEIVED_EVENT:
                     WifiP2pGroup group = (WifiP2pGroup) message.obj;
                     WifiP2pDevice owner = group.getOwner();
 
                     if (owner == null) {
-                        if (DBG) loge("Ignored invitation from null owner");
+                        loge("Ignored invitation from null owner");
                         break;
                     }
 
-                    mSavedPeerConfig = new WifiP2pConfig();
-                    mSavedPeerConfig.deviceAddress = group.getOwner().deviceAddress;
+                    config = new WifiP2pConfig();
+                    config.deviceAddress = group.getOwner().deviceAddress;
+
+                    if (isConfigInvalid(config)) {
+                        loge("Dropping invitation request " + config);
+                        break;
+                    }
+                    mSavedPeerConfig = config;
 
                     //Check if we have the owner in peer list and use appropriate
                     //wps method. Default is to use PBC.
@@ -1019,7 +1032,7 @@
 
                     mAutonomousGroup = false;
                     mJoinExistingGroup = true;
-                    transitionTo(mUserAuthorizingInvitationState);
+                    transitionTo(mUserAuthorizingInviteRequestState);
                     break;
                 case WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT:
                 case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:
@@ -1097,11 +1110,7 @@
                     break;
                 case WifiMonitor.P2P_DEVICE_LOST_EVENT:
                     WifiP2pDevice device = (WifiP2pDevice) message.obj;
-
-                    // If we lose a device during an autonomous group creation,
-                    // mSavedPeerConfig can be empty
-                    if (mSavedPeerConfig != null &&
-                            !mSavedPeerConfig.deviceAddress.equals(device.deviceAddress)) {
+                    if (!mSavedPeerConfig.deviceAddress.equals(device.deviceAddress)) {
                         if (DBG) {
                             logd("mSavedPeerConfig " + mSavedPeerConfig.deviceAddress +
                                 "device " + device.deviceAddress);
@@ -1137,7 +1146,7 @@
         }
     }
 
-    class UserAuthorizingInvitationState extends State {
+    class UserAuthorizingNegotiationRequestState extends State {
         @Override
         public void enter() {
             if (DBG) logd(getName());
@@ -1150,18 +1159,14 @@
             boolean ret = HANDLED;
             switch (message.what) {
                 case PEER_CONNECTION_USER_ACCEPT:
-                    if (connect(mSavedPeerConfig, TRY_REINVOCATION) == CONNECT_FAILURE) {
-                        handleGroupCreationFailure();
-                        transitionTo(mInactiveState);
-                        break;
-                    }
+                    mWifiNative.p2pStopFind();
+                    p2pConnectWithPinDisplay(mSavedPeerConfig);
                     mPeers.updateStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.INVITED);
                     sendPeersChangedBroadcast();
                     transitionTo(mGroupNegotiationState);
-                    break;
+                   break;
                 case PEER_CONNECTION_USER_REJECT:
-                    if (DBG) logd("User rejected invitation " + mSavedPeerConfig);
-                    mSavedPeerConfig = null;
+                    if (DBG) logd("User rejected negotiation " + mSavedPeerConfig);
                     transitionTo(mInactiveState);
                     break;
                 default:
@@ -1176,6 +1181,46 @@
         }
     }
 
+    class UserAuthorizingInviteRequestState extends State {
+        @Override
+        public void enter() {
+            if (DBG) logd(getName());
+            notifyInvitationReceived();
+        }
+
+        @Override
+        public boolean processMessage(Message message) {
+            if (DBG) logd(getName() + message.toString());
+            boolean ret = HANDLED;
+            switch (message.what) {
+                case PEER_CONNECTION_USER_ACCEPT:
+                    mWifiNative.p2pStopFind();
+                    if (!reinvokePersistentGroup(mSavedPeerConfig)) {
+                        // Do negotiation when persistence fails
+                        p2pConnectWithPinDisplay(mSavedPeerConfig);
+                    }
+                    mPeers.updateStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.INVITED);
+                    sendPeersChangedBroadcast();
+                    transitionTo(mGroupNegotiationState);
+                   break;
+                case PEER_CONNECTION_USER_REJECT:
+                    if (DBG) logd("User rejected invitation " + mSavedPeerConfig);
+                    transitionTo(mInactiveState);
+                    break;
+                default:
+                    return NOT_HANDLED;
+            }
+            return ret;
+        }
+
+        @Override
+        public void exit() {
+            //TODO: dismiss dialog if not already done
+        }
+    }
+
+
+
     class ProvisionDiscoveryState extends State {
         @Override
         public void enter() {
@@ -1213,7 +1258,7 @@
                             transitionTo(mGroupNegotiationState);
                         } else {
                             mJoinExistingGroup = false;
-                            transitionTo(mUserAuthorizingInvitationState);
+                            transitionTo(mUserAuthorizingNegotiationRequestState);
                         }
                     }
                     break;
@@ -1292,7 +1337,6 @@
                         mPeers.updateStatus(groupOwner.deviceAddress, WifiP2pDevice.CONNECTED);
                         sendPeersChangedBroadcast();
                     }
-                    mSavedPeerConfig = null;
                     transitionTo(mGroupCreatedState);
                     break;
                 case WifiMonitor.P2P_GO_NEGOTIATION_FAILURE_EVENT:
@@ -1323,7 +1367,9 @@
                         // invocation was succeeded.
                         // wait P2P_GROUP_STARTED_EVENT.
                         break;
-                    } else if (status == P2pStatus.UNKNOWN_P2P_GROUP) {
+                    }
+                    loge("Invitation result " + status);
+                    if (status == P2pStatus.UNKNOWN_P2P_GROUP) {
                         // target device has already removed the credential.
                         // So, remove this credential accordingly.
                         int netId = mSavedPeerConfig.netId;
@@ -1332,12 +1378,9 @@
                             removeClientFromList(netId, mSavedPeerConfig.deviceAddress, true);
                         }
 
-                        // invocation is failed. Try another way to connect.
+                        // Reinvocation has failed, try group negotiation
                         mSavedPeerConfig.netId = WifiP2pGroup.PERSISTENT_NET_ID;
-                        if (connect(mSavedPeerConfig, NO_REINVOCATION) == CONNECT_FAILURE) {
-                            handleGroupCreationFailure();
-                            transitionTo(mInactiveState);
-                        }
+                        p2pConnectWithPinDisplay(mSavedPeerConfig);
                     } else if (status == P2pStatus.INFORMATION_IS_CURRENTLY_UNAVAILABLE) {
 
                         // Devices setting persistent_reconnect to 0 in wpa_supplicant
@@ -1345,10 +1388,7 @@
                         // "information is currently unable" error.
                         // So, try another way to connect for interoperability.
                         mSavedPeerConfig.netId = WifiP2pGroup.PERSISTENT_NET_ID;
-                        if (connect(mSavedPeerConfig, NO_REINVOCATION) == CONNECT_FAILURE) {
-                            handleGroupCreationFailure();
-                            transitionTo(mInactiveState);
-                        }
+                        p2pConnectWithPinDisplay(mSavedPeerConfig);
                     } else if (status == P2pStatus.NO_COMMON_CHANNEL) {
                         transitionTo(mFrequencyConflictState);
                     } else {
@@ -1452,6 +1492,8 @@
         @Override
         public void enter() {
             if (DBG) logd(getName());
+            // Once connected, peer config details are invalid
+            mSavedPeerConfig.invalidate();
             mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
 
             updateThisDevice(WifiP2pDevice.CONNECTED);
@@ -1497,7 +1539,7 @@
                         if (mGroup.removeClient(deviceAddress)) {
                             if (DBG) logd("Removed client " + deviceAddress);
                             if (!mAutonomousGroup && mGroup.isClientListEmpty()) {
-                                Slog.d(TAG, "Client list empty, remove non-persistent p2p group");
+                                logd("Client list empty, remove non-persistent p2p group");
                                 mWifiNative.p2pGroupRemove(mGroup.getInterface());
                                 // We end up sending connection changed broadcast
                                 // when this happens at exit()
@@ -1573,50 +1615,61 @@
                     sendMessage(WifiP2pManager.REMOVE_GROUP);
                     deferMessage(message);
                     break;
-                case WifiP2pManager.CONNECT:
-                    WifiP2pConfig config = (WifiP2pConfig) message.obj;
                     // This allows any client to join the GO during the
                     // WPS window
-                    if (config.deviceAddress == null) {
-                         if (config.wps.setup == WpsInfo.PBC) {
-                             mWifiNative.startWpsPbc(mGroup.getInterface(), null);
-                         } else {
-                             if (config.wps.pin == null) {
-                                 String pin = mWifiNative.startWpsPinDisplay(mGroup.getInterface());
-                                 try {
-                                     Integer.parseInt(pin);
-                                     notifyInvitationSent(pin, config.deviceAddress != null ?
-                                             config.deviceAddress : "any");
-                                 } catch (NumberFormatException ignore) {
-                                     // do nothing if pin is invalid
-                                 }
-                             } else {
-                                 mWifiNative.startWpsPinKeypad(mGroup.getInterface(),
-                                         config.wps.pin);
-                             }
-                         }
+                case WifiP2pManager.START_WPS:
+                    WpsInfo wps = (WpsInfo) message.obj;
+                    if (wps == null) {
+                        replyToMessage(message, WifiP2pManager.START_WPS_FAILED);
+                        break;
+                    }
+                    boolean ret = true;
+                    if (wps.setup == WpsInfo.PBC) {
+                        ret = mWifiNative.startWpsPbc(mGroup.getInterface(), null);
+                    } else {
+                        if (wps.pin == null) {
+                            String pin = mWifiNative.startWpsPinDisplay(mGroup.getInterface());
+                            try {
+                                Integer.parseInt(pin);
+                                notifyInvitationSent(pin, "any");
+                            } catch (NumberFormatException ignore) {
+                                ret = false;
+                            }
+                        } else {
+                            ret = mWifiNative.startWpsPinKeypad(mGroup.getInterface(),
+                                    wps.pin);
+                        }
+                    }
+                    replyToMessage(message, ret ? WifiP2pManager.START_WPS_SUCCEEDED :
+                            WifiP2pManager.START_WPS_FAILED);
+                    break;
+                case WifiP2pManager.CONNECT:
+                    WifiP2pConfig config = (WifiP2pConfig) message.obj;
+                    if (isConfigInvalid(config)) {
+                        loge("Dropping connect requeset " + config);
+                        replyToMessage(message, WifiP2pManager.CONNECT_FAILED);
+                        break;
+                    }
+                    logd("Inviting device : " + config.deviceAddress);
+                    mSavedPeerConfig = config;
+                    if (mWifiNative.p2pInvite(mGroup, config.deviceAddress)) {
+                        mPeers.updateStatus(config.deviceAddress, WifiP2pDevice.INVITED);
+                        sendPeersChangedBroadcast();
                         replyToMessage(message, WifiP2pManager.CONNECT_SUCCEEDED);
                     } else {
-                        logd("Inviting device : " + config.deviceAddress);
-                        mSavedPeerConfig = config;
-                        if (mWifiNative.p2pInvite(mGroup, config.deviceAddress)) {
-                            mPeers.updateStatus(config.deviceAddress, WifiP2pDevice.INVITED);
-                            sendPeersChangedBroadcast();
-                            replyToMessage(message, WifiP2pManager.CONNECT_SUCCEEDED);
-                        } else {
-                            replyToMessage(message, WifiP2pManager.CONNECT_FAILED,
-                                    WifiP2pManager.ERROR);
-                        }
+                        replyToMessage(message, WifiP2pManager.CONNECT_FAILED,
+                                WifiP2pManager.ERROR);
                     }
                     // TODO: figure out updating the status to declined when invitation is rejected
                     break;
                 case WifiMonitor.P2P_INVITATION_RESULT_EVENT:
                     P2pStatus status = (P2pStatus)message.obj;
-                    logd("===> INVITATION RESULT EVENT : " + status);
                     if (status == P2pStatus.SUCCESS) {
                         // invocation was succeeded.
                         break;
-                    } else if (status == P2pStatus.UNKNOWN_P2P_GROUP) {
+                    }
+                    loge("Invitation result " + status);
+                    if (status == P2pStatus.UNKNOWN_P2P_GROUP) {
                         // target device has already removed the credential.
                         // So, remove this credential accordingly.
                         int netId = mGroup.getNetworkId();
@@ -1625,7 +1678,7 @@
                             if (!removeClientFromList(netId,
                                     mSavedPeerConfig.deviceAddress, false)) {
                                 // not found the client on the list
-                                Slog.e(TAG, "Already removed the client, ignore");
+                                loge("Already removed the client, ignore");
                                 break;
                             }
                             // try invitation.
@@ -1650,7 +1703,7 @@
                     transitionTo(mUserAuthorizingJoinState);
                     break;
                 case WifiMonitor.P2P_GROUP_STARTED_EVENT:
-                    Slog.e(TAG, "Duplicate group creation event notice, ignore");
+                    loge("Duplicate group creation event notice, ignore");
                     break;
                 default:
                     return NOT_HANDLED;
@@ -1689,12 +1742,10 @@
                         mWifiNative.startWpsPinKeypad(mGroup.getInterface(),
                                 mSavedPeerConfig.wps.pin);
                     }
-                    mSavedPeerConfig = null;
                     transitionTo(mGroupCreatedState);
                     break;
                 case PEER_CONNECTION_USER_REJECT:
                     if (DBG) logd("User rejected incoming request");
-                    mSavedPeerConfig = null;
                     transitionTo(mGroupCreatedState);
                     break;
                 default:
@@ -2025,30 +2076,52 @@
     }
 
     /**
-     * Try to connect to the target device.
-     *
-     * Use the persistent credential if it has been stored.
-     *
-     * @param config
-     * @param tryInvocation if true, try to invoke. Otherwise, never try to invoke.
-     * @return
+     * A config is valid if it has a peer address that has already been
+     * discovered
+     * @return true if it is invalid, false otherwise
      */
-    private int connect(WifiP2pConfig config, boolean tryInvocation) {
+    private boolean isConfigInvalid(WifiP2pConfig config) {
+        if (config == null) return true;
+        if (TextUtils.isEmpty(config.deviceAddress)) return true;
+        if (mPeers.get(config.deviceAddress) == null) return true;
+        return false;
+    }
 
-        if (config == null) {
-            loge("config is null");
-            return CONNECT_FAILURE;
+    /* TODO: The supplicant does not provide group capability changes as an event.
+     * Having it pushed as an event would avoid polling for this information right
+     * before a connection
+     */
+    private WifiP2pDevice fetchCurrentDeviceDetails(WifiP2pConfig config) {
+        /* Fetch & update group capability from supplicant on the device */
+        int gc = mWifiNative.getGroupCapability(config.deviceAddress);
+        mPeers.updateGroupCapability(config.deviceAddress, gc);
+        return mPeers.get(config.deviceAddress);
+    }
+
+    /**
+     * Start a p2p group negotiation and display pin if necessary
+     * @param config for the peer
+     */
+    private void p2pConnectWithPinDisplay(WifiP2pConfig config) {
+        WifiP2pDevice dev = fetchCurrentDeviceDetails(config);
+
+        String pin = mWifiNative.p2pConnect(config, dev.isGroupOwner());
+        try {
+            Integer.parseInt(pin);
+            notifyInvitationSent(pin, config.deviceAddress);
+        } catch (NumberFormatException ignore) {
+            // do nothing if p2pConnect did not return a pin
         }
+    }
 
-        boolean isResp = (mSavedPeerConfig != null &&
-                config.deviceAddress.equals(mSavedPeerConfig.deviceAddress));
-        mSavedPeerConfig = config;
-
-        WifiP2pDevice dev = mPeers.get(config.deviceAddress);
-        if (dev == null) {
-            loge("target device not found " + config.deviceAddress);
-            return CONNECT_FAILURE;
-        }
+    /**
+     * Reinvoke a persistent group.
+     *
+     * @param config for the peer
+     * @return true on success, false on failure
+     */
+    private boolean reinvokePersistentGroup(WifiP2pConfig config) {
+        WifiP2pDevice dev = fetchCurrentDeviceDetails(config);
 
         boolean join = dev.isGroupOwner();
         String ssid = mWifiNative.p2pGetSsid(dev.deviceAddress);
@@ -2065,18 +2138,18 @@
             if (netId >= 0) {
                 // Skip WPS and start 4way handshake immediately.
                 if (!mWifiNative.p2pGroupAdd(netId)) {
-                    return CONNECT_FAILURE;
+                    return false;
                 }
-                return CONNECT_SUCCESS;
+                return true;
             }
         }
 
         if (!join && dev.isDeviceLimit()) {
             loge("target device reaches the device limit.");
-            return CONNECT_FAILURE;
+            return false;
         }
 
-        if (!join && tryInvocation && dev.isInvitationCapable()) {
+        if (!join && dev.isInvitationCapable()) {
             int netId = WifiP2pGroup.PERSISTENT_NET_ID;
             if (config.netId >= 0) {
                 if (config.deviceAddress.equals(mGroups.getOwnerAddr(config.netId))) {
@@ -2093,25 +2166,17 @@
                 // Invoke the persistent group.
                 if (mWifiNative.p2pReinvoke(netId, dev.deviceAddress)) {
                     // Save network id. It'll be used when an invitation result event is received.
-                    mSavedPeerConfig.netId = netId;
-                    return CONNECT_SUCCESS;
+                    config.netId = netId;
+                    return true;
                 } else {
                     loge("p2pReinvoke() failed, update networks");
                     updatePersistentNetworks(RELOAD);
-                    // continue with negotiation
+                    return false;
                 }
             }
         }
 
-        //Stop discovery before issuing connect
-        mWifiNative.p2pStopFind();
-
-        if (!isResp) {
-            return NEEDS_PROVISION_REQ;
-        }
-
-        p2pConnectWithPinDisplay(config);
-        return CONNECT_SUCCESS;
+        return false;
     }
 
     /**
@@ -2217,22 +2282,6 @@
         return deviceAddress;
     }
 
-    private void p2pConnectWithPinDisplay(WifiP2pConfig config) {
-        WifiP2pDevice dev = mPeers.get(config.deviceAddress);
-        if (dev == null) {
-            loge("target device is not found " + config.deviceAddress);
-            return;
-        }
-
-        String pin = mWifiNative.p2pConnect(config, dev.isGroupOwner());
-        try {
-            Integer.parseInt(pin);
-            notifyInvitationSent(pin, config.deviceAddress);
-        } catch (NumberFormatException ignore) {
-            // do nothing if p2pConnect did not return a pin
-        }
-    }
-
     private String getPersistedDeviceName() {
         String deviceName = Settings.Global.getString(mContext.getContentResolver(),
                 Settings.Global.WIFI_P2P_DEVICE_NAME);
@@ -2299,7 +2348,7 @@
 
         mThisDevice.deviceAddress = mWifiNative.p2pGetDeviceAddress();
         updateThisDevice(WifiP2pDevice.AVAILABLE);
-        if (DBG) Slog.d(TAG, "DeviceAddress: " + mThisDevice.deviceAddress);
+        if (DBG) logd("DeviceAddress: " + mThisDevice.deviceAddress);
 
         mClientInfoList.clear();
         mWifiNative.p2pFlush();
@@ -2323,14 +2372,13 @@
         // Remove only the peer we failed to connect to so that other devices discovered
         // that have not timed out still remain in list for connection
         boolean peersChanged = mPeers.remove(mPeersLostDuringConnection);
-        if (mSavedPeerConfig != null && mPeers.remove(mSavedPeerConfig.deviceAddress) != null) {
+        if (mPeers.remove(mSavedPeerConfig.deviceAddress) != null) {
             peersChanged = true;
         }
         if (peersChanged) {
             sendPeersChangedBroadcast();
         }
 
-        mSavedPeerConfig = null;
         mPeersLostDuringConnection.clear();
         mServiceDiscReqId = null;
         sendMessage(WifiP2pManager.DISCOVER_PEERS);