Merge "Migrate ringtone playback to SystemUI." into jb-dev
diff --git a/Android.mk b/Android.mk
index 7ebf4b5..678ae55 100644
--- a/Android.mk
+++ b/Android.mk
@@ -198,6 +198,7 @@
 	media/java/android/media/IMediaScannerService.aidl \
 	media/java/android/media/IRemoteControlClient.aidl \
 	media/java/android/media/IRemoteControlDisplay.aidl \
+	media/java/android/media/IRingtonePlayer.aidl \
 	telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl \
 	telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl \
 	telephony/java/com/android/internal/telephony/ITelephony.aidl \
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 0a996df..69689c9 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Bitmap;
+import android.media.AudioManager;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -213,7 +214,7 @@
     /**
      * Use this constant as the value for audioStreamType to request that
      * the default stream type for notifications be used.  Currently the
-     * default stream type is STREAM_RING.
+     * default stream type is {@link AudioManager#STREAM_NOTIFICATION}.
      */
     public static final int STREAM_DEFAULT = -1;
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 3ae2b4e..471a496 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -628,6 +628,11 @@
         android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
         android:protectionLevel="signature" />
 
+    <!-- Allows registration for remote audio playback. @hide -->
+    <permission android:name="android.permission.REMOTE_AUDIO_PLAYBACK"
+        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
+        android:protectionLevel="signature" />
+
     <!-- =========================================== -->
     <!-- Permissions associated with telephony state -->
     <!-- =========================================== -->
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 2a006c6..f603525 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -2306,4 +2306,12 @@
         }
     }
 
+    /** {@hide} */
+    public IRingtonePlayer getRingtonePlayer() {
+        try {
+            return getService().getRingtonePlayer();
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
 }
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 3e958dc..dc9496f 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -16,6 +16,7 @@
 
 package android.media;
 
+import static android.Manifest.permission.REMOTE_AUDIO_PLAYBACK;
 import static android.media.AudioManager.RINGER_MODE_NORMAL;
 import static android.media.AudioManager.RINGER_MODE_SILENT;
 import static android.media.AudioManager.RINGER_MODE_VIBRATE;
@@ -360,7 +361,6 @@
     private int mPrevVolDirection = AudioManager.ADJUST_SAME;
     // Keyguard manager proxy
     private KeyguardManager mKeyguardManager;
-
     // mVolumeControlStream is set by VolumePanel to temporarily force the stream type which volume
     // is controlled by Vol keys.
     private int  mVolumeControlStream = -1;
@@ -369,6 +369,8 @@
     // server process so in theory it is not necessary to monitor the client death.
     // However it is good to be ready for future evolutions.
     private ForceControlStreamClient mForceControlStreamClient = null;
+    // Used to play ringtones outside system_server
+    private volatile IRingtonePlayer mRingtonePlayer;
 
     ///////////////////////////////////////////////////////////////////////////
     // Construction
@@ -4231,6 +4233,17 @@
     }
 
     @Override
+    public void setRingtonePlayer(IRingtonePlayer player) {
+        mContext.enforceCallingOrSelfPermission(REMOTE_AUDIO_PLAYBACK, null);
+        mRingtonePlayer = player;
+    }
+
+    @Override
+    public IRingtonePlayer getRingtonePlayer() {
+        return mRingtonePlayer;
+    }
+
+    @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
 
@@ -4238,6 +4251,4 @@
         dumpFocusStack(pw);
         dumpRCStack(pw);
     }
-
-
 }
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index df21040..1a2714e 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -21,6 +21,8 @@
 import android.media.IAudioFocusDispatcher;
 import android.media.IRemoteControlClient;
 import android.media.IRemoteControlDisplay;
