Merge change 2365 into donut

* changes:
  Fix wifi multicast API for public use.
diff --git a/api/current.xml b/api/current.xml
index ce1b726..bf3044e 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -309,6 +309,17 @@
  visibility="public"
 >
 </field>
+<field name="CHANGE_WIFI_MULTICAST_STATE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.permission.CHANGE_WIFI_MULTICAST_STATE&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="CHANGE_WIFI_STATE"
  type="java.lang.String"
  transient="false"
@@ -77087,6 +77098,19 @@
 <parameter name="rssiB" type="int">
 </parameter>
 </method>
+<method name="createMulticastLock"
+ return="android.net.wifi.WifiManager.MulticastLock"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="tag" type="java.lang.String">
+</parameter>
+</method>
 <method name="createWifiLock"
  return="android.net.wifi.WifiManager.WifiLock"
  abstract="false"
@@ -77579,6 +77603,48 @@
 >
 </field>
 </class>
+<class name="WifiManager.MulticastLock"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="acquire"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isHeld"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="release"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</class>
 <class name="WifiManager.WifiLock"
  extends="java.lang.Object"
  abstract="false"
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index fbaef5f..36f7dfb 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -644,6 +644,13 @@
         android:description="@string/permdesc_changeWifiState"
         android:label="@string/permlab_changeWifiState" />
 
+    <!-- Allows applications to enter Wi-Fi Multicast mode -->
+    <permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"
+        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
+        android:protectionLevel="dangerous"
+        android:description="@string/permdesc_changeWifiMulticastState"
+        android:label="@string/permlab_changeWifiMulticastState" />
+
     <!-- Allows applications to discover and pair bluetooth devices -->
     <permission android:name="android.permission.BLUETOOTH_ADMIN"
         android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 8b2689bd..e311abd 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1005,6 +1005,15 @@
       configured Wi-Fi networks.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_changeWifiMulticastState">allow Wi-Fi Multicast
+      reception</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_changeWifiMulticastState">Allows an application to
+      receive packets not directly addressed to your device.  This can be
+      useful when discovering services offered near by.  It uses more power
+      than the non-multicast mode.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_bluetoothAdmin">bluetooth administration</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_bluetoothAdmin">Allows an application to configure
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 5fa8701..1528ba3 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -588,6 +588,12 @@
 
     }
 
