summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk1
-rw-r--r--core/java/android/app/Notification.java3
-rw-r--r--core/res/AndroidManifest.xml5
-rw-r--r--media/java/android/media/AudioManager.java8
-rw-r--r--media/java/android/media/AudioService.java17
-rw-r--r--media/java/android/media/IAudioService.aidl5
-rw-r--r--media/java/android/media/IRingtonePlayer.aidl33
-rw-r--r--media/java/android/media/Ringtone.java177
-rw-r--r--media/java/android/media/RingtoneManager.java7
-rw-r--r--packages/SystemUI/AndroidManifest.xml2
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIService.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java (renamed from services/java/com/android/server/NotificationPlayer.java)5
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java164
-rwxr-xr-xservices/java/com/android/server/NotificationManagerService.java90
14 files changed, 390 insertions, 128 deletions
diff --git a/Android.mk b/Android.mk
index 7ebf4b5..678ae55 100644
--- a/Android.mk
+++ b/Android.mk
@@ -198,6 +198,7 @@ LOCAL_SRC_FILES += \
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 com.android.internal.R;
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 @@ public class Notification implements Parcelable
/**
* 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 @@ public class AudioManager {
}
}
+ /** {@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 @@ public class AudioService extends IAudioService.Stub {
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 @@ public class AudioService extends IAudioService.Stub {
// 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 @@ public class AudioService extends IAudioService.Stub {
}
@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 @@ public class AudioService extends IAudioService.Stub {
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.content.ComponentName;
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 @@ interface IAudioService {
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 @@ package android.media;
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 @@ import java.io.IOException;
* @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 @@ public class Ringtone {
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 class Ringtone {
*/
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 @@ public class Ringtone {
return title;
}
-
- private void openMediaPlayer() throws IOException {
- if (mAudio != null) {
- mAudio.release();
+
+ /**
+ * 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();
+
+ mUri = uri;
+ if (mUri == null) {
+ return;
}
- 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());
+
+ // 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);
}
- } else {
- throw new IOException("No data source set.");
}
- mAudio.setAudioStreamType(mStreamType);
- mAudio.prepare();
- }
-
- void open(FileDescriptor fd) throws IOException {
- mFileDescriptor = fd;
- openMediaPlayer();
- }
- void open(AssetFileDescriptor fd) throws IOException {
- mAssetFileDescriptor = fd;
- openMediaPlayer();
+ if (LOGD) {
+ if (mLocalPlayer != null) {
+ Log.d(TAG, "Successfully created local player");
+ } else {
+ Log.d(TAG, "Problem opening; delegating to remote player");
+ }
+ }
}
- void open(Uri uri) throws IOException {
- mUri = uri;
- openMediaPlayer();
+ /** {@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 @@ public class Ringtone {
* 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 @@ public class Ringtone {
* @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 @@ public class RingtoneManager {
* @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 @@ public class SystemUIService extends Service {
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
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 @@ import java.util.LinkedList;
/**
* @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 @@ public class NotificationPlayer implements OnCompletionListener {
}
}
}
-
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.pm.PackageManager.NameNotFoundException;
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.IBinder;
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.AccessibilityEvent;
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 java.util.HashSet;
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 @@ public class NotificationManagerService extends INotificationManager.Stub
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 @@ public class NotificationManagerService extends INotificationManager.Stub
// 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 @@ public class NotificationManagerService extends INotificationManager.Stub
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 @@ public class NotificationManagerService extends INotificationManager.Stub
identity = Binder.clearCallingIdentity();
try {
mVibrator.cancel();
- }
- finally {
+ } finally {
Binder.restoreCallingIdentity(identity);
}
@@ -570,8 +572,6 @@ public class NotificationManagerService extends INotificationManager.Stub
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 @@ public class NotificationManagerService extends INotificationManager.Stub
}
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 @@ public class NotificationManagerService extends INotificationManager.Stub
// 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 @@ public class NotificationManagerService extends INotificationManager.Stub
// 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 @@ public class NotificationManagerService extends INotificationManager.Stub
}
pw.println(" mSoundNotification=" + mSoundNotification);
- pw.println(" mSound=" + mSound);
pw.println(" mVibrateNotification=" + mVibrateNotification);
pw.println(" mDisabledNotifications=0x" + Integer.toHexString(mDisabledNotifications));
pw.println(" mSystemReady=" + mSystemReady);