+import android.media.IRingtonePlayer;
+import android.net.Uri;
 
 /**
  * {@hide}
@@ -113,10 +115,11 @@
     oneway void remoteControlDisplayUsesBitmapSize(in IRemoteControlDisplay rcd, int w, int h);
 
     void startBluetoothSco(IBinder cb);
-
     void stopBluetoothSco(IBinder cb);
 
     void forceVolumeControlStream(int streamType, IBinder cb);
 
+    void setRingtonePlayer(IRingtonePlayer player);
+    IRingtonePlayer getRingtonePlayer();
     int getMasterStreamType();
 }
diff --git a/media/java/android/media/IRingtonePlayer.aidl b/media/java/android/media/IRingtonePlayer.aidl
new file mode 100644
index 0000000..44a0333
--- /dev/null
+++ b/media/java/android/media/IRingtonePlayer.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.net.Uri;
+
+/**
+ * @hide
+ */
+interface IRingtonePlayer {
+    /** Used for Ringtone.java playback */
+    void play(IBinder token, in Uri uri, int streamType);
+    void stop(IBinder token);
+    boolean isPlaying(IBinder token);
+
+    /** Used for Notification sound playback. */
+    void playAsync(in Uri uri, boolean looping, int streamType);
+    void stopAsync();
+}
diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java
index f16ba36..57139d2 100644
--- a/media/java/android/media/Ringtone.java
+++ b/media/java/android/media/Ringtone.java
@@ -18,17 +18,15 @@
 
 import android.content.ContentResolver;
 import android.content.Context;
-import android.content.res.AssetFileDescriptor;
 import android.database.Cursor;
-import android.media.AudioManager;
-import android.media.MediaPlayer;
 import android.net.Uri;
+import android.os.Binder;
+import android.os.RemoteException;
 import android.provider.DrmStore;
 import android.provider.MediaStore;
 import android.provider.Settings;
 import android.util.Log;
 
