Retry wifi display connections.

Change-Id: I48c62c7e272c2696022ddeea1c3107d24a9431b8
diff --git a/services/java/com/android/server/display/WifiDisplayController.java b/services/java/com/android/server/display/WifiDisplayController.java
index b446e66..0e857da 100644
--- a/services/java/com/android/server/display/WifiDisplayController.java
+++ b/services/java/com/android/server/display/WifiDisplayController.java
@@ -64,6 +64,12 @@
     private static final int MAX_THROUGHPUT = 50;
     private static final int CONNECTION_TIMEOUT_SECONDS = 30;
 
+    private static final int DISCOVER_PEERS_MAX_RETRIES = 10;
+    private static final int DISCOVER_PEERS_RETRY_DELAY_MILLIS = 500;
+
+    private static final int CONNECT_MAX_RETRIES = 3;
+    private static final int CONNECT_RETRY_DELAY_MILLIS = 500;
+
     private final Context mContext;
     private final Handler mHandler;
     private final Listener mListener;
@@ -78,6 +84,12 @@
     private final ArrayList<WifiP2pDevice> mKnownWifiDisplayPeers =
             new ArrayList<WifiP2pDevice>();
 
+    // True if there is a call to discoverPeers in progress.
+    private boolean mDiscoverPeersInProgress;
+
+    // Number of discover peers retries remaining.
+    private int mDiscoverPeersRetriesLeft;
+
     // The device to which we want to connect, or null if we want to be disconnected.
     private WifiP2pDevice mDesiredDevice;
 
@@ -94,6 +106,9 @@
     // The device that we announced to the rest of the system.
     private WifiP2pDevice mPublishedDevice;
 
+    // Number of connection retries remaining.
+    private int mConnectionRetriesLeft;
+
     public WifiDisplayController(Context context, Handler handler, Listener listener) {
         mContext = context;
         mHandler = handler;
@@ -114,10 +129,13 @@
         pw.println("mWfdEnabled=" + mWfdEnabled);
         pw.println("mWfdEnabling=" + mWfdEnabling);
         pw.println("mNetworkInfo=" + mNetworkInfo);
+        pw.println("mDiscoverPeersInProgress=" + mDiscoverPeersInProgress);
+        pw.println("mDiscoverPeersRetriesLeft=" + mDiscoverPeersRetriesLeft);
         pw.println("mDesiredDevice=" + describeWifiP2pDevice(mDesiredDevice));
         pw.println("mConnectingDisplay=" + describeWifiP2pDevice(mConnectingDevice));
         pw.println("mConnectedDevice=" + describeWifiP2pDevice(mConnectedDevice));
         pw.println("mPublishedDevice=" + describeWifiP2pDevice(mPublishedDevice));
+        pw.println("mConnectionRetriesLeft=" + mConnectionRetriesLeft);
 
         pw.println("mKnownWifiDisplayPeers: size=" + mKnownWifiDisplayPeers.size());
         for (WifiP2pDevice device : mKnownWifiDisplayPeers) {
@@ -160,6 +178,14 @@
     }
 
     private void discoverPeers() {
+        if (!mDiscoverPeersInProgress) {
+            mDiscoverPeersInProgress = true;
+            mDiscoverPeersRetriesLeft = DISCOVER_PEERS_MAX_RETRIES;
+            tryDiscoverPeers();
+        }
+    }
+
+    private void tryDiscoverPeers() {
         mWifiP2pManager.discoverPeers(mWifiP2pChannel, new ActionListener() {
             @Override
             public void onSuccess() {
@@ -167,6 +193,7 @@
                     Slog.d(TAG, "Discover peers succeeded.  Requesting peers now.");
                 }
 
+                mDiscoverPeersInProgress = false;
                 requestPeers();
             }
 
@@ -175,6 +202,30 @@
                 if (DEBUG) {
                     Slog.d(TAG, "Discover peers failed with reason " + reason + ".");
                 }
+
+                if (mDiscoverPeersInProgress) {
+                    if (reason == 0 && mDiscoverPeersRetriesLeft > 0 && mWfdEnabled) {
+                        mHandler.postDelayed(new Runnable() {
+                            @Override
+                            public void run() {
+                                if (mDiscoverPeersInProgress) {
+                                    if (mDiscoverPeersRetriesLeft > 0 && mWfdEnabled) {
+                                        mDiscoverPeersRetriesLeft -= 1;
+                                        if (DEBUG) {
+                                            Slog.d(TAG, "Retrying discovery.  Retries left: "
+                                                    + mDiscoverPeersRetriesLeft);
+                                        }
+                                        tryDiscoverPeers();
+                                    } else {
+                                        mDiscoverPeersInProgress = false;
+                                    }
+                                }
+                            }
+                        }, DISCOVER_PEERS_RETRY_DELAY_MILLIS);
+                    } else {
+                        mDiscoverPeersInProgress = false;
+                    }
+                }
             }
         });
     }
