AsyncRingtonePlayer.java revision d931a017a0abea32ad4485a91402b5f62b9ddb0e
11ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon/*
21ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon * Copyright 2014, The Android Open Source Project
31ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon *
41ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon * Licensed under the Apache License, Version 2.0 (the "License");
51ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon * you may not use this file except in compliance with the License.
61ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon * You may obtain a copy of the License at
71ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon *
81ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon *     http://www.apache.org/licenses/LICENSE-2.0
91ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon *
101ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon * Unless required by applicable law or agreed to in writing, software
111ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon * distributed under the License is distributed on an "AS IS" BASIS,
121ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon * See the License for the specific language governing permissions and
141ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon * limitations under the License.
151ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon */
161ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
177cc70b4f0ad1064a4a0dce6056ad82b205887160Tyler Gunnpackage com.android.server.telecom;
181ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
191ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordonimport android.media.Ringtone;
201ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordonimport android.os.Handler;
211ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordonimport android.os.HandlerThread;
221ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordonimport android.os.Message;
231ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
24d931a017a0abea32ad4485a91402b5f62b9ddb0eBrad Ebingerimport com.android.internal.annotations.VisibleForTesting;
2591d43cf9c985cc5a83795f256ef5c46ebb8fbdc1Tyler Gunnimport com.android.internal.util.Preconditions;
261ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
271ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon/**
281ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon * Plays the default ringtone. Uses {@link Ringtone} in a separate thread so that this class can be
291ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon * used from the main thread.
301ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon */
31d931a017a0abea32ad4485a91402b5f62b9ddb0eBrad Ebinger@VisibleForTesting
32d931a017a0abea32ad4485a91402b5f62b9ddb0eBrad Ebingerpublic class AsyncRingtonePlayer {
331ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    // Message codes used with the ringtone thread.
34b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon    private static final int EVENT_PLAY = 1;
35b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon    private static final int EVENT_STOP = 2;
36b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon    private static final int EVENT_REPEAT = 3;
37b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon
38b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon    // The interval in which to restart the ringer.
39b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon    private static final int RESTART_RINGER_MILLIS = 3000;
401ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
411ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    /** Handler running on the ringtone thread. */
421ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    private Handler mHandler;
431ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
441ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    /** The current ringtone. Only used by the ringtone thread. */
451ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    private Ringtone mRingtone;
461ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
471ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    /** Plays the ringtone. */
48d931a017a0abea32ad4485a91402b5f62b9ddb0eBrad Ebinger    public void play(Ringtone ringtone) {
491ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        Log.d(this, "Posting play.");
50d931a017a0abea32ad4485a91402b5f62b9ddb0eBrad Ebinger        if (ringtone == null) {
51d931a017a0abea32ad4485a91402b5f62b9ddb0eBrad Ebinger            Log.i(this, "null (silence -- not playing anything)");
52f43ff474988d4b35c77e0834506d0e15111a4cf1Wenyi Wang            return;
53f43ff474988d4b35c77e0834506d0e15111a4cf1Wenyi Wang        }
545ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordon        postMessage(EVENT_PLAY, true /* shouldCreateHandler */, ringtone);
551ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    }
561ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
571ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    /** Stops playing the ringtone. */
58d931a017a0abea32ad4485a91402b5f62b9ddb0eBrad Ebinger    public void stop() {
591ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        Log.d(this, "Posting stop.");
605ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordon        postMessage(EVENT_STOP, false /* shouldCreateHandler */, null);
611ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    }
621ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
631ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    /**
641ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon     * Posts a message to the ringtone-thread handler. Creates the handler if specified by the
651ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon     * parameter shouldCreateHandler.
661ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon     *
671ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon     * @param messageCode The message to post.
681ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon     * @param shouldCreateHandler True when a handler should be created to handle this message.
691ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon     */
70d931a017a0abea32ad4485a91402b5f62b9ddb0eBrad Ebinger    private void postMessage(int messageCode, boolean shouldCreateHandler, Ringtone ringtone) {
711ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        synchronized(this) {
721ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            if (mHandler == null && shouldCreateHandler) {
731ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon                mHandler = getNewHandler();
741ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            }
751ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
761ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            if (mHandler == null) {
771ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon                Log.d(this, "Message %d skipped because there is no handler.", messageCode);
781ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            } else {
795ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordon                mHandler.obtainMessage(messageCode, ringtone).sendToTarget();
801ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            }
811ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        }
821ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    }
831ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
841ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    /**
851ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon     * Creates a new ringtone Handler running in its own thread.
861ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon     */
871ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    private Handler getNewHandler() {
881ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        Preconditions.checkState(mHandler == null);
891ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
901ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        HandlerThread thread = new HandlerThread("ringtone-player");
911ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        thread.start();
921ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
931ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        return new Handler(thread.getLooper()) {
941ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            @Override
951ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            public void handleMessage(Message msg) {
961ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon                switch(msg.what) {
971ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon                    case EVENT_PLAY:
98d931a017a0abea32ad4485a91402b5f62b9ddb0eBrad Ebinger                        handlePlay((Ringtone) msg.obj);
991ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon                        break;
100b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon                    case EVENT_REPEAT:
101b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon                        handleRepeat();
102b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon                        break;
1031ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon                    case EVENT_STOP:
1041ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon                        handleStop();
1051ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon                        break;
1061ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon                }
1071ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            }
1081ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        };
1091ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    }
1101ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
1111ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    /**
1121ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon     * Starts the actual playback of the ringtone. Executes on ringtone-thread.
1131ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon     */
114d931a017a0abea32ad4485a91402b5f62b9ddb0eBrad Ebinger    private void handlePlay(Ringtone ringtone) {
115b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon        // don't bother with any of this if there is an EVENT_STOP waiting.
116b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon        if (mHandler.hasMessages(EVENT_STOP)) {
117b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon            return;
118b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon        }
119b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon
1201ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        ThreadUtil.checkNotOnMainThread();
1211ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        Log.i(this, "Play ringtone.");
1221ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
1231ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        if (mRingtone == null) {
124d931a017a0abea32ad4485a91402b5f62b9ddb0eBrad Ebinger            mRingtone = ringtone;
1251ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        }
1261ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
127b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon        handleRepeat();
128b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon    }
129b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon
130b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon    private void handleRepeat() {
131b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon        if (mRingtone == null) {
132b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon            return;
133b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon        }
134b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon
1351ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        if (mRingtone.isPlaying()) {
1361ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            Log.d(this, "Ringtone already playing.");
1371ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        } else {
1381ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            mRingtone.play();
139b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon            Log.i(this, "Repeat ringtone.");
140b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon        }
141b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon
142b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon        // Repost event to restart ringer in {@link RESTART_RINGER_MILLIS}.
143b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon        synchronized(this) {
144b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon            if (!mHandler.hasMessages(EVENT_REPEAT)) {
145b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon                mHandler.sendEmptyMessageDelayed(EVENT_REPEAT, RESTART_RINGER_MILLIS);
146b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon            }
1471ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        }
1481ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    }
1491ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
1501ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    /**
1511ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon     * Stops the playback of the ringtone. Executes on the ringtone-thread.
1521ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon     */
1531ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    private void handleStop() {
1541ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        ThreadUtil.checkNotOnMainThread();
1551ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        Log.i(this, "Stop ringtone.");
1561ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
1571ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        if (mRingtone != null) {
1581ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            Log.d(this, "Ringtone.stop() invoked.");
1591ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            mRingtone.stop();
1601ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            mRingtone = null;
1611ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        }
1621ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
1631ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        synchronized(this) {
164b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon            // At the time that STOP is handled, there should be no need for repeat messages in the
165b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon            // queue.
166b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon            mHandler.removeMessages(EVENT_REPEAT);
167b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon
1681ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            if (mHandler.hasMessages(EVENT_PLAY)) {
169b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon                Log.v(this, "Keeping alive ringtone thread for subsequent play request.");
1701ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            } else {
1711ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon                mHandler.removeMessages(EVENT_STOP);
1721ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon                mHandler.getLooper().quitSafely();
1731ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon                mHandler = null;
1741ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon                Log.v(this, "Handler cleared.");
1751ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            }
1761ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        }
1771ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    }
1781ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon}
179