-import java.io.FileDescriptor;
 import java.io.IOException;
 
 /**
@@ -41,7 +39,8 @@
  * @see RingtoneManager
  */
 public class Ringtone {
-    private static String TAG = "Ringtone";
+    private static final String TAG = "Ringtone";
+    private static final boolean LOGD = true;
 
     private static final String[] MEDIA_COLUMNS = new String[] {
         MediaStore.Audio.Media._ID,
@@ -55,21 +54,26 @@
         DrmStore.Audio.TITLE
     };
 
-    private MediaPlayer mAudio;
+    private final Context mContext;
+    private final AudioManager mAudioManager;
+    private final boolean mAllowRemote;
+    private final IRingtonePlayer mRemotePlayer;
+    private final Binder mRemoteToken;
+
+    private MediaPlayer mLocalPlayer;
 
     private Uri mUri;
     private String mTitle;
-    private FileDescriptor mFileDescriptor;
-    private AssetFileDescriptor mAssetFileDescriptor;
 
     private int mStreamType = AudioManager.STREAM_RING;
-    private AudioManager mAudioManager;
 
-    private Context mContext;
-
-    Ringtone(Context context) {
+    /** {@hide} */
+    public Ringtone(Context context, boolean allowRemote) {
         mContext = context;
         mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+        mAllowRemote = allowRemote;
+        mRemotePlayer = allowRemote ? mAudioManager.getRingtonePlayer() : null;
+        mRemoteToken = allowRemote ? new Binder() : null;
     }
 
     /**
@@ -79,18 +83,10 @@
      */
     public void setStreamType(int streamType) {
         mStreamType = streamType;
-        
-        if (mAudio != null) {
-            /*
-             * The stream type has to be set before the media player is
-             * prepared. Re-initialize it.
-             */
-            try {
-                openMediaPlayer();
-            } catch (IOException e) {
-                Log.w(TAG, "Couldn't set the stream type", e);
-            }
-        }
+
+        // The stream type has to be set before the media player is prepared.
+        // Re-initialize it.
+        setUri(mUri);
     }
 
     /**
@@ -164,67 +160,75 @@
         
         return title;
     }
-    
-    private void openMediaPlayer() throws IOException {
-        if (mAudio != null) {
-            mAudio.release();
-        }
-        mAudio = new MediaPlayer();
-        if (mUri != null) {
-            mAudio.setDataSource(mContext, mUri);
-        } else if (mFileDescriptor != null) {
-            mAudio.setDataSource(mFileDescriptor);
-        } else if (mAssetFileDescriptor != null) {
-            // Note: using getDeclaredLength so that our behavior is the same
-            // as previous versions when the content provider is returning
-            // a full file.
-            if (mAssetFileDescriptor.getDeclaredLength() < 0) {
-                mAudio.setDataSource(mAssetFileDescriptor.getFileDescriptor());
-            } else {
-                mAudio.setDataSource(mAssetFileDescriptor.getFileDescriptor(),
-                        mAssetFileDescriptor.getStartOffset(),
-                        mAssetFileDescriptor.getDeclaredLength());
-            }
-        } else {
-            throw new IOException("No data source set.");
-        }
-        mAudio.setAudioStreamType(mStreamType);
-        mAudio.prepare();
-    }
 
-    void open(FileDescriptor fd) throws IOException {
-        mFileDescriptor = fd;
-        openMediaPlayer();
-    }
+    /**
+     * Set {@link Uri} to be used for ringtone playback. Attempts to open
+     * locally, otherwise will delegate playback to remote
+     * {@link IRingtonePlayer}.
+     *
+     * @hide
+     */
+    public void setUri(Uri uri) {
+        destroyLocalPlayer();
 
-    void open(AssetFileDescriptor fd) throws IOException {
-        mAssetFileDescriptor = fd;
-        openMediaPlayer();
-    }
-
-    void open(Uri uri) throws IOException {
         mUri = uri;
-        openMediaPlayer();
+        if (mUri == null) {
+            return;
+        }
+
+        // TODO: detect READ_EXTERNAL and specific content provider case, instead of relying on throwing
+
+        // try opening uri locally before delegating to remote player
+        mLocalPlayer = new MediaPlayer();
+        try {
+            mLocalPlayer.setDataSource(mContext, mUri);
+            mLocalPlayer.setAudioStreamType(mStreamType);
+            mLocalPlayer.prepare();
+
+        } catch (SecurityException e) {
+            destroyLocalPlayer();
+            if (!mAllowRemote) {
+                throw new IllegalStateException("Remote playback not allowed", e);
+            }
+        } catch (IOException e) {
+            destroyLocalPlayer();
+            if (!mAllowRemote) {
+                throw new IllegalStateException("Remote playback not allowed", e);
+            }
+        }
+
+        if (LOGD) {
+            if (mLocalPlayer != null) {
+                Log.d(TAG, "Successfully created local player");
+            } else {
+                Log.d(TAG, "Problem opening; delegating to remote player");
+            }
+        }
+    }
+
+    /** {@hide} */
+    public Uri getUri() {
+        return mUri;
     }
 
     /**
      * Plays the ringtone.
      */
     public void play() {
-        if (mAudio == null) {
-            try {
-                openMediaPlayer();
-            } catch (Exception ex) {
-                Log.e(TAG, "play() caught ", ex);
-                mAudio = null;
-            }
-        }
-        if (mAudio != null) {
-            // do not ringtones if stream volume is 0
+        if (mLocalPlayer != null) {
+            // do not play ringtones if stream volume is 0
             // (typically because ringer mode is silent).
             if (mAudioManager.getStreamVolume(mStreamType) != 0) {
-                mAudio.start();
+                mLocalPlayer.start();
             }
+        } else if (mAllowRemote) {
+            try {
+                mRemotePlayer.play(mRemoteToken, mUri, mStreamType);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Problem playing ringtone: " + e);
+            }
+        } else {
+            throw new IllegalStateException("Neither local nor remote playback available");
         }
     }
 
@@ -232,10 +236,22 @@
      * Stops a playing ringtone.
      */
     public void stop() {
-        if (mAudio != null) {
-            mAudio.reset();
-            mAudio.release();
-            mAudio = null;
+        if (mLocalPlayer != null) {
+            destroyLocalPlayer();
+        } else if (mAllowRemote) {
+            try {
+                mRemotePlayer.stop(mRemoteToken);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Problem stopping ringtone: " + e);
+            }
+        }
+    }
+
+    private void destroyLocalPlayer() {
+        if (mLocalPlayer != null) {
+            mLocalPlayer.reset();
+            mLocalPlayer.release();
+            mLocalPlayer = null;
         }
     }
 
@@ -245,7 +261,18 @@
      * @return True if playing, false otherwise.
      */
     public boolean isPlaying() {
-        return mAudio != null && mAudio.isPlaying();
+        if (mLocalPlayer != null) {
+            return mLocalPlayer.isPlaying();
+        } else if (mAllowRemote) {
+            try {
+                return mRemotePlayer.isPlaying(mRemoteToken);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Problem checking ringtone: " + e);
+                return false;
+            }
+        } else {
+            throw new IllegalStateException("Neither local nor remote playback available");
+        }
     }
 
     void setTitle(String title) {
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index a5b1f45..5e18bfa 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -606,16 +606,15 @@
      * @see #getRingtone(Context, Uri)
      */
     private static Ringtone getRingtone(final Context context, Uri ringtoneUri, int streamType) {
-
         try {
-            Ringtone r = new Ringtone(context);
+            final Ringtone r = new Ringtone(context, true);
             if (streamType >= 0) {
                 r.setStreamType(streamType);
             }
-            r.open(ringtoneUri);
+            r.setUri(ringtoneUri);
             return r;
         } catch (Exception ex) {
-            Log.e(TAG, "Failed to open ringtone " + ringtoneUri);
+            Log.e(TAG, "Failed to open ringtone " + ringtoneUri + ": " + ex);
         }
 
         return null;
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index a31c264..cef21b0 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -5,6 +5,7 @@
     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
 
     <uses-permission android:name="android.permission.INJECT_EVENTS" />
     <uses-permission android:name="android.permission.WRITE_SETTINGS" />
@@ -12,6 +13,7 @@
     <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
     <uses-permission android:name="android.permission.STATUS_BAR" />
     <uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />
+    <uses-permission android:name="android.permission.REMOTE_AUDIO_PLAYBACK" />
 
     <!-- Networking and telephony -->
     <uses-permission android:name="android.permission.BLUETOOTH" />
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIService.java b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
index ae568f8..0a57499 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIService.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
@@ -41,6 +41,7 @@
     final Object[] SERVICES = new Object[] {
             0, // system bar or status bar, filled in below.
             com.android.systemui.power.PowerUI.class,
+            com.android.systemui.media.RingtonePlayer.class,
         };
 
     /**
diff --git a/services/java/com/android/server/NotificationPlayer.java b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
similarity index 98%
rename from services/java/com/android/server/NotificationPlayer.java
rename to packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
index 52d2381..6a12eb1 100644
--- a/services/java/com/android/server/NotificationPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server;
+package com.android.systemui.media;
 
 import android.content.Context;
 import android.media.AudioManager;
@@ -36,7 +36,7 @@
 /**
  * @hide
  * This class is provides the same interface and functionality as android.media.AsyncPlayer
- * with the following differences: 
+ * with the following differences:
  * - whenever audio is played, audio focus is requested,
  * - whenever audio playback is stopped or the playback completed, audio focus is abandoned.
  */
@@ -338,4 +338,3 @@
         }
     }
 }
-
diff --git a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
new file mode 100644
index 0000000..9e273d4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media;
+
+import android.content.Context;
+import android.media.IAudioService;
+import android.media.IRingtonePlayer;
+import android.media.Ringtone;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Slog;
+
+import com.android.systemui.SystemUI;
+import com.google.android.collect.Maps;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.HashMap;
+
+/**
+ * Service that offers to play ringtones by {@link Uri}, since our process has
+ * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE}.
+ */
+public class RingtonePlayer extends SystemUI {
+    private static final String TAG = "RingtonePlayer";
+    private static final boolean LOGD = true;
+
+    // TODO: support Uri switching under same IBinder
+
+    private IAudioService mAudioService;
+
+    private final NotificationPlayer mAsyncPlayer = new NotificationPlayer(TAG);
+    private final HashMap<IBinder, Client> mClients = Maps.newHashMap();
+
+    @Override
+    public void start() {
+        mAsyncPlayer.setUsesWakeLock(mContext);
+
+        mAudioService = IAudioService.Stub.asInterface(
+                ServiceManager.getService(Context.AUDIO_SERVICE));
+        try {
+            mAudioService.setRingtonePlayer(mCallback);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Problem registering RingtonePlayer: " + e);
+        }
+    }
+
+    /**
+     * Represents an active remote {@link Ringtone} client.
+     */
+    private class Client implements IBinder.DeathRecipient {
+        private final IBinder mToken;
+        private final Ringtone mRingtone;
+
+        public Client(IBinder token, Uri uri, int streamType) {
+            mToken = token;
+            mRingtone = new Ringtone(mContext, false);
+            mRingtone.setStreamType(streamType);
+            mRingtone.setUri(uri);
+        }
+
+        @Override
+        public void binderDied() {
+            if (LOGD) Slog.d(TAG, "binderDied() token=" + mToken);
+            synchronized (mClients) {
+                mClients.remove(mToken);
+            }
+            mRingtone.stop();
+        }
+    }
+
+    private IRingtonePlayer mCallback = new IRingtonePlayer.Stub() {
+        @Override
+        public void play(IBinder token, Uri uri, int streamType) throws RemoteException {
+            if (LOGD) Slog.d(TAG, "play(token=" + token + ", uri=" + uri + ")");
+            Client client;
+            synchronized (mClients) {
+                client = mClients.get(token);
+                if (client == null) {
+                    client = new Client(token, uri, streamType);
+                    token.linkToDeath(client, 0);
+                    mClients.put(token, client);
+                }
+            }
+            client.mRingtone.play();
+        }
+
+        @Override
+        public void stop(IBinder token) {
+            if (LOGD) Slog.d(TAG, "stop(token=" + token + ")");
+            Client client;
+            synchronized (mClients) {
+                client = mClients.remove(token);
+            }
+            if (client != null) {
+                client.mToken.unlinkToDeath(client, 0);
+                client.mRingtone.stop();
+            }
+        }
+
+        @Override
+        public boolean isPlaying(IBinder token) {
+            if (LOGD) Slog.d(TAG, "isPlaying(token=" + token + ")");
+            Client client;
+            synchronized (mClients) {
+                client = mClients.get(token);
+            }
+            if (client != null) {
+                return client.mRingtone.isPlaying();
+            } else {
+                return false;
+            }
+        }
+
+        @Override
+        public void playAsync(Uri uri, boolean looping, int streamType) {
+            if (LOGD) Slog.d(TAG, "playAsync(uri=" + uri + ")");
+            if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+                throw new SecurityException("Async playback only available from system UID.");
+            }
+            mAsyncPlayer.play(mContext, uri, looping, streamType);
+        }
+
+        @Override
+        public void stopAsync() {
+            if (LOGD) Slog.d(TAG, "stopAsync()");
+            if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+                throw new SecurityException("Async playback only available from system UID.");
+            }
+            mAsyncPlayer.stop();
+        }
+    };
+
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("Clients:");
+        synchronized (mClients) {
+            for (Client client : mClients.values()) {
+                pw.print("  mToken=");
+                pw.print(client.mToken);
+                pw.print(" mUri=");
+                pw.println(client.mRingtone.getUri());
+            }
+        }
+    }
+}
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 1ba7e79..663a031 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -16,16 +16,15 @@
 
 package com.android.server;
 
-import com.android.internal.os.AtomicFile;
-import com.android.internal.statusbar.StatusBarNotification;
-import com.android.internal.util.FastXmlSerializer;
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.END_TAG;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
 
 import android.app.ActivityManagerNative;
 import android.app.IActivityManager;
 import android.app.INotificationManager;
 import android.app.ITransientNotification;
 import android.app.Notification;
-import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.StatusBarManager;
 import android.content.BroadcastReceiver;
@@ -39,8 +38,8 @@
 import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.media.AudioManager;
-import android.net.NetworkPolicy;
-import android.net.NetworkTemplate;
+import android.media.IAudioService;
+import android.media.IRingtonePlayer;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Handler;
@@ -48,6 +47,7 @@
 import android.os.Message;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.UserId;
 import android.os.Vibrator;
 import android.provider.Settings;
@@ -61,6 +61,14 @@
 import android.view.accessibility.AccessibilityManager;
 import android.widget.Toast;
 
+import com.android.internal.os.AtomicFile;
+import com.android.internal.statusbar.StatusBarNotification;
+import com.android.internal.util.FastXmlSerializer;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
@@ -74,18 +82,6 @@
 
 import libcore.io.IoUtils;
 
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
-import static android.net.NetworkPolicyManager.POLICY_NONE;
-import static com.android.server.net.NetworkPolicyManagerService.XmlUtils.writeBooleanAttribute;
-import static com.android.server.net.NetworkPolicyManagerService.XmlUtils.writeIntAttribute;
-import static com.android.server.net.NetworkPolicyManagerService.XmlUtils.writeLongAttribute;
-import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
-import static org.xmlpull.v1.XmlPullParser.END_TAG;
-import static org.xmlpull.v1.XmlPullParser.START_TAG;
-
 
 /** {@hide} */
 public class NotificationManagerService extends INotificationManager.Stub
@@ -126,12 +122,13 @@
     private int mDefaultNotificationLedOn;
     private int mDefaultNotificationLedOff;
 
-    private NotificationRecord mSoundNotification;
-    private NotificationPlayer mSound;
     private boolean mSystemReady;
     private int mDisabledNotifications;
 
+    private NotificationRecord mSoundNotification;
     private NotificationRecord mVibrateNotification;
+
+    private IAudioService mAudioService;
     private Vibrator mVibrator;
 
     // for enabling and disabling notification pulse behavior
@@ -409,17 +406,19 @@
                     // cancel whatever's going on
                     long identity = Binder.clearCallingIdentity();
                     try {
-                        mSound.stop();
-                    }
-                    finally {
+                        final IRingtonePlayer player = mAudioService.getRingtonePlayer();
+                        if (player != null) {
+                            player.stopAsync();
+                        }
+                    } catch (RemoteException e) {
+                    } finally {
                         Binder.restoreCallingIdentity(identity);
                     }
 
                     identity = Binder.clearCallingIdentity();
                     try {
                         mVibrator.cancel();
-                    }
-                    finally {
+                    } finally {
                         Binder.restoreCallingIdentity(identity);
                     }
                 }