@@ -230,9 +281,11 @@
                         + describeWifiP2pDevice(device) + " and not part way through "
                         + "connecting to a different device.");
             }
+            return;
         }
 
         mDesiredDevice = device;
+        mConnectionRetriesLeft = CONNECT_MAX_RETRIES;
         updateConnection();
     }
 
@@ -241,6 +294,21 @@
         updateConnection();
     }
 
+    private void retryConnection() {
+        if (mDesiredDevice != null && mPublishedDevice != mDesiredDevice
+                && mConnectionRetriesLeft > 0) {
+            mConnectionRetriesLeft -= 1;
+            Slog.i(TAG, "Retrying Wifi display connection.  Retries left: "
+                    + mConnectionRetriesLeft);
+
+            // Cheap hack.  Make a new instance of the device object so that we
+            // can distinguish it from the previous connection attempt.
+            // This will cause us to tear everything down before we try again.
+            mDesiredDevice = new WifiP2pDevice(mDesiredDevice);
+            updateConnection();
+        }
+    }
+
     /**
      * This function is called repeatedly after each asynchronous operation
      * until all preconditions for the connection have been satisfied and the
@@ -353,7 +421,7 @@
                             + newDevice.deviceName + ", reason=" + reason);
                     if (mConnectingDevice == newDevice) {
                         mConnectingDevice = null;
-                        handleConnectionFailure();
+                        handleConnectionFailure(false);
                     }
                 }
             });
@@ -366,7 +434,7 @@
             if (addr == null) {
                 Slog.i(TAG, "Failed to get local interface address for communicating "
                         + "with Wifi display: " + mConnectedDevice.deviceName);
-                handleConnectionFailure();
+                handleConnectionFailure(false);
                 return; // done
             }
 
@@ -426,7 +494,7 @@
                             Slog.i(TAG, "Aborting connection to Wifi display because "
                                     + "the current P2P group does not contain the device "
                                     + "we expected to find: " + mConnectingDevice.deviceName);
-                            handleConnectionFailure();
+                            handleConnectionFailure(false);
                             return;
                         }
 
@@ -436,7 +504,8 @@
                         }
 
                         if (mConnectingDevice != null && mConnectingDevice == mDesiredDevice) {
-                            Slog.i(TAG, "Connected to Wifi display: " + mConnectingDevice.deviceName);
+                            Slog.i(TAG, "Connected to Wifi display: "
+                                    + mConnectingDevice.deviceName);
 
                             mHandler.removeCallbacks(mConnectionTimeout);
                             mConnectedDeviceGroupInfo = info;
@@ -459,15 +528,25 @@
                 Slog.i(TAG, "Timed out waiting for Wifi display connection after "
                         + CONNECTION_TIMEOUT_SECONDS + " seconds: "
                         + mConnectingDevice.deviceName);
-                handleConnectionFailure();
+                handleConnectionFailure(true);
             }
         }
     };
 
-    private void handleConnectionFailure() {
+    private void handleConnectionFailure(boolean timeoutOccurred) {
         if (mDesiredDevice != null) {
             Slog.i(TAG, "Wifi display connection failed!");
-            disconnect();
+
+            if (mConnectionRetriesLeft > 0) {
+                mHandler.postDelayed(new Runnable() {
+                    @Override
+                    public void run() {
+                        retryConnection();
+                    }
+                }, timeoutOccurred ? 0 : CONNECT_RETRY_DELAY_MILLIS);
+            } else {
+                disconnect();
+            }
         }
     }