AsyncRingtonePlayer.java revision f43ff474988d4b35c77e0834506d0e15111a4cf1
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
1991d43cf9c985cc5a83795f256ef5c46ebb8fbdc1Tyler Gunnimport android.content.Context;
20b64c150bde86ae22787aec4eaa16b9e3b3420f9dSantos Cordonimport android.media.AudioManager;
211ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordonimport android.media.Ringtone;
221ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordonimport android.media.RingtoneManager;
235ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordonimport android.net.Uri;
241ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordonimport android.os.Handler;
251ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordonimport android.os.HandlerThread;
261ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordonimport android.os.Message;
271ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordonimport android.provider.Settings;
281ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
2991d43cf9c985cc5a83795f256ef5c46ebb8fbdc1Tyler Gunnimport com.android.internal.util.Preconditions;
301ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
311ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon/**
321ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon * Plays the default ringtone. Uses {@link Ringtone} in a separate thread so that this class can be
331ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon * used from the main thread.
341ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon */
351ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordonclass AsyncRingtonePlayer {
361ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    // Message codes used with the ringtone thread.
37b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon    private static final int EVENT_PLAY = 1;
38b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon    private static final int EVENT_STOP = 2;
39b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon    private static final int EVENT_REPEAT = 3;
40b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon
41b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon    // The interval in which to restart the ringer.
42b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon    private static final int RESTART_RINGER_MILLIS = 3000;
431ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
441ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    /** Handler running on the ringtone thread. */
451ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    private Handler mHandler;
461ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
471ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    /** The current ringtone. Only used by the ringtone thread. */
481ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    private Ringtone mRingtone;
491ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
5091d43cf9c985cc5a83795f256ef5c46ebb8fbdc1Tyler Gunn    /**
5191d43cf9c985cc5a83795f256ef5c46ebb8fbdc1Tyler Gunn     * The context.
5291d43cf9c985cc5a83795f256ef5c46ebb8fbdc1Tyler Gunn     */
5391d43cf9c985cc5a83795f256ef5c46ebb8fbdc1Tyler Gunn    private final Context mContext;
5491d43cf9c985cc5a83795f256ef5c46ebb8fbdc1Tyler Gunn
5591d43cf9c985cc5a83795f256ef5c46ebb8fbdc1Tyler Gunn    AsyncRingtonePlayer(Context context) {
5691d43cf9c985cc5a83795f256ef5c46ebb8fbdc1Tyler Gunn        mContext = context;
5791d43cf9c985cc5a83795f256ef5c46ebb8fbdc1Tyler Gunn    }
5891d43cf9c985cc5a83795f256ef5c46ebb8fbdc1Tyler Gunn
591ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    /** Plays the ringtone. */
605ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordon    void play(Uri ringtone) {
611ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        Log.d(this, "Posting play.");
62f43ff474988d4b35c77e0834506d0e15111a4cf1Wenyi Wang        if (ringtone == Uri.EMPTY) {
63f43ff474988d4b35c77e0834506d0e15111a4cf1Wenyi Wang            Log.i(this, "Uri.EMPTY (silence -- not playing anything)");
64f43ff474988d4b35c77e0834506d0e15111a4cf1Wenyi Wang            return;
65f43ff474988d4b35c77e0834506d0e15111a4cf1Wenyi Wang        }
665ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordon        postMessage(EVENT_PLAY, true /* shouldCreateHandler */, ringtone);
671ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    }
681ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
691ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    /** Stops playing the ringtone. */
701ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    void stop() {
711ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        Log.d(this, "Posting stop.");
725ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordon        postMessage(EVENT_STOP, false /* shouldCreateHandler */, null);
731ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    }
741ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
751ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    /**
761ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon     * Posts a message to the ringtone-thread handler. Creates the handler if specified by the
771ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon     * parameter shouldCreateHandler.
781ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon     *
791ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon     * @param messageCode The message to post.
801ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon     * @param shouldCreateHandler True when a handler should be created to handle this message.
811ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon     */
825ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordon    private void postMessage(int messageCode, boolean shouldCreateHandler, Uri ringtone) {
831ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        synchronized(this) {
841ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            if (mHandler == null && shouldCreateHandler) {
851ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon                mHandler = getNewHandler();
861ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            }
871ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
881ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            if (mHandler == null) {
891ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon                Log.d(this, "Message %d skipped because there is no handler.", messageCode);
901ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            } else {
915ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordon                mHandler.obtainMessage(messageCode, ringtone).sendToTarget();
921ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            }
931ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        }
941ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    }
951ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
961ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    /**
971ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon     * Creates a new ringtone Handler running in its own thread.
981ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon     */
991ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    private Handler getNewHandler() {
1001ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        Preconditions.checkState(mHandler == null);
1011ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
1021ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        HandlerThread thread = new HandlerThread("ringtone-player");
1031ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        thread.start();
1041ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
1051ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        return new Handler(thread.getLooper()) {
1061ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            @Override
1071ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            public void handleMessage(Message msg) {
1081ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon                switch(msg.what) {
1091ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon                    case EVENT_PLAY:
1105ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordon                        handlePlay((Uri) msg.obj);
1111ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon                        break;
112b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon                    case EVENT_REPEAT:
113b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon                        handleRepeat();
114b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon                        break;
1151ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon                    case EVENT_STOP:
1161ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon                        handleStop();
1171ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon                        break;
1181ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon                }
1191ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            }
1201ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        };
1211ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    }
1221ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
1231ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    /**
1241ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon     * Starts the actual playback of the ringtone. Executes on ringtone-thread.
1251ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon     */
1265ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordon    private void handlePlay(Uri ringtoneUri) {
127b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon        // don't bother with any of this if there is an EVENT_STOP waiting.
128b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon        if (mHandler.hasMessages(EVENT_STOP)) {
129b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon            return;
130b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon        }
131b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon
1321ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        ThreadUtil.checkNotOnMainThread();
1331ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        Log.i(this, "Play ringtone.");
1341ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
1351ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        if (mRingtone == null) {
1365ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordon            mRingtone = getRingtone(ringtoneUri);
1371ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
1381ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            // Cancel everything if there is no ringtone.
1391ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            if (mRingtone == null) {
1401ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon                handleStop();
1411ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon                return;
1421ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            }
1431ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        }
1441ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
145b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon        handleRepeat();
146b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon    }
147b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon
148b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon    private void handleRepeat() {
149b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon        if (mRingtone == null) {
150b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon            return;
151b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon        }
152b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon
1531ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        if (mRingtone.isPlaying()) {
1541ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            Log.d(this, "Ringtone already playing.");
1551ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        } else {
1561ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            mRingtone.play();
157b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon            Log.i(this, "Repeat ringtone.");
158b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon        }
159b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon
160b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon        // Repost event to restart ringer in {@link RESTART_RINGER_MILLIS}.
161b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon        synchronized(this) {
162b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon            if (!mHandler.hasMessages(EVENT_REPEAT)) {
163b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon                mHandler.sendEmptyMessageDelayed(EVENT_REPEAT, RESTART_RINGER_MILLIS);
164b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon            }
1651ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        }
1661ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    }
1671ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
1681ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    /**
1691ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon     * Stops the playback of the ringtone. Executes on the ringtone-thread.
1701ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon     */
1711ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    private void handleStop() {
1721ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        ThreadUtil.checkNotOnMainThread();
1731ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        Log.i(this, "Stop ringtone.");
1741ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
1751ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        if (mRingtone != null) {
1761ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            Log.d(this, "Ringtone.stop() invoked.");
1771ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            mRingtone.stop();
1781ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            mRingtone = null;
1791ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        }
1801ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
1811ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        synchronized(this) {
182b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon            // At the time that STOP is handled, there should be no need for repeat messages in the
183b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon            // queue.
184b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon            mHandler.removeMessages(EVENT_REPEAT);
185b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon
1861ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            if (mHandler.hasMessages(EVENT_PLAY)) {
187b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon                Log.v(this, "Keeping alive ringtone thread for subsequent play request.");
1881ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            } else {
1891ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon                mHandler.removeMessages(EVENT_STOP);
1901ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon                mHandler.getLooper().quitSafely();
1911ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon                mHandler = null;
1921ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon                Log.v(this, "Handler cleared.");
1931ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            }
1941ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        }
1951ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    }
1961ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
1975ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordon    private Ringtone getRingtone(Uri ringtoneUri) {
1985ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordon        if (ringtoneUri == null) {
1995ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordon            ringtoneUri = Settings.System.DEFAULT_RINGTONE_URI;
2005ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordon        }
2015ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordon
20291d43cf9c985cc5a83795f256ef5c46ebb8fbdc1Tyler Gunn        Ringtone ringtone = RingtoneManager.getRingtone(mContext, ringtoneUri);
203dcf7b1f6cdbf597f4e1da766eeb995a9cf9bc949Yorke Lee        if (ringtone != null) {
204dcf7b1f6cdbf597f4e1da766eeb995a9cf9bc949Yorke Lee            ringtone.setStreamType(AudioManager.STREAM_RING);
205dcf7b1f6cdbf597f4e1da766eeb995a9cf9bc949Yorke Lee        }
206b64c150bde86ae22787aec4eaa16b9e3b3420f9dSantos Cordon        return ringtone;
2071ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    }
2081ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon}
209