am 263c4310: am ce468a35: Stop wifi display discovery when no longer needed.

* commit '263c431017c51fc51c85454a0f9cc7979001a3c1':
  Stop wifi display discovery when no longer needed.
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index d5208d9..093e0e9 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -297,16 +297,31 @@
     }
 
     /**
-     * Initiates a fresh scan of availble Wifi displays.
+     * Starts scanning for available Wifi displays.
      * The results are sent as a {@link #ACTION_WIFI_DISPLAY_STATUS_CHANGED} broadcast.
      * <p>
+     * Calls to this method nest and must be matched by an equal number of calls to
+     * {@link #stopWifiDisplayScan()}.
+     * </p><p>
+     * Requires {@link android.Manifest.permission#CONFIGURE_WIFI_DISPLAY}.
+     * </p>
+     *
+     * @hide
+     */
+    public void startWifiDisplayScan() {
+        mGlobal.startWifiDisplayScan();
+    }
+
+    /**
+     * Stops scanning for available Wifi displays.
+     * <p>
      * Requires {@link android.Manifest.permission#CONFIGURE_WIFI_DISPLAY}.
      * </p>
      *
      * @hide
      */
-    public void scanWifiDisplays() {
-        mGlobal.scanWifiDisplays();
+    public void stopWifiDisplayScan() {
+        mGlobal.stopWifiDisplayScan();
     }
 
     /**
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 936a086..3417430 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -72,6 +72,8 @@
     private final SparseArray<DisplayInfo> mDisplayInfoCache = new SparseArray<DisplayInfo>();
     private int[] mDisplayIdCache;
 
+    private int mWifiDisplayScanNestCount;
+
     private DisplayManagerGlobal(IDisplayManager dm) {
         mDm = dm;
     }
@@ -267,11 +269,32 @@
         }
     }
 
-    public void scanWifiDisplays() {
-        try {
-            mDm.scanWifiDisplays();
-        } catch (RemoteException ex) {
-            Log.e(TAG, "Failed to scan for Wifi displays.", ex);
+    public void startWifiDisplayScan() {
+        synchronized (mLock) {
+            if (mWifiDisplayScanNestCount++ == 0) {
+                registerCallbackIfNeededLocked();
+                try {
+                    mDm.startWifiDisplayScan();
+                } catch (RemoteException ex) {
+                    Log.e(TAG, "Failed to scan for Wifi displays.", ex);
+                }
+            }
+        }
+    }
+
+    public void stopWifiDisplayScan() {
+        synchronized (mLock) {
+            if (--mWifiDisplayScanNestCount == 0) {
+                try {
+                    mDm.stopWifiDisplayScan();
+                } catch (RemoteException ex) {
+                    Log.e(TAG, "Failed to scan for Wifi displays.", ex);
+                }
+            } else if (mWifiDisplayScanNestCount < 0) {
+                Log.wtf(TAG, "Wifi display scan nest count became negative: "
+                        + mWifiDisplayScanNestCount);
+                mWifiDisplayScanNestCount = 0;
+            }
         }
     }
 
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 6b2c887..68eb13f 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -29,11 +29,14 @@
 
     void registerCallback(in IDisplayManagerCallback callback);
 
-    // No permissions required.
-    void scanWifiDisplays();
+    // Requires CONFIGURE_WIFI_DISPLAY permission.
+    // The process must have previously registered a callback.
+    void startWifiDisplayScan();
 
-    // Requires CONFIGURE_WIFI_DISPLAY permission to connect to an unknown device.
-    // No permissions required to connect to a known device.
+    // Requires CONFIGURE_WIFI_DISPLAY permission.
+    void stopWifiDisplayScan();
+
+    // Requires CONFIGURE_WIFI_DISPLAY permission.
     void connectWifiDisplay(String address);
 
     // No permissions required.
@@ -45,6 +48,12 @@
     // Requires CONFIGURE_WIFI_DISPLAY permission.
     void forgetWifiDisplay(String address);
 
+    // Requires CONFIGURE_WIFI_DISPLAY permission.
+    void pauseWifiDisplay();
+
+    // Requires CONFIGURE_WIFI_DISPLAY permission.
+    void resumeWifiDisplay();
+
     // No permissions required.
     WifiDisplayStatus getWifiDisplayStatus();
 
@@ -55,10 +64,4 @@
 
     // No permissions required but must be same Uid as the creator.
     void releaseVirtualDisplay(in IBinder token);
-
-    // Requires CONFIGURE_WIFI_DISPLAY permission.
-    void pauseWifiDisplay();
-
-    // Requires CONFIGURE_WIFI_DISPLAY permission.
-    void resumeWifiDisplay();
 }
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 3f1851d..de20227 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -61,9 +61,6 @@
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     static class Static implements DisplayManager.DisplayListener {
-        // Time between wifi display scans when actively scanning in milliseconds.
-        private static final int WIFI_DISPLAY_SCAN_INTERVAL = 10000;
-
         final Context mAppContext;
         final Resources mResources;
         final IAudioService mAudioService;
@@ -87,6 +84,7 @@
 
         final boolean mCanConfigureWifiDisplays;
         boolean mActivelyScanningWifiDisplays;
+        String mPreviousActiveWifiDisplayAddress;
 
         int mDiscoveryRequestRouteTypes;
         boolean mDiscoverRequestActiveScan;
@@ -106,16 +104,6 @@
             }
         };
 
-        final Runnable mScanWifiDisplays = new Runnable() {
-            @Override
-            public void run() {
-                if (mActivelyScanningWifiDisplays) {
-                    mDisplayService.scanWifiDisplays();
-                    mHandler.postDelayed(this, WIFI_DISPLAY_SCAN_INTERVAL);
-                }
-            }
-        };
-
         Static(Context appContext) {
             mAppContext = appContext;
             mResources = Resources.getSystem();
@@ -279,15 +267,24 @@
             }
 
             // Update wifi display scanning.
-            if (activeScanWifiDisplay && mCanConfigureWifiDisplays) {
-                if (!mActivelyScanningWifiDisplays) {
-                    mActivelyScanningWifiDisplays = true;
-                    mHandler.post(mScanWifiDisplays);
+            // TODO: All of this should be managed by the media router service.
+            if (mCanConfigureWifiDisplays) {
+                if (mSelectedRoute != null
+                        && mSelectedRoute.matchesTypes(ROUTE_TYPE_REMOTE_DISPLAY)) {
+                    // Don't scan while already connected to a remote display since
+                    // it may interfere with the ongoing transmission.
+                    activeScanWifiDisplay = false;
                 }
-            } else {
-                if (mActivelyScanningWifiDisplays) {
-                    mActivelyScanningWifiDisplays = false;
-                    mHandler.removeCallbacks(mScanWifiDisplays);
+                if (activeScanWifiDisplay) {
+                    if (!mActivelyScanningWifiDisplays) {
+                        mActivelyScanningWifiDisplays = true;
+                        mDisplayService.startWifiDisplayScan();
+                    }
+                } else {
+                    if (mActivelyScanningWifiDisplays) {
+                        mActivelyScanningWifiDisplays = false;
+                        mDisplayService.stopWifiDisplayScan();
+                    }
                 }
             }
 
@@ -945,6 +942,9 @@
             }
             dispatchRouteSelected(types & route.getSupportedTypes(), route);
         }
+
+        // The behavior of active scans may depend on the currently selected route.
+        sStatic.updateDiscoveryRequest();
     }
 
     static void selectDefaultRouteStatic() {
@@ -1291,10 +1291,8 @@
     }
 
     static void updateWifiDisplayStatus(WifiDisplayStatus status) {
-        boolean wantScan = false;
         WifiDisplay[] displays;
         WifiDisplay activeDisplay;
-
         if (status.getFeatureState() == WifiDisplayStatus.FEATURE_STATE_ON) {
             displays = status.getDisplays();
             activeDisplay = status.getActiveDisplay();
@@ -1314,6 +1312,8 @@
             displays = WifiDisplay.EMPTY_ARRAY;
             activeDisplay = null;
         }
+        String activeDisplayAddress = activeDisplay != null ?
+                activeDisplay.getDeviceAddress() : null;
 
         // Add or update routes.
         for (int i = 0; i < displays.length; i++) {
@@ -1323,9 +1323,11 @@
                 if (route == null) {
                     route = makeWifiDisplayRoute(d, status);
                     addRouteStatic(route);
-                    wantScan = true;
                 } else {
-                    updateWifiDisplayRoute(route, d, status);
+                    String address = d.getDeviceAddress();
+                    boolean disconnected = !address.equals(activeDisplayAddress)
+                            && address.equals(sStatic.mPreviousActiveWifiDisplayAddress);
+                    updateWifiDisplayRoute(route, d, status, disconnected);
                 }
                 if (d.equals(activeDisplay)) {
                     selectRouteStatic(route.getSupportedTypes(), route, false);
@@ -1343,6 +1345,10 @@
                 }
             }
         }
+
+        // Remember the current active wifi display address so that we can infer disconnections.
+        // TODO: This hack will go away once all of this is moved into the media router service.
+        sStatic.mPreviousActiveWifiDisplayAddress = activeDisplayAddress;
     }
 
     private static boolean shouldShowWifiDisplay(WifiDisplay d, WifiDisplay activeDisplay) {
@@ -1400,7 +1406,8 @@
     }
 
     private static void updateWifiDisplayRoute(
-            RouteInfo route, WifiDisplay display, WifiDisplayStatus wfdStatus) {
+            RouteInfo route, WifiDisplay display, WifiDisplayStatus wfdStatus,
+            boolean disconnected) {
         boolean changed = false;
         final String newName = display.getFriendlyDisplayName();
         if (!route.getName().equals(newName)) {
@@ -1418,7 +1425,7 @@
             dispatchRouteChanged(route);
         }
 
-        if (!enabled && route.isSelected()) {
+        if ((!enabled || disconnected) && route.isSelected()) {
             // Oops, no longer available. Reselect the default.
             selectDefaultRouteStatic();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
index e1a20ec..42201c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
@@ -692,14 +692,8 @@
         } else {
             connectedRoute = null;
             connecting = false;
-            final int count = mMediaRouter.getRouteCount();
-            for (int i = 0; i < count; i++) {
-                MediaRouter.RouteInfo route = mMediaRouter.getRouteAt(i);
-                if (route.matchesTypes(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY)) {
-                    enabled = true;
-                    break;
-                }
-            }
+            enabled = mMediaRouter.isRouteAvailable(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY,
+                    MediaRouter.AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE);
         }
 
         mRemoteDisplayState.enabled = enabled;
diff --git a/services/java/com/android/server/display/DisplayManagerService.java b/services/java/com/android/server/display/DisplayManagerService.java
index 02f26b3..bcb677f 100644
--- a/services/java/com/android/server/display/DisplayManagerService.java
+++ b/services/java/com/android/server/display/DisplayManagerService.java
@@ -35,6 +35,7 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.text.TextUtils;
+import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.Display;
@@ -173,6 +174,9 @@
     // The Wifi display adapter, or null if not registered.
     private WifiDisplayAdapter mWifiDisplayAdapter;
 
+    // The number of active wifi display scan requests.
+    private int mWifiDisplayScanRequestCount;
+
     // The virtual display adapter, or null if not registered.
     private VirtualDisplayAdapter mVirtualDisplayAdapter;
 
@@ -458,29 +462,81 @@
         }
     }
 
-    private void onCallbackDied(int pid) {
+    private void onCallbackDied(CallbackRecord record) {
         synchronized (mSyncRoot) {
-            mCallbacks.remove(pid);
+            mCallbacks.remove(record.mPid);
+            stopWifiDisplayScanLocked(record);
         }
     }
 
     @Override // Binder call
-    public void scanWifiDisplays() {
+    public void startWifiDisplayScan() {
         mContext.enforceCallingOrSelfPermission(Manifest.permission.CONFIGURE_WIFI_DISPLAY,
-                "Permission required to scan wifi displays");
+                "Permission required to start wifi display scans");
 
+        final int callingPid = Binder.getCallingPid();
         final long token = Binder.clearCallingIdentity();
         try {
             synchronized (mSyncRoot) {
-                if (mWifiDisplayAdapter != null) {
-                    mWifiDisplayAdapter.requestScanLocked();
+                CallbackRecord record = mCallbacks.get(callingPid);
+                if (record == null) {
+                    throw new IllegalStateException("The calling process has not "
+                            + "registered an IDisplayManagerCallback.");
                 }
+                startWifiDisplayScanLocked(record);
             }
         } finally {
             Binder.restoreCallingIdentity(token);
         }
     }
 
+    private void startWifiDisplayScanLocked(CallbackRecord record) {
+        if (!record.mWifiDisplayScanRequested) {
+            record.mWifiDisplayScanRequested = true;
+            if (mWifiDisplayScanRequestCount++ == 0) {
+                if (mWifiDisplayAdapter != null) {
+                    mWifiDisplayAdapter.requestStartScanLocked();
+                }
+            }
+        }
+    }
+
+    @Override // Binder call
+    public void stopWifiDisplayScan() {
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.CONFIGURE_WIFI_DISPLAY,
+                "Permission required to stop wifi display scans");
+
+        final int callingPid = Binder.getCallingPid();
+        final long token = Binder.clearCallingIdentity();
+        try {
+            synchronized (mSyncRoot) {
+                CallbackRecord record = mCallbacks.get(callingPid);
+                if (record == null) {
+                    throw new IllegalStateException("The calling process has not "
+                            + "registered an IDisplayManagerCallback.");
+                }
+                stopWifiDisplayScanLocked(record);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    private void stopWifiDisplayScanLocked(CallbackRecord record) {
+        if (record.mWifiDisplayScanRequested) {
+            record.mWifiDisplayScanRequested = false;
+            if (--mWifiDisplayScanRequestCount == 0) {
+                if (mWifiDisplayAdapter != null) {
+                    mWifiDisplayAdapter.requestStopScanLocked();
+                }
+            } else if (mWifiDisplayScanRequestCount < 0) {
+                Log.wtf(TAG, "mWifiDisplayScanRequestCount became negative: "
+                        + mWifiDisplayScanRequestCount);
+                mWifiDisplayScanRequestCount = 0;
+            }
+        }
+    }
+
     @Override // Binder call
     public void connectWifiDisplay(String address) {
         if (address == null) {
@@ -1107,6 +1163,7 @@
             pw.println("  mDefaultViewport=" + mDefaultViewport);
             pw.println("  mExternalTouchViewport=" + mExternalTouchViewport);
             pw.println("  mSingleDisplayDemoMode=" + mSingleDisplayDemoMode);
+            pw.println("  mWifiDisplayScanRequestCount=" + mWifiDisplayScanRequestCount);
 
             IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "    ");
             ipw.increaseIndent();
@@ -1134,6 +1191,15 @@
                 pw.println("  Display " + displayId + ":");
                 display.dumpLocked(ipw);
             }
+
+            final int callbackCount = mCallbacks.size();
+            pw.println();
+            pw.println("Callbacks: size=" + callbackCount);
+            for (int i = 0; i < callbackCount; i++) {
+                CallbackRecord callback = mCallbacks.valueAt(i);
+                pw.println("  " + i + ": mPid=" + callback.mPid
+                        + ", mWifiDisplayScanRequested=" + callback.mWifiDisplayScanRequested);
+            }
         }
     }
 
@@ -1234,9 +1300,11 @@
     }
 
     private final class CallbackRecord implements DeathRecipient {
-        private final int mPid;
+        public final int mPid;
         private final IDisplayManagerCallback mCallback;
 
+        public boolean mWifiDisplayScanRequested;
+
         public CallbackRecord(int pid, IDisplayManagerCallback callback) {
             mPid = pid;
             mCallback = callback;
@@ -1247,7 +1315,7 @@
             if (DEBUG) {
                 Slog.d(TAG, "Display listener for pid " + mPid + " died.");
             }
-            onCallbackDied(mPid);
+            onCallbackDied(this);
         }
 
         public void notifyDisplayEventAsync(int displayId, int event) {
diff --git a/services/java/com/android/server/display/WifiDisplayAdapter.java b/services/java/com/android/server/display/WifiDisplayAdapter.java
index fdef039..cd57941 100644
--- a/services/java/com/android/server/display/WifiDisplayAdapter.java
+++ b/services/java/com/android/server/display/WifiDisplayAdapter.java
@@ -127,7 +127,7 @@
         pw.println("mPendingStatusChangeBroadcast=" + mPendingStatusChangeBroadcast);
         pw.println("mPendingNotificationUpdate=" + mPendingNotificationUpdate);
         pw.println("mSupportsProtectedBuffers=" + mSupportsProtectedBuffers);
-
+ 
         // Try to dump the controller state.
         if (mDisplayController == null) {
             pw.println("mDisplayController=null");
@@ -157,16 +157,31 @@
         });
     }
 
-    public void requestScanLocked() {
+    public void requestStartScanLocked() {
         if (DEBUG) {
-            Slog.d(TAG, "requestScanLocked");
+            Slog.d(TAG, "requestStartScanLocked");
         }
 
         getHandler().post(new Runnable() {
             @Override
             public void run() {
                 if (mDisplayController != null) {
-                    mDisplayController.requestScan();
+                    mDisplayController.requestStartScan();
+                }
+            }
+        });
+    }
+
+    public void requestStopScanLocked() {
+        if (DEBUG) {
+            Slog.d(TAG, "requestStopScanLocked");
+        }
+
+        getHandler().post(new Runnable() {
+            @Override
+            public void run() {
+                if (mDisplayController != null) {
+                    mDisplayController.requestStopScan();
                 }
             }
         });
@@ -187,15 +202,6 @@
         });
     }
 
-    private boolean isRememberedDisplayLocked(String address) {
-        for (WifiDisplay display : mRememberedDisplays) {
-            if (display.getDeviceAddress().equals(address)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     public void requestPauseLocked() {
         if (DEBUG) {
             Slog.d(TAG, "requestPauseLocked");
@@ -552,20 +558,20 @@
         }
 
         @Override
-        public void onScanFinished(WifiDisplay[] availableDisplays) {
+        public void onScanResults(WifiDisplay[] availableDisplays) {
             synchronized (getSyncRoot()) {
                 availableDisplays = mPersistentDataStore.applyWifiDisplayAliases(
                         availableDisplays);
 
-                // check if any of the available displays changed canConnect status
                 boolean changed = !Arrays.equals(mAvailableDisplays, availableDisplays);
+
+                // Check whether any of the available displays changed canConnect status.
                 for (int i = 0; !changed && i<availableDisplays.length; i++) {
                     changed = availableDisplays[i].canConnect()
                             != mAvailableDisplays[i].canConnect();
                 }
 
-                if (mScanState != WifiDisplayStatus.SCAN_STATE_NOT_SCANNING || changed) {
-                    mScanState = WifiDisplayStatus.SCAN_STATE_NOT_SCANNING;
+                if (changed) {
                     mAvailableDisplays = availableDisplays;
                     fixRememberedDisplayNamesFromAvailableDisplaysLocked();
                     updateDisplaysLocked();
@@ -575,6 +581,16 @@
         }
 
         @Override
+        public void onScanFinished() {
+            synchronized (getSyncRoot()) {
+                if (mScanState != WifiDisplayStatus.SCAN_STATE_NOT_SCANNING) {
+                    mScanState = WifiDisplayStatus.SCAN_STATE_NOT_SCANNING;
+                    scheduleStatusChangedBroadcastLocked();
+                }
+            }
+        }
+
+        @Override
         public void onDisplayConnecting(WifiDisplay display) {
             synchronized (getSyncRoot()) {
                 display = mPersistentDataStore.applyWifiDisplayAlias(display);
diff --git a/services/java/com/android/server/display/WifiDisplayController.java b/services/java/com/android/server/display/WifiDisplayController.java
index b2939fe..dbb59b2 100644
--- a/services/java/com/android/server/display/WifiDisplayController.java
+++ b/services/java/com/android/server/display/WifiDisplayController.java
@@ -27,7 +27,6 @@
 import android.hardware.display.WifiDisplay;
 import android.hardware.display.WifiDisplaySessionInfo;
 import android.hardware.display.WifiDisplayStatus;
-import android.media.AudioManager;
 import android.media.RemoteDisplay;
 import android.net.NetworkInfo;
 import android.net.Uri;
@@ -75,12 +74,19 @@
 
     private static final int DEFAULT_CONTROL_PORT = 7236;
     private static final int MAX_THROUGHPUT = 50;
-    private static final int CONNECTION_TIMEOUT_SECONDS = 60;
+    private static final int CONNECTION_TIMEOUT_SECONDS = 30;
     private static final int RTSP_TIMEOUT_SECONDS = 30;
     private static final int RTSP_TIMEOUT_SECONDS_CERT_MODE = 120;
 
-    private static final int DISCOVER_PEERS_MAX_RETRIES = 10;
-    private static final int DISCOVER_PEERS_RETRY_DELAY_MILLIS = 500;
+    // We repeatedly issue calls to discover peers every so often for a few reasons.
+    // 1. The initial request may fail and need to retried.
+    // 2. Discovery will self-abort after any group is initiated, which may not necessarily
+    //    be what we want to have happen.
+    // 3. Discovery will self-timeout after 2 minutes, whereas we want discovery to
+    //    be occur for as long as a client is requesting it be.
+    // 4. We don't seem to get updated results for displays we've already found until
+    //    we ask to discover again, particularly for the isSessionAvailable() property.
+    private static final int DISCOVER_PEERS_INTERVAL_MILLIS = 10000;
 
     private static final int CONNECT_MAX_RETRIES = 3;
     private static final int CONNECT_RETRY_DELAY_MILLIS = 500;
@@ -103,12 +109,12 @@
     // True if Wifi display is enabled by the user.
     private boolean mWifiDisplayOnSetting;
 
+    // True if a scan was requested independent of whether one is actually in progress.
+    private boolean mScanRequested;
+
     // 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;
 
@@ -209,8 +215,8 @@
         pw.println("mWfdEnabled=" + mWfdEnabled);
         pw.println("mWfdEnabling=" + mWfdEnabling);
         pw.println("mNetworkInfo=" + mNetworkInfo);
+        pw.println("mScanRequested=" + mScanRequested);
         pw.println("mDiscoverPeersInProgress=" + mDiscoverPeersInProgress);
-        pw.println("mDiscoverPeersRetriesLeft=" + mDiscoverPeersRetriesLeft);
         pw.println("mDesiredDevice=" + describeWifiP2pDevice(mDesiredDevice));
         pw.println("mConnectingDisplay=" + describeWifiP2pDevice(mConnectingDevice));
         pw.println("mDisconnectingDisplay=" + describeWifiP2pDevice(mDisconnectingDevice));
@@ -232,8 +238,18 @@
         }
     }
 
-    public void requestScan() {
-        discoverPeers();
+    public void requestStartScan() {
+        if (!mScanRequested) {
+            mScanRequested = true;
+            updateScanState();
+        }
+    }
+
+    public void requestStopScan() {
+        if (mScanRequested) {
+            mScanRequested = false;
+            updateScanState();
+        }
     }
 
     public void requestConnect(String address) {
@@ -282,6 +298,7 @@
                             mWfdEnabling = false;
                             mWfdEnabled = true;
                             reportFeatureState();
+                            updateScanState();
                         }
                     }
 
@@ -318,6 +335,7 @@
             mWfdEnabling = false;
             mWfdEnabled = false;
             reportFeatureState();
+            updateScanState();
             disconnect();
         }
     }
@@ -340,12 +358,29 @@
                 WifiDisplayStatus.FEATURE_STATE_OFF;
     }
 
-    private void discoverPeers() {
-        if (!mDiscoverPeersInProgress) {
-            mDiscoverPeersInProgress = true;
-            mDiscoverPeersRetriesLeft = DISCOVER_PEERS_MAX_RETRIES;
-            handleScanStarted();
-            tryDiscoverPeers();
+    private void updateScanState() {
+        if (mScanRequested && mWfdEnabled && mDesiredDevice == null) {
+            if (!mDiscoverPeersInProgress) {
+                Slog.i(TAG, "Starting Wifi display scan.");
+                mDiscoverPeersInProgress = true;
+                handleScanStarted();
+                tryDiscoverPeers();
+            }
+        } else {
+            if (mDiscoverPeersInProgress) {
+                // Cancel automatic retry right away.
+                mHandler.removeCallbacks(mDiscoverPeers);
+
+                // Defer actually stopping discovery if we have a connection attempt in progress.
+                // The wifi display connection attempt often fails if we are not in discovery
+                // mode.  So we allow discovery to continue until we give up trying to connect.
+                if (mDesiredDevice == null || mDesiredDevice == mConnectedDevice) {
+                    Slog.i(TAG, "Stopping Wifi display scan.");
+                    mDiscoverPeersInProgress = false;
+                    stopPeerDiscovery();
+                    handleScanFinished();
+                }
+            }
         }
     }
 
@@ -357,8 +392,9 @@
                     Slog.d(TAG, "Discover peers succeeded.  Requesting peers now.");
                 }
 
-                mDiscoverPeersInProgress = false;
-                requestPeers();
+                if (mDiscoverPeersInProgress) {
+                    requestPeers();
+                }
             }
 
             @Override
@@ -367,30 +403,28 @@
                     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 {
-                                        handleScanFinished();
-                                        mDiscoverPeersInProgress = false;
-                                    }
-                                }
-                            }
-                        }, DISCOVER_PEERS_RETRY_DELAY_MILLIS);
-                    } else {
-                        handleScanFinished();
-                        mDiscoverPeersInProgress = false;
-                    }
+                // Ignore the error.
+                // We will retry automatically in a little bit.
+            }
+        });
+
+        // Retry discover peers periodically until stopped.
+        mHandler.postDelayed(mDiscoverPeers, DISCOVER_PEERS_INTERVAL_MILLIS);
+    }
+
+    private void stopPeerDiscovery() {
+        mWifiP2pManager.stopPeerDiscovery(mWifiP2pChannel, new ActionListener() {
+            @Override
+            public void onSuccess() {
+                if (DEBUG) {
+                    Slog.d(TAG, "Stop peer discovery succeeded.");
+                }
+            }
+
+            @Override
+            public void onFailure(int reason) {
+                if (DEBUG) {
+                    Slog.d(TAG, "Stop peer discovery failed with reason " + reason + ".");
                 }
             }
         });
@@ -415,7 +449,9 @@
                     }
                 }
 
-                handleScanFinished();
+                if (mDiscoverPeersInProgress) {
+                    handleScanResults();
+                }
             }
         });
     }
@@ -429,7 +465,7 @@
         });
     }
 
-    private void handleScanFinished() {
+    private void handleScanResults() {
         final int count = mAvailableWifiDisplayPeers.size();
         final WifiDisplay[] displays = WifiDisplay.CREATOR.newArray(count);
         for (int i = 0; i < count; i++) {
@@ -441,7 +477,16 @@
         mHandler.post(new Runnable() {
             @Override
             public void run() {
-                mListener.onScanFinished(displays);
+                mListener.onScanResults(displays);
+            }
+        });
+    }
+
+    private void handleScanFinished() {
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                mListener.onScanFinished();
             }
         });
     }
@@ -484,6 +529,12 @@
             return;
         }
 
+        if (!mWfdEnabled) {
+            Slog.i(TAG, "Ignoring request to connect to Wifi display because the "
+                    +" feature is currently disabled: " + device.deviceName);
+            return;
+        }
+
         mDesiredDevice = device;
         mConnectionRetriesLeft = CONNECT_MAX_RETRIES;
         updateConnection();
@@ -508,6 +559,10 @@
      * connection is established (or not).
      */
     private void updateConnection() {
+        // Step 0. Stop scans if necessary to prevent interference while connected.
+        // Resume scans later when no longer attempting to connect.
+        updateScanState();
+
         // Step 1. Before we try to connect to a new device, tell the system we
         // have disconnected from the old one.
         if (mRemoteDisplay != null && mConnectedDevice != mDesiredDevice) {
@@ -661,7 +716,7 @@
             return; // wait for asynchronous callback
         }
 
-        // Step 6. Listen for incoming connections.
+        // Step 6. Listen for incoming RTSP connection.
         if (mConnectedDevice != null && mRemoteDisplay == null) {
             Inet4Address addr = getInterfaceAddress(mConnectedDeviceGroupInfo);
             if (addr == null) {
@@ -817,7 +872,11 @@
             }
         } else {
             mConnectedDeviceGroupInfo = null;
-            disconnect();
+
+            // Disconnect if we lost the network while connecting or connected to a display.
+            if (mConnectingDevice != null || mConnectedDevice != null) {
+                disconnect();
+            }
 
             // After disconnection for a group, for some reason we have a tendency
             // to get a peer change notification with an empty list of peers.
@@ -828,6 +887,13 @@
         }
     }
 
+    private final Runnable mDiscoverPeers = new Runnable() {
+        @Override
+        public void run() {
+            tryDiscoverPeers();
+        }
+    };
+
     private final Runnable mConnectionTimeout = new Runnable() {
         @Override
         public void run() {
@@ -1033,7 +1099,8 @@
         void onFeatureStateChanged(int featureState);
 
         void onScanStarted();
-        void onScanFinished(WifiDisplay[] availableDisplays);
+        void onScanResults(WifiDisplay[] availableDisplays);
+        void onScanFinished();
 
         void onDisplayConnecting(WifiDisplay display);
         void onDisplayConnectionFailed();