Allow applications to connect to known wifi displays.

Bug: 7177920
Change-Id: I9d8406e1016988e2cd267dfa52d78a829f1b385e
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 58a0f13..28e320b 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -46,9 +46,7 @@
      * The status is provided as a {@link WifiDisplayStatus} object in the
      * {@link #EXTRA_WIFI_DISPLAY_STATUS} extra.
      * </p><p>
-     * This broadcast is only sent to registered receivers with the
-     * {@link android.Manifest.permission#CONFIGURE_WIFI_DISPLAY} permission and can
-     * only be sent by the system.
+     * This broadcast is only sent to registered receivers and can only be sent by the system.
      * </p>
      * @hide
      */
@@ -163,6 +161,9 @@
      * <p>
      * Automatically remembers the display after a successful connection, if not
      * already remembered.
+     * </p><p>
+     * Requires {@link android.Manifest.permission#CONFIGURE_WIFI_DISPLAY} to connect
+     * to unknown displays.  No permissions are required to connect to already known displays.
      * </p>
      *
      * @param deviceAddress The MAC address of the device to which we should connect.
@@ -187,6 +188,8 @@
      * The display must already be remembered for this call to succeed.  In other words,
      * we must already have successfully connected to the display at least once and then
      * not forgotten it.
+     * </p><p>
+     * Requires {@link android.Manifest.permission#CONFIGURE_WIFI_DISPLAY}.
      * </p>
      *
      * @param deviceAddress The MAC address of the device to rename.
@@ -202,6 +205,8 @@
      * Forgets a previously remembered Wifi display.
      * <p>
      * Automatically disconnects from the display if currently connected to it.
+     * </p><p>
+     * Requires {@link android.Manifest.permission#CONFIGURE_WIFI_DISPLAY}.
      * </p>
      *
      * @param deviceAddress The MAC address of the device to forget.
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 4b6fb53..79aad78 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -28,13 +28,14 @@
 
     void registerCallback(in IDisplayManagerCallback callback);
 
-    // Requires CONFIGURE_WIFI_DISPLAY permission.
+    // No permissions required.
     void scanWifiDisplays();
 
-    // Requires CONFIGURE_WIFI_DISPLAY permission.
+    // Requires CONFIGURE_WIFI_DISPLAY permission to connect to an unknown device.
+    // No permissions required to connect to a known device.
     void connectWifiDisplay(String address);
 
-    // Requires CONFIGURE_WIFI_DISPLAY permission.
+    // No permissions required.
     void disconnectWifiDisplay();
 
     // Requires CONFIGURE_WIFI_DISPLAY permission.
@@ -43,6 +44,6 @@
     // Requires CONFIGURE_WIFI_DISPLAY permission.
     void forgetWifiDisplay(String address);
 
-    // Requires CONFIGURE_WIFI_DISPLAY permission.
+    // No permissions required.
     WifiDisplayStatus getWifiDisplayStatus();
 }
diff --git a/services/java/com/android/server/display/DisplayManagerService.java b/services/java/com/android/server/display/DisplayManagerService.java
index 85f3b56..f348cb6 100644
--- a/services/java/com/android/server/display/DisplayManagerService.java
+++ b/services/java/com/android/server/display/DisplayManagerService.java
@@ -352,11 +352,6 @@
 
     @Override // Binder call
     public void scanWifiDisplays() {
-        if (mContext.checkCallingPermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires CONFIGURE_WIFI_DISPLAY permission");
-        }
-
         final long token = Binder.clearCallingIdentity();
         try {
             synchronized (mSyncRoot) {
@@ -371,19 +366,16 @@
 
     @Override // Binder call
     public void connectWifiDisplay(String address) {
-        if (mContext.checkCallingPermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires CONFIGURE_WIFI_DISPLAY permission");
-        }
         if (address == null) {
             throw new IllegalArgumentException("address must not be null");
         }
 
+        final boolean trusted = canCallerConfigureWifiDisplay();
         final long token = Binder.clearCallingIdentity();
         try {
             synchronized (mSyncRoot) {
                 if (mWifiDisplayAdapter != null) {
-                    mWifiDisplayAdapter.requestConnectLocked(address);
+                    mWifiDisplayAdapter.requestConnectLocked(address, trusted);
                 }
             }
         } finally {
@@ -393,11 +385,6 @@
 
     @Override // Binder call
     public void disconnectWifiDisplay() {
-        if (mContext.checkCallingPermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires CONFIGURE_WIFI_DISPLAY permission");
-        }
-
         final long token = Binder.clearCallingIdentity();
         try {
             synchronized (mSyncRoot) {
@@ -412,13 +399,13 @@
 
     @Override // Binder call
     public void renameWifiDisplay(String address, String alias) {
-        if (mContext.checkCallingPermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires CONFIGURE_WIFI_DISPLAY permission");
-        }
         if (address == null) {
             throw new IllegalArgumentException("address must not be null");
         }
+        if (!canCallerConfigureWifiDisplay()) {
+            throw new SecurityException("Requires CONFIGURE_WIFI_DISPLAY permission to "
+                    + "rename a wifi display.");
+        }
 
         final long token = Binder.clearCallingIdentity();
         try {
@@ -434,13 +421,13 @@
 
     @Override // Binder call
     public void forgetWifiDisplay(String address) {
-        if (mContext.checkCallingPermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires CONFIGURE_WIFI_DISPLAY permission");
-        }
         if (address == null) {
             throw new IllegalArgumentException("address must not be null");
         }
+        if (!canCallerConfigureWifiDisplay()) {
+            throw new SecurityException("Requires CONFIGURE_WIFI_DISPLAY permission to "
+                    + "forget a wifi display.");
+        }
 
         final long token = Binder.clearCallingIdentity();
         try {
@@ -456,11 +443,6 @@
 
     @Override // Binder call
     public WifiDisplayStatus getWifiDisplayStatus() {
-        if (mContext.checkCallingPermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires CONFIGURE_WIFI_DISPLAY permission");
-        }
-
         final long token = Binder.clearCallingIdentity();
         try {
             synchronized (mSyncRoot) {
@@ -475,6 +457,11 @@
         }
     }
 
+    private boolean canCallerConfigureWifiDisplay() {
+        return mContext.checkCallingPermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY)
+                == PackageManager.PERMISSION_GRANTED;
+    }
+
     private void registerDefaultDisplayAdapter() {
         // Register default display adapter.
         synchronized (mSyncRoot) {
diff --git a/services/java/com/android/server/display/WifiDisplayAdapter.java b/services/java/com/android/server/display/WifiDisplayAdapter.java
index 1d50ded..4a89be7 100644
--- a/services/java/com/android/server/display/WifiDisplayAdapter.java
+++ b/services/java/com/android/server/display/WifiDisplayAdapter.java
@@ -27,6 +27,7 @@
 import android.media.RemoteDisplay;
 import android.os.Handler;
 import android.os.IBinder;
+import android.util.Slog;
 import android.view.Surface;
 
 import java.io.PrintWriter;
@@ -121,7 +122,17 @@
         });
     }
 
-    public void requestConnectLocked(final String address) {
+    public void requestConnectLocked(final String address, final boolean trusted) {
+        if (!trusted) {
+            synchronized (getSyncRoot()) {
+                if (!isRememberedDisplayLocked(address)) {
+                    Slog.w(TAG, "Ignoring request by an untrusted client to connect to "
+                            + "an unknown wifi display: " + address);
+                    return;
+                }
+            }
+        }
+
         getHandler().post(new Runnable() {
             @Override
             public void run() {
@@ -132,6 +143,15 @@
         });
     }
 
+    private boolean isRememberedDisplayLocked(String address) {
+        for (WifiDisplay display : mRememberedDisplays) {
+            if (display.getDeviceAddress().equals(address)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     public void requestDisconnectLocked() {
         getHandler().post(new Runnable() {
             @Override
@@ -241,10 +261,8 @@
                         getWifiDisplayStatusLocked());
             }
 
-            // Send protected broadcast about wifi display status to receivers that
-            // have the required permission.
-            getContext().sendBroadcast(intent,
-                    android.Manifest.permission.CONFIGURE_WIFI_DISPLAY);
+            // Send protected broadcast about wifi display status to registered receivers.
+            getContext().sendBroadcast(intent);
         }
     };