summaryrefslogtreecommitdiffstats
path: root/services
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 /services
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
Diffstat (limited to 'services')
-rwxr-xr-xservices/java/com/android/server/NotificationManagerService.java90
-rw-r--r--services/java/com/android/server/NotificationPlayer.java341
2 files changed, 49 insertions, 382 deletions
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);
diff --git a/services/java/com/android/server/NotificationPlayer.java b/services/java/com/android/server/NotificationPlayer.java
deleted file mode 100644
index 52d2381..0000000
--- a/services/java/com/android/server/NotificationPlayer.java
+++ /dev/null
@@ -1,341 +0,0 @@
-/*
- * Copyright (C) 2008 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.server;
-
-import android.content.Context;
-import android.media.AudioManager;
-import android.media.MediaPlayer;
-import android.media.MediaPlayer.OnCompletionListener;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.PowerManager;
-import android.os.SystemClock;
-import android.util.Log;
-
-import java.io.IOException;
-import java.lang.IllegalStateException;
-import java.lang.Thread;
-import java.util.LinkedList;
-
-/**
- * @hide
- * This class is provides the same interface and functionality as android.media.AsyncPlayer
- * 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.
- */
-public class NotificationPlayer implements OnCompletionListener {
- private static final int PLAY = 1;
- private static final int STOP = 2;
- private static final boolean mDebug = false;
-
- private static final class Command {
- int code;
- Context context;
- Uri uri;
- boolean looping;
- int stream;
- long requestTime;
-
- public String toString() {
- return "{ code=" + code + " looping=" + looping + " stream=" + stream
- + " uri=" + uri + " }";
- }
- }
-
- private LinkedList<Command> mCmdQueue = new LinkedList();
-
- private Looper mLooper;
-
- /*
- * Besides the use of audio focus, the only implementation difference between AsyncPlayer and
- * NotificationPlayer resides in the creation of the MediaPlayer. For the completion callback,
- * OnCompletionListener, to be called at the end of the playback, the MediaPlayer needs to
- * be created with a looper running so its event handler is not null.
- */
- private final class CreationAndCompletionThread extends Thread {
- public Command mCmd;
- public CreationAndCompletionThread(Command cmd) {
- super();
- mCmd = cmd;
- }
-
- public void run() {
- Looper.prepare();
- mLooper = Looper.myLooper();
- synchronized(this) {
- AudioManager audioManager =
- (AudioManager) mCmd.context.getSystemService(Context.AUDIO_SERVICE);
- try {
- MediaPlayer player = new MediaPlayer();
- player.setAudioStreamType(mCmd.stream);
- player.setDataSource(mCmd.context, mCmd.uri);
- player.setLooping(mCmd.looping);
- player.prepare();
- if ((mCmd.uri != null) && (mCmd.uri.getEncodedPath() != null)
- && (mCmd.uri.getEncodedPath().length() > 0)) {
- if (mCmd.looping) {
- audioManager.requestAudioFocus(null, mCmd.stream,
- AudioManager.AUDIOFOCUS_GAIN);
- } else {
- audioManager.requestAudioFocus(null, mCmd.stream,
- AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
- }
- }
- player.setOnCompletionListener(NotificationPlayer.this);
- player.start();
- if (mPlayer != null) {
- mPlayer.release();
- }
- mPlayer = player;
- }
- catch (Exception e) {
- Log.w(mTag, "error loading sound for " + mCmd.uri, e);
- }
- mAudioManager = audioManager;
- this.notify();
- }
- Looper.loop();
- }
- };
-
- private void startSound(Command cmd) {
- // Preparing can be slow, so if there is something else
- // is playing, let it continue until we're done, so there
- // is less of a glitch.
- try {
- if (mDebug) Log.d(mTag, "Starting playback");
- //-----------------------------------
- // This is were we deviate from the AsyncPlayer implementation and create the
- // MediaPlayer in a new thread with which we're synchronized
- synchronized(mCompletionHandlingLock) {
- // if another sound was already playing, it doesn't matter we won't get notified
- // of the completion, since only the completion notification of the last sound
- // matters
- if((mLooper != null)
- && (mLooper.getThread().getState() != Thread.State.TERMINATED)) {
- mLooper.quit();
- }
- mCompletionThread = new CreationAndCompletionThread(cmd);
- synchronized(mCompletionThread) {
- mCompletionThread.start();
- mCompletionThread.wait();
- }
- }
- //-----------------------------------
-
- long delay = SystemClock.uptimeMillis() - cmd.requestTime;
- if (delay > 1000) {
- Log.w(mTag, "Notification sound delayed by " + delay + "msecs");
- }
- }
- catch (Exception e) {
- Log.w(mTag, "error loading sound for " + cmd.uri, e);
- }
- }
-
- private final class CmdThread extends java.lang.Thread {
- CmdThread() {
- super("NotificationPlayer-" + mTag);
- }
-
- public void run() {
- while (true) {
- Command cmd = null;
-
- synchronized (mCmdQueue) {
- if (mDebug) Log.d(mTag, "RemoveFirst");
- cmd = mCmdQueue.removeFirst();
- }
-
- switch (cmd.code) {
- case PLAY:
- if (mDebug) Log.d(mTag, "PLAY");
- startSound(cmd);
- break;
- case STOP:
- if (mDebug) Log.d(mTag, "STOP");
- if (mPlayer != null) {
- long delay = SystemClock.uptimeMillis() - cmd.requestTime;
- if (delay > 1000) {
- Log.w(mTag, "Notification stop delayed by " + delay + "msecs");
- }
- mPlayer.stop();
- mPlayer.release();
- mPlayer = null;
- mAudioManager.abandonAudioFocus(null);
- mAudioManager = null;
- if((mLooper != null)
- && (mLooper.getThread().getState() != Thread.State.TERMINATED)) {
- mLooper.quit();
- }
- } else {
- Log.w(mTag, "STOP command without a player");
- }
- break;
- }
-
- synchronized (mCmdQueue) {
- if (mCmdQueue.size() == 0) {
- // nothing left to do, quit
- // doing this check after we're done prevents the case where they
- // added it during the operation from spawning two threads and
- // trying to do them in parallel.
- mThread = null;
- releaseWakeLock();
- return;
- }
- }
- }
- }
- }
-
- public void onCompletion(MediaPlayer mp) {
- if (mAudioManager != null) {
- mAudioManager.abandonAudioFocus(null);
- }
- // if there are no more sounds to play, end the Looper to listen for media completion
- synchronized (mCmdQueue) {
- if (mCmdQueue.size() == 0) {
- synchronized(mCompletionHandlingLock) {
- if(mLooper != null) {
- mLooper.quit();
- }
- mCompletionThread = null;
- }
- }
- }
- }
-
- private String mTag;
- private CmdThread mThread;
- private CreationAndCompletionThread mCompletionThread;
- private final Object mCompletionHandlingLock = new Object();
- private MediaPlayer mPlayer;
- private PowerManager.WakeLock mWakeLock;
- private AudioManager mAudioManager;
-
- // The current state according to the caller. Reality lags behind
- // because of the asynchronous nature of this class.
- private int mState = STOP;
-
- /**
- * Construct a NotificationPlayer object.
- *
- * @param tag a string to use for debugging
- */
- public NotificationPlayer(String tag) {
- if (tag != null) {
- mTag = tag;
- } else {
- mTag = "NotificationPlayer";
- }
- }
-
- /**
- * Start playing the sound. It will actually start playing at some
- * point in the future. There are no guarantees about latency here.
- * Calling this before another audio file is done playing will stop
- * that one and start the new one.
- *
- * @param context Your application's context.
- * @param uri The URI to play. (see {@link MediaPlayer#setDataSource(Context, Uri)})
- * @param looping Whether the audio should loop forever.
- * (see {@link MediaPlayer#setLooping(boolean)})
- * @param stream the AudioStream to use.
- * (see {@link MediaPlayer#setAudioStreamType(int)})
- */
- public void play(Context context, Uri uri, boolean looping, int stream) {
- Command cmd = new Command();
- cmd.requestTime = SystemClock.uptimeMillis();
- cmd.code = PLAY;
- cmd.context = context;
- cmd.uri = uri;
- cmd.looping = looping;
- cmd.stream = stream;
- synchronized (mCmdQueue) {
- enqueueLocked(cmd);
- mState = PLAY;
- }
- }
-
- /**
- * Stop a previously played sound. It can't be played again or unpaused
- * at this point. Calling this multiple times has no ill effects.
- */
- public void stop() {
- synchronized (mCmdQueue) {
- // This check allows stop to be called multiple times without starting
- // a thread that ends up doing nothing.
- if (mState != STOP) {
- Command cmd = new Command();
- cmd.requestTime = SystemClock.uptimeMillis();
- cmd.code = STOP;
- enqueueLocked(cmd);
- mState = STOP;
- }
- }
- }
-
- private void enqueueLocked(Command cmd) {
- mCmdQueue.add(cmd);
- if (mThread == null) {
- acquireWakeLock();
- mThread = new CmdThread();
- mThread.start();
- }
- }
-
- /**
- * We want to hold a wake lock while we do the prepare and play. The stop probably is
- * optional, but it won't hurt to have it too. The problem is that if you start a sound
- * while you're holding a wake lock (e.g. an alarm starting a notification), you want the
- * sound to play, but if the CPU turns off before mThread gets to work, it won't. The
- * simplest way to deal with this is to make it so there is a wake lock held while the
- * thread is starting or running. You're going to need the WAKE_LOCK permission if you're
- * going to call this.
- *
- * This must be called before the first time play is called.
- *
- * @hide
- */
- public void setUsesWakeLock(Context context) {
- if (mWakeLock != null || mThread != null) {
- // if either of these has happened, we've already played something.
- // and our releases will be out of sync.
- throw new RuntimeException("assertion failed mWakeLock=" + mWakeLock
- + " mThread=" + mThread);
- }
- PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
- mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, mTag);
- }
-
- private void acquireWakeLock() {
- if (mWakeLock != null) {
- mWakeLock.acquire();
- }
- }
-
- private void releaseWakeLock() {
- if (mWakeLock != null) {
- mWakeLock.release();
- }
- }
-}
-