summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeff Sharkey <jsharkey@android.com>2012-04-26 17:30:34 -0700
committerJeff Sharkey <jsharkey@android.com>2012-04-27 14:15:05 -0700
commit098d580cc2bb6c0891c756a4e5230c6c6b0d2376 (patch)
treebd5ae0f9e87a5516e8c481cebc54321272152491
parentf5d70fd2add31cdb2e4ca1e931b47db95fa4b3e0 (diff)
downloadframeworks_base-098d580cc2bb6c0891c756a4e5230c6c6b0d2376.zip
frameworks_base-098d580cc2bb6c0891c756a4e5230c6c6b0d2376.tar.gz
frameworks_base-098d580cc2bb6c0891c756a4e5230c6c6b0d2376.tar.bz2
Migrate ringtone playback to SystemUI.
Introduce IRingtonePlayer, which handles playback for both Ringtone objects and Notifications. SystemUI now hosts this player, which it registers with AudioService. It also keeps MediaPlayer instances warm, and cleans them up after stop() or Binder death. Move both Ringtone and NotificationManagerService to play back audio through this new interface. Bug: 6376128, 6350773 Change-Id: I1dcb86d16ee3c4f07cdb2248d33dcff4ead3609a
-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);