diff options
-rw-r--r-- | Android.mk | 1 | ||||
-rw-r--r-- | core/java/android/app/Notification.java | 3 | ||||
-rw-r--r-- | core/res/AndroidManifest.xml | 5 | ||||
-rw-r--r-- | media/java/android/media/AudioManager.java | 8 | ||||
-rw-r--r-- | media/java/android/media/AudioService.java | 17 | ||||
-rw-r--r-- | media/java/android/media/IAudioService.aidl | 5 | ||||
-rw-r--r-- | media/java/android/media/IRingtonePlayer.aidl | 33 | ||||
-rw-r--r-- | media/java/android/media/Ringtone.java | 177 | ||||
-rw-r--r-- | media/java/android/media/RingtoneManager.java | 7 | ||||
-rw-r--r-- | packages/SystemUI/AndroidManifest.xml | 2 | ||||
-rw-r--r-- | packages/SystemUI/src/com/android/systemui/SystemUIService.java | 1 | ||||
-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.java | 164 | ||||
-rwxr-xr-x | services/java/com/android/server/NotificationManagerService.java | 90 |
14 files changed, 390 insertions, 128 deletions
@@ -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); |