19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2008 The Android Open Source Project
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License.
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.media;
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
19157784b575c5ebf4225343a085af43beb76bc2f3Jean-Michel Triviimport android.annotation.NonNull;
209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.Context;
219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.net.Uri;
229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.PowerManager;
238cc42c5230eb02db8c28391dd15f83851df4f948Dave Sparksimport android.os.SystemClock;
249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.Log;
259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
268cc42c5230eb02db8c28391dd15f83851df4f948Dave Sparksimport java.util.LinkedList;
279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Plays a series of audio URIs, but does all the hard work on another thread
309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * so that any slowness with preparing or loading doesn't block the calling thread.
319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic class AsyncPlayer {
339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int PLAY = 1;
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int STOP = 2;
358cc42c5230eb02db8c28391dd15f83851df4f948Dave Sparks    private static final boolean mDebug = false;
369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final class Command {
389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int code;
399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Context context;
409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Uri uri;
419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean looping;
42157784b575c5ebf4225343a085af43beb76bc2f3Jean-Michel Trivi        AudioAttributes attributes;
438cc42c5230eb02db8c28391dd15f83851df4f948Dave Sparks        long requestTime;
449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String toString() {
46157784b575c5ebf4225343a085af43beb76bc2f3Jean-Michel Trivi            return "{ code=" + code + " looping=" + looping + " attr=" + attributes
479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    + " uri=" + uri + " }";
489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5130c918ce7fbe171944b28fc91b3f22b3d631872dGlenn Kasten    private final LinkedList<Command> mCmdQueue = new LinkedList();
528cc42c5230eb02db8c28391dd15f83851df4f948Dave Sparks
538cc42c5230eb02db8c28391dd15f83851df4f948Dave Sparks    private void startSound(Command cmd) {
548cc42c5230eb02db8c28391dd15f83851df4f948Dave Sparks        // Preparing can be slow, so if there is something else
558cc42c5230eb02db8c28391dd15f83851df4f948Dave Sparks        // is playing, let it continue until we're done, so there
568cc42c5230eb02db8c28391dd15f83851df4f948Dave Sparks        // is less of a glitch.
578cc42c5230eb02db8c28391dd15f83851df4f948Dave Sparks        try {
588cc42c5230eb02db8c28391dd15f83851df4f948Dave Sparks            if (mDebug) Log.d(mTag, "Starting playback");
598cc42c5230eb02db8c28391dd15f83851df4f948Dave Sparks            MediaPlayer player = new MediaPlayer();
60157784b575c5ebf4225343a085af43beb76bc2f3Jean-Michel Trivi            player.setAudioAttributes(cmd.attributes);
618cc42c5230eb02db8c28391dd15f83851df4f948Dave Sparks            player.setDataSource(cmd.context, cmd.uri);
628cc42c5230eb02db8c28391dd15f83851df4f948Dave Sparks            player.setLooping(cmd.looping);
638cc42c5230eb02db8c28391dd15f83851df4f948Dave Sparks            player.prepare();
648cc42c5230eb02db8c28391dd15f83851df4f948Dave Sparks            player.start();
658cc42c5230eb02db8c28391dd15f83851df4f948Dave Sparks            if (mPlayer != null) {
668cc42c5230eb02db8c28391dd15f83851df4f948Dave Sparks                mPlayer.release();
678cc42c5230eb02db8c28391dd15f83851df4f948Dave Sparks            }
688cc42c5230eb02db8c28391dd15f83851df4f948Dave Sparks            mPlayer = player;
698cc42c5230eb02db8c28391dd15f83851df4f948Dave Sparks            long delay = SystemClock.uptimeMillis() - cmd.requestTime;
708cc42c5230eb02db8c28391dd15f83851df4f948Dave Sparks            if (delay > 1000) {
718cc42c5230eb02db8c28391dd15f83851df4f948Dave Sparks                Log.w(mTag, "Notification sound delayed by " + delay + "msecs");
728cc42c5230eb02db8c28391dd15f83851df4f948Dave Sparks            }
738cc42c5230eb02db8c28391dd15f83851df4f948Dave Sparks        }
744ba297f3ed2def2845edb18723e43379984bc9ffMarco Nelissen        catch (Exception e) {
758cc42c5230eb02db8c28391dd15f83851df4f948Dave Sparks            Log.w(mTag, "error loading sound for " + cmd.uri, e);
768cc42c5230eb02db8c28391dd15f83851df4f948Dave Sparks        }
778cc42c5230eb02db8c28391dd15f83851df4f948Dave Sparks    }
788cc42c5230eb02db8c28391dd15f83851df4f948Dave Sparks
799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private final class Thread extends java.lang.Thread {
809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Thread() {
819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            super("AsyncPlayer-" + mTag);
829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void run() {
859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            while (true) {
869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Command cmd = null;
879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
888cc42c5230eb02db8c28391dd15f83851df4f948Dave Sparks                synchronized (mCmdQueue) {
898cc42c5230eb02db8c28391dd15f83851df4f948Dave Sparks                    if (mDebug) Log.d(mTag, "RemoveFirst");
908cc42c5230eb02db8c28391dd15f83851df4f948Dave Sparks                    cmd = mCmdQueue.removeFirst();
919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                switch (cmd.code) {
949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case PLAY:
958cc42c5230eb02db8c28391dd15f83851df4f948Dave Sparks                    if (mDebug) Log.d(mTag, "PLAY");
968cc42c5230eb02db8c28391dd15f83851df4f948Dave Sparks                    startSound(cmd);
979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case STOP:
998cc42c5230eb02db8c28391dd15f83851df4f948Dave Sparks                    if (mDebug) Log.d(mTag, "STOP");
1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (mPlayer != null) {
1018cc42c5230eb02db8c28391dd15f83851df4f948Dave Sparks                        long delay = SystemClock.uptimeMillis() - cmd.requestTime;
1028cc42c5230eb02db8c28391dd15f83851df4f948Dave Sparks                        if (delay > 1000) {
1038cc42c5230eb02db8c28391dd15f83851df4f948Dave Sparks                            Log.w(mTag, "Notification stop delayed by " + delay + "msecs");
1048cc42c5230eb02db8c28391dd15f83851df4f948Dave Sparks                        }
1059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        mPlayer.stop();
1069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        mPlayer.release();
1079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        mPlayer = null;
1089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else {
1099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        Log.w(mTag, "STOP command without a player");
1109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
1139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1148cc42c5230eb02db8c28391dd15f83851df4f948Dave Sparks                synchronized (mCmdQueue) {
1158cc42c5230eb02db8c28391dd15f83851df4f948Dave Sparks                    if (mCmdQueue.size() == 0) {
1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        // nothing left to do, quit
1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        // doing this check after we're done prevents the case where they
1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        // added it during the operation from spawning two threads and
1199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        // trying to do them in parallel.
1209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        mThread = null;
1219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        releaseWakeLock();
1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        return;
1239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
1249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
1259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private String mTag;
1309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Thread mThread;
1319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private MediaPlayer mPlayer;
1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private PowerManager.WakeLock mWakeLock;
1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // The current state according to the caller.  Reality lags behind
1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // because of the asynchronous nature of this class.
1369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mState = STOP;
1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Construct an AsyncPlayer object.
1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param tag a string to use for debugging
1429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public AsyncPlayer(String tag) {
1449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (tag != null) {
1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mTag = tag;
1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mTag = "AsyncPlayer";
1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Start playing the sound.  It will actually start playing at some
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * point in the future.  There are no guarantees about latency here.
1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Calling this before another audio file is done playing will stop
1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * that one and start the new one.
1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param context Your application's context.
1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param uri The URI to play.  (see {@link MediaPlayer#setDataSource(Context, Uri)})
1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param looping Whether the audio should loop forever.
1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *          (see {@link MediaPlayer#setLooping(boolean)})
1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param stream the AudioStream to use.
1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *          (see {@link MediaPlayer#setAudioStreamType(int)})
163157784b575c5ebf4225343a085af43beb76bc2f3Jean-Michel Trivi     * @deprecated use {@link #play(Context, Uri, boolean, AudioAttributes)} instead
1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void play(Context context, Uri uri, boolean looping, int stream) {
166157784b575c5ebf4225343a085af43beb76bc2f3Jean-Michel Trivi        if (context == null || uri == null) {
167157784b575c5ebf4225343a085af43beb76bc2f3Jean-Michel Trivi            return;
168157784b575c5ebf4225343a085af43beb76bc2f3Jean-Michel Trivi        }
169157784b575c5ebf4225343a085af43beb76bc2f3Jean-Michel Trivi        try {
170157784b575c5ebf4225343a085af43beb76bc2f3Jean-Michel Trivi            play(context, uri, looping,
171157784b575c5ebf4225343a085af43beb76bc2f3Jean-Michel Trivi                    new AudioAttributes.Builder().setInternalLegacyStreamType(stream).build());
172157784b575c5ebf4225343a085af43beb76bc2f3Jean-Michel Trivi        } catch (IllegalArgumentException e) {
173157784b575c5ebf4225343a085af43beb76bc2f3Jean-Michel Trivi            Log.e(mTag, "Call to deprecated AsyncPlayer.play() method caused:", e);
174157784b575c5ebf4225343a085af43beb76bc2f3Jean-Michel Trivi        }
175157784b575c5ebf4225343a085af43beb76bc2f3Jean-Michel Trivi    }
176157784b575c5ebf4225343a085af43beb76bc2f3Jean-Michel Trivi
177157784b575c5ebf4225343a085af43beb76bc2f3Jean-Michel Trivi    /**
178157784b575c5ebf4225343a085af43beb76bc2f3Jean-Michel Trivi     * Start playing the sound.  It will actually start playing at some
179157784b575c5ebf4225343a085af43beb76bc2f3Jean-Michel Trivi     * point in the future.  There are no guarantees about latency here.
180157784b575c5ebf4225343a085af43beb76bc2f3Jean-Michel Trivi     * Calling this before another audio file is done playing will stop
181157784b575c5ebf4225343a085af43beb76bc2f3Jean-Michel Trivi     * that one and start the new one.
182157784b575c5ebf4225343a085af43beb76bc2f3Jean-Michel Trivi     *
183157784b575c5ebf4225343a085af43beb76bc2f3Jean-Michel Trivi     * @param context the non-null application's context.
184157784b575c5ebf4225343a085af43beb76bc2f3Jean-Michel Trivi     * @param uri the non-null URI to play.  (see {@link MediaPlayer#setDataSource(Context, Uri)})
185157784b575c5ebf4225343a085af43beb76bc2f3Jean-Michel Trivi     * @param looping whether the audio should loop forever.
186157784b575c5ebf4225343a085af43beb76bc2f3Jean-Michel Trivi     *          (see {@link MediaPlayer#setLooping(boolean)})
187157784b575c5ebf4225343a085af43beb76bc2f3Jean-Michel Trivi     * @param attributes the non-null {@link AudioAttributes} to use.
188157784b575c5ebf4225343a085af43beb76bc2f3Jean-Michel Trivi     *          (see {@link MediaPlayer#setAudioAttributes(AudioAttributes)})
189157784b575c5ebf4225343a085af43beb76bc2f3Jean-Michel Trivi     * @throws IllegalArgumentException
190157784b575c5ebf4225343a085af43beb76bc2f3Jean-Michel Trivi     */
191157784b575c5ebf4225343a085af43beb76bc2f3Jean-Michel Trivi    public void play(@NonNull Context context, @NonNull Uri uri, boolean looping,
192157784b575c5ebf4225343a085af43beb76bc2f3Jean-Michel Trivi            @NonNull AudioAttributes attributes) throws IllegalArgumentException {
193157784b575c5ebf4225343a085af43beb76bc2f3Jean-Michel Trivi        if (context == null || uri == null || attributes == null) {
194157784b575c5ebf4225343a085af43beb76bc2f3Jean-Michel Trivi            throw new IllegalArgumentException("Illegal null AsyncPlayer.play() argument");
195157784b575c5ebf4225343a085af43beb76bc2f3Jean-Michel Trivi        }
1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Command cmd = new Command();
1978cc42c5230eb02db8c28391dd15f83851df4f948Dave Sparks        cmd.requestTime = SystemClock.uptimeMillis();
1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        cmd.code = PLAY;
1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        cmd.context = context;
2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        cmd.uri = uri;
2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        cmd.looping = looping;
202157784b575c5ebf4225343a085af43beb76bc2f3Jean-Michel Trivi        cmd.attributes = attributes;
2038cc42c5230eb02db8c28391dd15f83851df4f948Dave Sparks        synchronized (mCmdQueue) {
2049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            enqueueLocked(cmd);
2059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mState = PLAY;
2069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
208157784b575c5ebf4225343a085af43beb76bc2f3Jean-Michel Trivi
2099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Stop a previously played sound.  It can't be played again or unpaused
2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * at this point.  Calling this multiple times has no ill effects.
2129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void stop() {
2148cc42c5230eb02db8c28391dd15f83851df4f948Dave Sparks        synchronized (mCmdQueue) {
2159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // This check allows stop to be called multiple times without starting
2169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // a thread that ends up doing nothing.
2179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mState != STOP) {
2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Command cmd = new Command();
2198cc42c5230eb02db8c28391dd15f83851df4f948Dave Sparks                cmd.requestTime = SystemClock.uptimeMillis();
2209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                cmd.code = STOP;
2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                enqueueLocked(cmd);
2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mState = STOP;
2239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void enqueueLocked(Command cmd) {
2288cc42c5230eb02db8c28391dd15f83851df4f948Dave Sparks        mCmdQueue.add(cmd);
2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mThread == null) {
2309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            acquireWakeLock();
2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mThread = new Thread();
2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mThread.start();
2339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * We want to hold a wake lock while we do the prepare and play.  The stop probably is
2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * optional, but it won't hurt to have it too.  The problem is that if you start a sound
2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * while you're holding a wake lock (e.g. an alarm starting a notification), you want the
2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * sound to play, but if the CPU turns off before mThread gets to work, it won't.  The
2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * simplest way to deal with this is to make it so there is a wake lock held while the
2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * thread is starting or running.  You're going to need the WAKE_LOCK permission if you're
2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * going to call this.
2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * This must be called before the first time play is called.
2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @hide
2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setUsesWakeLock(Context context) {
2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mWakeLock != null || mThread != null) {
2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // if either of these has happened, we've already played something.
2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // and our releases will be out of sync.
2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new RuntimeException("assertion failed mWakeLock=" + mWakeLock
2549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    + " mThread=" + mThread);
2559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
2579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, mTag);
2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void acquireWakeLock() {
2619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mWakeLock != null) {
2629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mWakeLock.acquire();
2639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void releaseWakeLock() {
2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mWakeLock != null) {
2689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mWakeLock.release();
2699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
2729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
273