@@ -445,11 +444,15 @@
             synchronized (mNotificationList) {
                 // sound
                 mSoundNotification = null;
+
                 long identity = Binder.clearCallingIdentity();
                 try {
-                    mSound.stop();
-                }
-                finally {
+                    final IRingtonePlayer player = mAudioService.getRingtonePlayer();
+                    if (player != null) {
+                        player.stopAsync();
+                    }
+                } catch (RemoteException e) {
+                } finally {
                     Binder.restoreCallingIdentity(identity);
                 }
 
@@ -458,8 +461,7 @@
                 identity = Binder.clearCallingIdentity();
                 try {
                     mVibrator.cancel();
-                }
-                finally {
+                } finally {
                     Binder.restoreCallingIdentity(identity);
                 }
 
@@ -570,8 +572,6 @@
         mContext = context;
         mVibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
         mAm = ActivityManagerNative.getDefault();
-        mSound = new NotificationPlayer(TAG);
-        mSound.setUsesWakeLock(context);
         mToastQueue = new ArrayList<ToastRecord>();
         mHandler = new WorkerHandler();
 
@@ -622,6 +622,9 @@
     }
 
     void systemReady() {
+        mAudioService = IAudioService.Stub.asInterface(
+                ServiceManager.getService(Context.AUDIO_SERVICE));
+
         // no beeping until we're basically done booting
         mSystemReady = true;
     }
