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.");
625ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordon        postMessage(EVENT_PLAY, true /* shouldCreateHandler */, ringtone);
631ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    }
641ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
651ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    /** Stops playing the ringtone. */
661ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    void stop() {
671ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        Log.d(this, "Posting stop.");
685ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordon        postMessage(EVENT_STOP, false /* shouldCreateHandler */, null);
691ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    }
701ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
711ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    /**
721ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon     * Posts a message to the ringtone-thread handler. Creates the handler if specified by the
731ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon     * parameter shouldCreateHandler.
741ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon     *
751ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon     * @param messageCode The message to post.
761ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon     * @param shouldCreateHandler True when a handler should be created to handle this message.
771ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon     */
785ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordon    private void postMessage(int messageCode, boolean shouldCreateHandler, Uri ringtone) {
791ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        synchronized(this) {
801ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            if (mHandler == null && shouldCreateHandler) {
811ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon                mHandler = getNewHandler();
821ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            }
831ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
841ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            if (mHandler == null) {
851ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon                Log.d(this, "Message %d skipped because there is no handler.", messageCode);
861ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            } else {
875ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordon                mHandler.obtainMessage(messageCode, ringtone).sendToTarget();
881ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            }
891ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        }
901ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    }
911ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
921ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    /**
931ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon     * Creates a new ringtone Handler running in its own thread.
941ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon     */
951ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    private Handler getNewHandler() {
961ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        Preconditions.checkState(mHandler == null);
971ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
981ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        HandlerThread thread = new HandlerThread("ringtone-player");
991ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        thread.start();
1001ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
1011ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        return new Handler(thread.getLooper()) {
1021ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            @Override
1031ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            public void handleMessage(Message msg) {
1041ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon                switch(msg.what) {
1051ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon                    case EVENT_PLAY:
1065ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordon                        handlePlay((Uri) msg.obj);
1071ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon                        break;
108b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon                    case EVENT_REPEAT:
109b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon                        handleRepeat();
110b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon                        break;
1111ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon                    case EVENT_STOP:
1121ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon                        handleStop();
1131ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon                        break;
1141ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon                }
1151ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            }
1161ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        };
1171ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    }
1181ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
1191ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    /**
1201ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon     * Starts the actual playback of the ringtone. Executes on ringtone-thread.
1211ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon     */
1225ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordon    private void handlePlay(Uri ringtoneUri) {
123b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon        // don't bother with any of this if there is an EVENT_STOP waiting.
124b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon        if (mHandler.hasMessages(EVENT_STOP)) {
125b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon            return;
126b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon        }
127b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon
1281ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        ThreadUtil.checkNotOnMainThread();
1291ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        Log.i(this, "Play ringtone.");
1301ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
1311ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        if (mRingtone == null) {
1325ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordon            mRingtone = getRingtone(ringtoneUri);
1331ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
1341ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            // Cancel everything if there is no ringtone.
1351ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            if (mRingtone == null) {
1361ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon                handleStop();
1371ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon                return;
1381ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            }
1391ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        }
1401ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
141b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon        handleRepeat();
142b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon    }
143b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon
144b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon    private void handleRepeat() {
145b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon        if (mRingtone == null) {
146b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon            return;
147b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon        }
148b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon
1491ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        if (mRingtone.isPlaying()) {
1501ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            Log.d(this, "Ringtone already playing.");
1511ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        } else {
1521ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            mRingtone.play();
153b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon            Log.i(this, "Repeat ringtone.");
154b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon        }
155b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon
156b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon        // Repost event to restart ringer in {@link RESTART_RINGER_MILLIS}.
157b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon        synchronized(this) {
158b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon            if (!mHandler.hasMessages(EVENT_REPEAT)) {
159b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon                mHandler.sendEmptyMessageDelayed(EVENT_REPEAT, RESTART_RINGER_MILLIS);
160b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon            }
1611ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        }
1621ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    }
1631ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
1641ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    /**
1651ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon     * Stops the playback of the ringtone. Executes on the ringtone-thread.
1661ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon     */
1671ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    private void handleStop() {
1681ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        ThreadUtil.checkNotOnMainThread();
1691ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        Log.i(this, "Stop ringtone.");
1701ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
1711ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        if (mRingtone != null) {
1721ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            Log.d(this, "Ringtone.stop() invoked.");
1731ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            mRingtone.stop();
1741ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            mRingtone = null;
1751ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        }
1761ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
1771ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        synchronized(this) {
178b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon            // At the time that STOP is handled, there should be no need for repeat messages in the
179b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon            // queue.
180b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon            mHandler.removeMessages(EVENT_REPEAT);
181b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon
1821ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            if (mHandler.hasMessages(EVENT_PLAY)) {
183b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon                Log.v(this, "Keeping alive ringtone thread for subsequent play request.");
1841ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            } else {
1851ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon                mHandler.removeMessages(EVENT_STOP);
1861ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon                mHandler.getLooper().quitSafely();
1871ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon                mHandler = null;
1881ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon                Log.v(this, "Handler cleared.");
1891ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon            }
1901ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon        }
1911ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    }
1921ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon
1935ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordon    private Ringtone getRingtone(Uri ringtoneUri) {
1945ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordon        if (ringtoneUri == null) {
1955ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordon            ringtoneUri = Settings.System.DEFAULT_RINGTONE_URI;
1965ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordon        }
1975ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordon
19891d43cf9c985cc5a83795f256ef5c46ebb8fbdc1Tyler Gunn        Ringtone ringtone = RingtoneManager.getRingtone(mContext, ringtoneUri);
199b64c150bde86ae22787aec4eaa16b9e3b3420f9dSantos Cordon        ringtone.setStreamType(AudioManager.STREAM_RING);
200b64c150bde86ae22787aec4eaa16b9e3b3420f9dSantos Cordon        return ringtone;
2011ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon    }
2021ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon}
203