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; 208c89b20bc84c06294bff803b3a6043992b613e76Hall Liuimport android.net.Uri; 211ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordonimport android.os.Handler; 221ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordonimport android.os.HandlerThread; 231ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordonimport android.os.Message; 24a3eccfee788c3ac3c831a443b085b141b39bb63dBrad Ebingerimport android.telecom.Log; 251ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon 26d931a017a0abea32ad4485a91402b5f62b9ddb0eBrad Ebingerimport com.android.internal.annotations.VisibleForTesting; 278c89b20bc84c06294bff803b3a6043992b613e76Hall Liuimport com.android.internal.os.SomeArgs; 2891d43cf9c985cc5a83795f256ef5c46ebb8fbdc1Tyler Gunnimport com.android.internal.util.Preconditions; 291ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon 301ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon/** 311ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon * Plays the default ringtone. Uses {@link Ringtone} in a separate thread so that this class can be 321ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon * used from the main thread. 331ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon */ 34d931a017a0abea32ad4485a91402b5f62b9ddb0eBrad Ebinger@VisibleForTesting 35d931a017a0abea32ad4485a91402b5f62b9ddb0eBrad Ebingerpublic class 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 501ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon /** Plays the ringtone. */ 51c9286f49d12769d775d8ad7a66f9117c49c71162Brad Ebinger public void play(RingtoneFactory factory, Call incomingCall) { 521ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon Log.d(this, "Posting play."); 538c89b20bc84c06294bff803b3a6043992b613e76Hall Liu SomeArgs args = SomeArgs.obtain(); 548c89b20bc84c06294bff803b3a6043992b613e76Hall Liu args.arg1 = factory; 55c9286f49d12769d775d8ad7a66f9117c49c71162Brad Ebinger args.arg2 = incomingCall; 568c89b20bc84c06294bff803b3a6043992b613e76Hall Liu postMessage(EVENT_PLAY, true /* shouldCreateHandler */, args); 571ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon } 581ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon 591ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon /** Stops playing the ringtone. */ 60d931a017a0abea32ad4485a91402b5f62b9ddb0eBrad Ebinger public void stop() { 611ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon Log.d(this, "Posting stop."); 625ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordon postMessage(EVENT_STOP, false /* shouldCreateHandler */, null); 631ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon } 641ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon 651ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon /** 661ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon * Posts a message to the ringtone-thread handler. Creates the handler if specified by the 671ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon * parameter shouldCreateHandler. 681ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon * 691ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon * @param messageCode The message to post. 701ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon * @param shouldCreateHandler True when a handler should be created to handle this message. 711ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon */ 728c89b20bc84c06294bff803b3a6043992b613e76Hall Liu private void postMessage(int messageCode, boolean shouldCreateHandler, SomeArgs args) { 731ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon synchronized(this) { 741ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon if (mHandler == null && shouldCreateHandler) { 751ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon mHandler = getNewHandler(); 761ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon } 771ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon 781ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon if (mHandler == null) { 791ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon Log.d(this, "Message %d skipped because there is no handler.", messageCode); 801ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon } else { 818c89b20bc84c06294bff803b3a6043992b613e76Hall Liu mHandler.obtainMessage(messageCode, args).sendToTarget(); 821ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon } 831ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon } 841ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon } 851ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon 861ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon /** 871ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon * Creates a new ringtone Handler running in its own thread. 881ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon */ 891ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon private Handler getNewHandler() { 901ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon Preconditions.checkState(mHandler == null); 911ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon 921ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon HandlerThread thread = new HandlerThread("ringtone-player"); 931ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon thread.start(); 941ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon 951ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon return new Handler(thread.getLooper()) { 961ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon @Override 971ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon public void handleMessage(Message msg) { 981ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon switch(msg.what) { 991ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon case EVENT_PLAY: 1008c89b20bc84c06294bff803b3a6043992b613e76Hall Liu handlePlay((SomeArgs) msg.obj); 1011ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon break; 102b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon case EVENT_REPEAT: 103b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon handleRepeat(); 104b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon break; 1051ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon case EVENT_STOP: 1061ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon handleStop(); 1071ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon break; 1081ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon } 1091ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon } 1101ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon }; 1111ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon } 1121ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon 1131ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon /** 1141ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon * Starts the actual playback of the ringtone. Executes on ringtone-thread. 1151ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon */ 1168c89b20bc84c06294bff803b3a6043992b613e76Hall Liu private void handlePlay(SomeArgs args) { 1178c89b20bc84c06294bff803b3a6043992b613e76Hall Liu RingtoneFactory factory = (RingtoneFactory) args.arg1; 118c9286f49d12769d775d8ad7a66f9117c49c71162Brad Ebinger Call incomingCall = (Call) args.arg2; 1198c89b20bc84c06294bff803b3a6043992b613e76Hall Liu args.recycle(); 120b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon // don't bother with any of this if there is an EVENT_STOP waiting. 121b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon if (mHandler.hasMessages(EVENT_STOP)) { 122b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon return; 123b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon } 124b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon 125ad54e7f053f00d6c77fe18c11cc5e7c5a7b8fb60Brad Ebinger // If the Ringtone Uri is EMPTY, then the "None" Ringtone has been selected. Do not play 126ad54e7f053f00d6c77fe18c11cc5e7c5a7b8fb60Brad Ebinger // anything. 127ad54e7f053f00d6c77fe18c11cc5e7c5a7b8fb60Brad Ebinger if(Uri.EMPTY.equals(incomingCall.getRingtone())) { 128ad54e7f053f00d6c77fe18c11cc5e7c5a7b8fb60Brad Ebinger mRingtone = null; 129ad54e7f053f00d6c77fe18c11cc5e7c5a7b8fb60Brad Ebinger return; 130ad54e7f053f00d6c77fe18c11cc5e7c5a7b8fb60Brad Ebinger } 131ad54e7f053f00d6c77fe18c11cc5e7c5a7b8fb60Brad Ebinger 1321ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon ThreadUtil.checkNotOnMainThread(); 1331ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon Log.i(this, "Play ringtone."); 1341ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon 1351ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon if (mRingtone == null) { 136c9286f49d12769d775d8ad7a66f9117c49c71162Brad Ebinger mRingtone = factory.getRingtone(incomingCall); 1378c89b20bc84c06294bff803b3a6043992b613e76Hall Liu if (mRingtone == null) { 138c9286f49d12769d775d8ad7a66f9117c49c71162Brad Ebinger Uri ringtoneUri = incomingCall.getRingtone(); 139c9286f49d12769d775d8ad7a66f9117c49c71162Brad Ebinger String ringtoneUriString = (ringtoneUri == null) ? "null" : 140c9286f49d12769d775d8ad7a66f9117c49c71162Brad Ebinger ringtoneUri.toSafeString(); 141a3eccfee788c3ac3c831a443b085b141b39bb63dBrad Ebinger Log.addEvent(null, LogUtils.Events.ERROR_LOG, "Failed to get ringtone from " + 142a3eccfee788c3ac3c831a443b085b141b39bb63dBrad Ebinger "factory. Skipping ringing. Uri was: " + ringtoneUriString); 1438c89b20bc84c06294bff803b3a6043992b613e76Hall Liu return; 1448c89b20bc84c06294bff803b3a6043992b613e76Hall Liu } 1451ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon } 1461ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon 147b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon handleRepeat(); 148b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon } 149b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon 150b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon private void handleRepeat() { 151b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon if (mRingtone == null) { 152b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon return; 153b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon } 154b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon 1551ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon if (mRingtone.isPlaying()) { 1561ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon Log.d(this, "Ringtone already playing."); 1571ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon } else { 1581ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon mRingtone.play(); 159b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon Log.i(this, "Repeat ringtone."); 160b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon } 161b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon 162b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon // Repost event to restart ringer in {@link RESTART_RINGER_MILLIS}. 163b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon synchronized(this) { 164b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon if (!mHandler.hasMessages(EVENT_REPEAT)) { 165b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon mHandler.sendEmptyMessageDelayed(EVENT_REPEAT, RESTART_RINGER_MILLIS); 166b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon } 1671ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon } 1681ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon } 1691ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon 1701ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon /** 1711ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon * Stops the playback of the ringtone. Executes on the ringtone-thread. 1721ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon */ 1731ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon private void handleStop() { 1741ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon ThreadUtil.checkNotOnMainThread(); 1751ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon Log.i(this, "Stop ringtone."); 1761ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon 1771ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon if (mRingtone != null) { 1781ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon Log.d(this, "Ringtone.stop() invoked."); 1791ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon mRingtone.stop(); 1801ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon mRingtone = null; 1811ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon } 1821ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon 1831ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon synchronized(this) { 184b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon // At the time that STOP is handled, there should be no need for repeat messages in the 185b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon // queue. 186b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon mHandler.removeMessages(EVENT_REPEAT); 187b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon 1881ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon if (mHandler.hasMessages(EVENT_PLAY)) { 189b21c5daa6ce683d7f6a1605a50a7ae05c520309fSantos Cordon Log.v(this, "Keeping alive ringtone thread for subsequent play request."); 1901ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon } else { 1911ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon mHandler.removeMessages(EVENT_STOP); 1921ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon mHandler.getLooper().quitSafely(); 1931ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon mHandler = null; 1941ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon Log.v(this, "Handler cleared."); 1951ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon } 1961ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon } 1971ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon } 1981ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon} 199