+    private void enforceMulticastChangePermission() {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.CHANGE_WIFI_MULTICAST_STATE,
+                "WifiService");
+    }
+
     /**
      * see {@link WifiManager#getWifiState()}
      * @return One of {@link WifiManager#WIFI_STATE_DISABLED},
@@ -1930,8 +1936,8 @@
         }
     }
 
-    public void enableMulticast(IBinder binder, String tag) {
-        enforceChangePermission();
+    public void acquireMulticastLock(IBinder binder, String tag) {
+        enforceMulticastChangePermission();
 
         synchronized (mMulticasters) {
             mMulticastEnabled++;
@@ -1953,8 +1959,8 @@
         }
     }
 
-    public void disableMulticast() {
-        enforceChangePermission();
+    public void releaseMulticastLock() {
+        enforceMulticastChangePermission();
 
         int uid = Binder.getCallingUid();
         synchronized (mMulticasters) {
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 7c3af69..3d65d3c 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -72,8 +72,8 @@
 
     boolean isMulticastEnabled();
 
-    void enableMulticast(IBinder binder, String tag);
+    void acquireMulticastLock(IBinder binder, String tag);
 
-    void disableMulticast();
+    void releaseMulticastLock();
 }
 
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 1a7caef..5b8ced6 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -824,6 +824,117 @@
         return new WifiLock(WIFI_MODE_FULL, tag);
     }
 
+
+    /**
+     * Create a new MulticastLock
+     *
+     * @param tag a tag for the MulticastLock to identify it in debugging
+     *            messages.
+     *
+     * @return a new, unacquired MulticastLock with the given tag.
+     *
+     * @see MulticastLock
+     */
+    public MulticastLock createMulticastLock(String tag) {
+        return new MulticastLock(tag);
+    }
+
+    /**
+     * Allows an application to receive Wifi Multicast packets.
+     * Normally the Wifi stack filters out packets not explicitly
+     * addressed to this device.  Acquring a MulticastLock will
+     * cause the stack to receive packets addressed to multicast
+     * addresses.  Processing these extra packets can cause a noticable
+     * battery drain and should be disabled when not needed
+     */
+    public class MulticastLock {
+        private String mTag;
+        private final IBinder mBinder;
+        private boolean mHeld;
+
+        private MulticastLock(String tag) {
+            mTag = tag;
+            mBinder = new Binder();
+            mHeld = false;
+        }
+
+        /**
+         * Locks Wifi Multicast on until {@link #release} is called.
+         *
+         * The first call to {@code acquire} will lock the Multicast on
+         * but subsequent calls will be ignored.  Only one call to
+         * {@link #release} will be required, regardless of the number of
+         * times that {@code acquire} is called.
+         *
+         * Note that other applications may also lock Wifi Multicast on.
+         * Only they can relinquish their lock.
+         *
+         * Also note that applications cannot leave Multicast locked on.
+         * When an app exits or crashes, any Multicast locks will be released.
+         */
+        public void acquire() {
+            synchronized (mBinder) {
+                if (!mHeld) {
+                    try {
+                        mService.acquireMulticastLock(mBinder, mTag);
+                        mHeld = true;
+                    } catch (RemoteException ignore) {
+                    }
+                }
+            }
+        }
+
+        /**
+         * Unlocks Wifi Multicast, restoring the filter of packets
+         * not addressed specifically to this device and saving power.
+         *
+         * Note that if any other Wifi Multicast Locks are still outstanding
+         * this {@code release} call will not have an immediate effect.  Only
+         * when all applications have released all their Multicast Locks will
+         * the Multicast filter be turned back on.
+         *
+         * Also note that when an app exits or crashes all of its Multicast
+         * Locks will be automatically released.
+         */
+        public void release() {
+            synchronized (mBinder) {
+                if (mHeld) {
+                    try {
+                        mService.releaseMulticastLock();
+                        mHeld = false;
+                    } catch (RemoteException ignore) {
+                    }
+                }
+            }
+        }
+
+        /**
+         * Checks whether this MulticastLock is currently held.
+         *
+         * @return true if this MulticastLock is held, false otherwise
+         */
+        public boolean isHeld() {
+            synchronized (mBinder) {
+                return mHeld;
+            }
+        }
+
+        public String toString() {
+            String s1, s2;
+            synchronized (mBinder) {
+                s1 = Integer.toHexString(System.identityHashCode(this));
+                s2 = mHeld ? "held; " : "";
+                return "MulticastLock{ " + s1 + "; " + s2 + " }";
+            }
+        }
+
+        @Override
+        protected void finalize() throws Throwable {
+            super.finalize();
+            release();
+        }
+    }
+
     /**
      * Check multicast filter status.
      *
@@ -838,50 +949,4 @@
             return false;
         }
     }
-
-    /**
-     * Turn on the reception of multicast packets.
-     * The default behavior is to disable multicast packets as they
-     * have a noticable negative effect on battery life.  An
-     * application can turn them on, but should not leave it on for longer
-     * than needed.  When the app quits (or crashes) its request will
-     * be reverted.
-     *
-     * @param tag a string associated with this request for debugging.
-     *
-     * @return true on success
-     *
-     * @see #disableMulticast
-     *
-     * @hide pending API council approval
-     */
-    public boolean enableMulticast(String tag) {
-        try {
-            mService.enableMulticast(new Binder(), tag);
-            return true;
-        } catch (RemoteException e) {
-            return false;
-        }
-    }
-
-    /**
-     * Return to the default multicast-off setting.
-     * Note that if others had turned on Multicast reception, your
-     * call will not turn it back off - they must also turn off their
-     * request for multicast reception.
-     *
-     * @return true on success
-     *
-     * @see #enableMulticast
-     *
-     * @hide pending API council approval
-     */
-    public boolean disableMulticast() {
-        try {
-            mService.disableMulticast();
-            return true;
-        } catch (RemoteException e) {
-            return false;
-        }
-    }
 }
diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
index 6771136..2fbc779 100644
--- a/wifi/java/android/net/wifi/WifiStateTracker.java
+++ b/wifi/java/android/net/wifi/WifiStateTracker.java
@@ -747,8 +747,10 @@
                  * first and then off.. if nobody else wants it on it'll be
                  * off then and it's all synchronized within the API.
                  */
-                mWM.enableMulticast("WifiStateTracker");
-                mWM.disableMulticast();
+                WifiManager.MulticastLock l = 
+                        mWM.createMulticastLock("WifiStateTracker");
+                l.acquire();
+                l.release();
 
                 if (mBluetoothA2dp == null) {
                     mBluetoothA2dp = new BluetoothA2dp(mContext);