@@ -1026,11 +1029,14 @@
                     // do not play notifications if stream volume is 0
                     // (typically because ringer mode is silent).
                     if (audioManager.getStreamVolume(audioStreamType) != 0) {
-                        long identity = Binder.clearCallingIdentity();
+                        final long identity = Binder.clearCallingIdentity();
                         try {
-                            mSound.play(mContext, uri, looping, audioStreamType);
-                        }
-                        finally {
+                            final IRingtonePlayer player = mAudioService.getRingtonePlayer();
+                            if (player != null) {
+                                player.playAsync(uri, looping, audioStreamType);
+                            }
+                        } catch (RemoteException e) {
+                        } finally {
                             Binder.restoreCallingIdentity(identity);
                         }
                     }
@@ -1121,11 +1127,14 @@
         // sound
         if (mSoundNotification == r) {
             mSoundNotification = null;
-            long identity = Binder.clearCallingIdentity();
+            final long identity = Binder.clearCallingIdentity();
             try {
-                mSound.stop();
-            }
-            finally {
+                final IRingtonePlayer player = mAudioService.getRingtonePlayer();
+                if (player != null) {
+                    player.stopAsync();
+                }
+            } catch (RemoteException e) {
+            } finally {
                 Binder.restoreCallingIdentity(identity);
             }
         }
@@ -1386,7 +1395,6 @@
             }
 
             pw.println("  mSoundNotification=" + mSoundNotification);
-            pw.println("  mSound=" + mSound);
             pw.println("  mVibrateNotification=" + mVibrateNotification);
             pw.println("  mDisabledNotifications=0x" + Integer.toHexString(mDisabledNotifications));
             pw.println("  mSystemReady=" + mSystemReady);