15cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson/* 25cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson * Copyright (C) 2012 The Android Open Source Project 35cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson * 45cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson * Licensed under the Apache License, Version 2.0 (the "License"); 55cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson * you may not use this file except in compliance with the License. 65cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson * You may obtain a copy of the License at 75cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson * 85cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson * http://www.apache.org/licenses/LICENSE-2.0 95cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson * 105cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson * Unless required by applicable law or agreed to in writing, software 115cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson * distributed under the License is distributed on an "AS IS" BASIS, 125cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 135cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson * See the License for the specific language governing permissions and 145cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson * limitations under the License. 155cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson */ 165cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson 175cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelsonpackage com.android.deskclock; 185cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson 195cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelsonimport android.app.Service; 205cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelsonimport android.content.Context; 215cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelsonimport android.content.Intent; 225cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelsonimport android.content.res.AssetFileDescriptor; 235cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelsonimport android.content.res.Resources; 245cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelsonimport android.media.AudioManager; 255cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelsonimport android.media.MediaPlayer; 265cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelsonimport android.media.MediaPlayer.OnErrorListener; 275cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelsonimport android.os.IBinder; 285cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelsonimport android.telephony.PhoneStateListener; 295cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelsonimport android.telephony.TelephonyManager; 305cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson 315cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson/** 325cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson * Play the timer's ringtone. Will continue playing the same alarm until service is stopped. 335cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson */ 34205f02bc332c272cd43a2466578ac741bc700d10Alon Albertpublic class TimerRingService extends Service implements AudioManager.OnAudioFocusChangeListener { 355cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson 365cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson private boolean mPlaying = false; 375cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson private MediaPlayer mMediaPlayer; 385cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson private TelephonyManager mTelephonyManager; 395cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson private int mInitialCallState; 405cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson 415cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson 425cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() { 435cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson @Override 445cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson public void onCallStateChanged(int state, String ignored) { 455cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson // The user might already be in a call when the alarm fires. When 465cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson // we register onCallStateChanged, we get the initial in-call state 475cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson // which kills the alarm. Check against the initial call state so 485cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson // we don't kill the alarm during a call. 495cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson if (state != TelephonyManager.CALL_STATE_IDLE 505cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson && state != mInitialCallState) { 515cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson stopSelf(); 525cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson } 535cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson } 545cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson }; 555cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson 565cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson @Override 575cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson public void onCreate() { 585cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson // Listen for incoming calls to kill the alarm. 595cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson mTelephonyManager = 605cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); 615cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson mTelephonyManager.listen( 625cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE); 635aee14904f3b7272b5a1741fe820a8ed73b96664Robyn Coultas AlarmAlertWakeLock.acquireScreenCpuWakeLock(this); 645cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson } 655cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson 665cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson @Override 675cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson public void onDestroy() { 685cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson stop(); 695cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson // Stop listening for incoming calls. 705cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson mTelephonyManager.listen(mPhoneStateListener, 0); 715cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson AlarmAlertWakeLock.releaseCpuLock(); 725cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson } 735cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson 745cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson @Override 755cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson public IBinder onBind(Intent intent) { 765cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson return null; 775cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson } 785cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson 795cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson @Override 805cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson public int onStartCommand(Intent intent, int flags, int startId) { 815cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson // No intent, tell the system not to restart us. 825cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson if (intent == null) { 835cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson stopSelf(); 845cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson return START_NOT_STICKY; 855cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson } 865cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson 875cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson play(); 885cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson // Record the initial call state here so that the new alarm has the 895cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson // newest state. 905cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson mInitialCallState = mTelephonyManager.getCallState(); 915cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson 925cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson return START_STICKY; 935cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson } 945cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson 955cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson // Volume suggested by media team for in-call alarms. 965cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson private static final float IN_CALL_VOLUME = 0.125f; 975cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson 985cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson private void play() { 995cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson 1005cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson if (mPlaying) { 1015cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson return; 1025cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson } 1035cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson 104cdbb71b08c13c84af850f4036febc0b90dcfcc7dJustin Klaassen LogUtils.v("TimerRingService.play()"); 1055cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson 1065cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson // TODO: Reuse mMediaPlayer instead of creating a new one and/or use 1075cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson // RingtoneManager. 1085cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson mMediaPlayer = new MediaPlayer(); 1095cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson mMediaPlayer.setOnErrorListener(new OnErrorListener() { 1105cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson @Override 1115cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson public boolean onError(MediaPlayer mp, int what, int extra) { 112cdbb71b08c13c84af850f4036febc0b90dcfcc7dJustin Klaassen LogUtils.e("Error occurred while playing audio."); 1135cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson mp.stop(); 1145cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson mp.release(); 1155cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson mMediaPlayer = null; 1165cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson return true; 1175cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson } 1185cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson }); 1195cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson 1205cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson try { 1215cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson // Check if we are in a call. If we are, use the in-call alarm 1225cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson // resource at a low volume to not disrupt the call. 1235cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson if (mTelephonyManager.getCallState() 1245cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson != TelephonyManager.CALL_STATE_IDLE) { 125cdbb71b08c13c84af850f4036febc0b90dcfcc7dJustin Klaassen LogUtils.v("Using the in-call alarm"); 1265cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson mMediaPlayer.setVolume(IN_CALL_VOLUME, IN_CALL_VOLUME); 1275cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson setDataSourceFromResource(getResources(), mMediaPlayer, 1285cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson R.raw.in_call_alarm); 1295cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson } else { 130f98a82ceff862cde3b0a0440e6c3889db377d897Isaac Katzenelson AssetFileDescriptor afd = getAssets().openFd("sounds/Timer_Expire.ogg"); 131f98a82ceff862cde3b0a0440e6c3889db377d897Isaac Katzenelson mMediaPlayer.setDataSource( 132f98a82ceff862cde3b0a0440e6c3889db377d897Isaac Katzenelson afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength()); 1335cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson } 1345cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson startAlarm(mMediaPlayer); 1355cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson } catch (Exception ex) { 136cdbb71b08c13c84af850f4036febc0b90dcfcc7dJustin Klaassen LogUtils.v("Using the fallback ringtone"); 1375cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson // The alert may be on the sd card which could be busy right 1385cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson // now. Use the fallback ringtone. 1395cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson try { 1405cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson // Must reset the media player to clear the error state. 1415cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson mMediaPlayer.reset(); 1425cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson setDataSourceFromResource(getResources(), mMediaPlayer, 1435cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson R.raw.fallbackring); 1445cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson startAlarm(mMediaPlayer); 1455cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson } catch (Exception ex2) { 1465cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson // At this point we just don't play anything. 147cdbb71b08c13c84af850f4036febc0b90dcfcc7dJustin Klaassen LogUtils.e("Failed to play fallback ringtone", ex2); 1485cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson } 1495cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson } 1505cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson 1515cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson mPlaying = true; 1525cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson } 1535cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson 1545cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson // Do the common stuff when starting the alarm. 1555cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson private void startAlarm(MediaPlayer player) 1565cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson throws java.io.IOException, IllegalArgumentException, 1575cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson IllegalStateException { 1585cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson final AudioManager audioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE); 1595cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson // do not play alarms if stream volume is 0 1605cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson // (typically because ringer mode is silent). 1615cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson if (audioManager.getStreamVolume(AudioManager.STREAM_ALARM) != 0) { 1625cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson player.setAudioStreamType(AudioManager.STREAM_ALARM); 1635cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson player.setLooping(true); 1645cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson player.prepare(); 165205f02bc332c272cd43a2466578ac741bc700d10Alon Albert audioManager.requestAudioFocus( 166205f02bc332c272cd43a2466578ac741bc700d10Alon Albert this, AudioManager.STREAM_ALARM, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT); 1675cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson player.start(); 1685cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson } 1695cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson } 1705cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson 1715cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson private void setDataSourceFromResource(Resources resources, 1725cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson MediaPlayer player, int res) throws java.io.IOException { 1735cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson AssetFileDescriptor afd = resources.openRawResourceFd(res); 1745cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson if (afd != null) { 1755cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson player.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), 1765cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson afd.getLength()); 1775cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson afd.close(); 1785cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson } 1795cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson } 1805cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson 1815cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson /** 18258c329f52d7952597ae7a4e88fc2ec9e15ddd93aRobyn Coultas * Stops timer audio 1835cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson */ 1845cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson public void stop() { 185cdbb71b08c13c84af850f4036febc0b90dcfcc7dJustin Klaassen LogUtils.v("TimerRingService.stop()"); 1865cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson if (mPlaying) { 1875cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson mPlaying = false; 1885cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson 1895cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson // Stop audio playing 1905cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson if (mMediaPlayer != null) { 1915cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson mMediaPlayer.stop(); 192205f02bc332c272cd43a2466578ac741bc700d10Alon Albert final AudioManager audioManager = 193205f02bc332c272cd43a2466578ac741bc700d10Alon Albert (AudioManager)getSystemService(Context.AUDIO_SERVICE); 194205f02bc332c272cd43a2466578ac741bc700d10Alon Albert audioManager.abandonAudioFocus(this); 1955cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson mMediaPlayer.release(); 1965cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson mMediaPlayer = null; 1975cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson } 1985cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson } 1995cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson } 2005cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson 2015cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson 202205f02bc332c272cd43a2466578ac741bc700d10Alon Albert @Override 203205f02bc332c272cd43a2466578ac741bc700d10Alon Albert public void onAudioFocusChange(int focusChange) { 204205f02bc332c272cd43a2466578ac741bc700d10Alon Albert // Do nothing 205205f02bc332c272cd43a2466578ac741bc700d10Alon Albert } 2065cacdd0b56950cf6a5f28e780ed07625a81bde53Isaac Katzenelson} 207