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