Merge "NotificationPlayer: fix race conditions" into oc-mr1-dev
am: 20e1c66a78
Change-Id: Iba4e88e036b564c8ccbd615beb9ec7b608e7230e
diff --git a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
index 50720e9..b5c0d53 100644
--- a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
@@ -29,6 +29,8 @@
import android.os.SystemClock;
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
+
import java.util.LinkedList;
/**
@@ -57,8 +59,12 @@
}
}
- private LinkedList<Command> mCmdQueue = new LinkedList();
+ private final LinkedList<Command> mCmdQueue = new LinkedList<Command>();
+ private final Object mCompletionHandlingLock = new Object();
+ @GuardedBy("mCompletionHandlingLock")
+ private CreationAndCompletionThread mCompletionThread;
+ @GuardedBy("mCompletionHandlingLock")
private Looper mLooper;
/*
@@ -76,7 +82,10 @@
public void run() {
Looper.prepare();
+ // ok to modify mLooper as here we are
+ // synchronized on mCompletionHandlingLock due to the Object.wait() in startSound(cmd)
mLooper = Looper.myLooper();
+ if (DEBUG) Log.d(mTag, "in run: new looper " + mLooper);
synchronized(this) {
AudioManager audioManager =
(AudioManager) mCmd.context.getSystemService(Context.AUDIO_SERVICE);
@@ -97,7 +106,7 @@
if ((mCmd.uri != null) && (mCmd.uri.getEncodedPath() != null)
&& (mCmd.uri.getEncodedPath().length() > 0)) {
if (!audioManager.isMusicActiveRemotely()) {
- synchronized(mQueueAudioFocusLock) {
+ synchronized (mQueueAudioFocusLock) {
if (mAudioManagerWithAudioFocus == null) {
if (DEBUG) Log.d(mTag, "requesting AudioFocus");
int focusGain = AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK;
@@ -129,7 +138,9 @@
Log.e(mTag, "Exception while sleeping to sync notification playback"
+ " with ducking", e);
}
+ if (DEBUG) { Log.d(mTag, "player.start"); }
if (mPlayer != null) {
+ if (DEBUG) { Log.d(mTag, "mPlayer.release"); }
mPlayer.release();
}
mPlayer = player;
@@ -148,7 +159,7 @@
// is playing, let it continue until we're done, so there
// is less of a glitch.
try {
- if (DEBUG) Log.d(mTag, "Starting playback");
+ if (DEBUG) { Log.d(mTag, "startSound()"); }
//-----------------------------------
// This is were we deviate from the AsyncPlayer implementation and create the
// MediaPlayer in a new thread with which we're synchronized
@@ -158,10 +169,11 @@
// matters
if((mLooper != null)
&& (mLooper.getThread().getState() != Thread.State.TERMINATED)) {
+ if (DEBUG) { Log.d(mTag, "in startSound quitting looper " + mLooper); }
mLooper.quit();
}
mCompletionThread = new CreationAndCompletionThread(cmd);
- synchronized(mCompletionThread) {
+ synchronized (mCompletionThread) {
mCompletionThread.start();
mCompletionThread.wait();
}
@@ -209,13 +221,18 @@
mPlayer = null;
synchronized(mQueueAudioFocusLock) {
if (mAudioManagerWithAudioFocus != null) {
+ if (DEBUG) { Log.d(mTag, "in STOP: abandonning AudioFocus"); }
mAudioManagerWithAudioFocus.abandonAudioFocus(null);
mAudioManagerWithAudioFocus = null;
}
}
- if((mLooper != null)
- && (mLooper.getThread().getState() != Thread.State.TERMINATED)) {
- mLooper.quit();
+ synchronized (mCompletionHandlingLock) {
+ if ((mLooper != null) &&
+ (mLooper.getThread().getState() != Thread.State.TERMINATED))
+ {
+ if (DEBUG) { Log.d(mTag, "in STOP: quitting looper "+ mLooper); }
+ mLooper.quit();
+ }
}
} else {
Log.w(mTag, "STOP command without a player");
@@ -250,9 +267,11 @@
}
// 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) {
+ synchronized(mCompletionHandlingLock) {
+ if (DEBUG) { Log.d(mTag, "onCompletion queue size=" + mCmdQueue.size()); }
+ if ((mCmdQueue.size() == 0)) {
+ if (mLooper != null) {
+ if (DEBUG) { Log.d(mTag, "in onCompletion quitting looper " + mLooper); }
mLooper.quit();
}
mCompletionThread = null;
@@ -269,13 +288,20 @@
}
private String mTag;
+
+ @GuardedBy("mCmdQueue")
private CmdThread mThread;
- private CreationAndCompletionThread mCompletionThread;
- private final Object mCompletionHandlingLock = new Object();
+
private MediaPlayer mPlayer;
+
+
+ @GuardedBy("mCmdQueue")
private PowerManager.WakeLock mWakeLock;
+
private final Object mQueueAudioFocusLock = new Object();
- private AudioManager mAudioManagerWithAudioFocus; // synchronized on mQueueAudioFocusLock
+ @GuardedBy("mQueueAudioFocusLock")
+ private AudioManager mAudioManagerWithAudioFocus;
+
private int mNotificationRampTimeMs = 0;
// The current state according to the caller. Reality lags behind
@@ -311,6 +337,7 @@
*/
@Deprecated
public void play(Context context, Uri uri, boolean looping, int stream) {
+ if (DEBUG) { Log.d(mTag, "play uri=" + uri.toString()); }
PlayerBase.deprecateStreamTypeForPlayback(stream, "NotificationPlayer", "play");
Command cmd = new Command();
cmd.requestTime = SystemClock.uptimeMillis();
@@ -339,6 +366,7 @@
* (see {@link MediaPlayer#setAudioAttributes(AudioAttributes)})
*/
public void play(Context context, Uri uri, boolean looping, AudioAttributes attributes) {
+ if (DEBUG) { Log.d(mTag, "play uri=" + uri.toString()); }
Command cmd = new Command();
cmd.requestTime = SystemClock.uptimeMillis();
cmd.code = PLAY;
@@ -357,6 +385,7 @@
* at this point. Calling this multiple times has no ill effects.
*/
public void stop() {
+ if (DEBUG) { Log.d(mTag, "stop"); }
synchronized (mCmdQueue) {
// This check allows stop to be called multiple times without starting
// a thread that ends up doing nothing.
@@ -370,6 +399,7 @@
}
}
+ @GuardedBy("mCmdQueue")
private void enqueueLocked(Command cmd) {
mCmdQueue.add(cmd);
if (mThread == null) {
@@ -393,22 +423,26 @@
* @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);
+ synchronized (mCmdQueue) {
+ 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);
}
- PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
- mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, mTag);
}
+ @GuardedBy("mCmdQueue")
private void acquireWakeLock() {
if (mWakeLock != null) {
mWakeLock.acquire();
}
}
+ @GuardedBy("mCmdQueue")
private void releaseWakeLock() {
if (mWakeLock != null) {
mWakeLock.release();