1/* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.deskclock; 18 19import android.app.Service; 20import android.content.Context; 21import android.content.Intent; 22import android.content.res.AssetFileDescriptor; 23import android.content.res.Resources; 24import android.media.AudioManager; 25import android.media.MediaPlayer; 26import android.media.MediaPlayer.OnErrorListener; 27import android.os.IBinder; 28import android.telephony.PhoneStateListener; 29import android.telephony.TelephonyManager; 30 31/** 32 * Play the timer's ringtone. Will continue playing the same alarm until service is stopped. 33 */ 34public class TimerRingService extends Service implements AudioManager.OnAudioFocusChangeListener { 35 36 private boolean mPlaying = false; 37 private MediaPlayer mMediaPlayer; 38 private TelephonyManager mTelephonyManager; 39 private int mInitialCallState; 40 41 42 private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() { 43 @Override 44 public void onCallStateChanged(int state, String ignored) { 45 // The user might already be in a call when the alarm fires. When 46 // we register onCallStateChanged, we get the initial in-call state 47 // which kills the alarm. Check against the initial call state so 48 // we don't kill the alarm during a call. 49 if (state != TelephonyManager.CALL_STATE_IDLE 50 && state != mInitialCallState) { 51 stopSelf(); 52 } 53 } 54 }; 55 56 @Override 57 public void onCreate() { 58 // Listen for incoming calls to kill the alarm. 59 mTelephonyManager = 60 (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); 61 mTelephonyManager.listen( 62 mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE); 63 AlarmAlertWakeLock.acquireScreenCpuWakeLock(this); 64 } 65 66 @Override 67 public void onDestroy() { 68 stop(); 69 // Stop listening for incoming calls. 70 mTelephonyManager.listen(mPhoneStateListener, 0); 71 AlarmAlertWakeLock.releaseCpuLock(); 72 } 73 74 @Override 75 public IBinder onBind(Intent intent) { 76 return null; 77 } 78 79 @Override 80 public int onStartCommand(Intent intent, int flags, int startId) { 81 // No intent, tell the system not to restart us. 82 if (intent == null) { 83 stopSelf(); 84 return START_NOT_STICKY; 85 } 86 87 play(); 88 // Record the initial call state here so that the new alarm has the newest state. 89 mInitialCallState = mTelephonyManager.getCallState(); 90 91 return START_STICKY; 92 } 93 94 // Volume suggested by media team for in-call alarms. 95 private static final float IN_CALL_VOLUME = 0.125f; 96 97 private void play() { 98 99 if (mPlaying) { 100 return; 101 } 102 103 LogUtils.v("TimerRingService.play()"); 104 105 // TODO: Reuse mMediaPlayer instead of creating a new one and/or use 106 // RingtoneManager. 107 mMediaPlayer = new MediaPlayer(); 108 mMediaPlayer.setOnErrorListener(new OnErrorListener() { 109 @Override 110 public boolean onError(MediaPlayer mp, int what, int extra) { 111 LogUtils.e("Error occurred while playing audio."); 112 mp.stop(); 113 mp.release(); 114 mMediaPlayer = null; 115 return true; 116 } 117 }); 118 119 try { 120 // Check if we are in a call. If we are, use the in-call alarm 121 // resource at a low volume to not disrupt the call. 122 if (mTelephonyManager.getCallState() 123 != TelephonyManager.CALL_STATE_IDLE) { 124 LogUtils.v("Using the in-call alarm"); 125 mMediaPlayer.setVolume(IN_CALL_VOLUME, IN_CALL_VOLUME); 126 setDataSourceFromResource(getResources(), mMediaPlayer, 127 R.raw.in_call_alarm); 128 } else { 129 AssetFileDescriptor afd = getAssets().openFd("sounds/Timer_Expire.ogg"); 130 mMediaPlayer.setDataSource( 131 afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength()); 132 } 133 startAlarm(mMediaPlayer); 134 } catch (Exception ex) { 135 LogUtils.v("Using the fallback ringtone"); 136 // The alert may be on the sd card which could be busy right 137 // now. Use the fallback ringtone. 138 try { 139 // Must reset the media player to clear the error state. 140 mMediaPlayer.reset(); 141 setDataSourceFromResource(getResources(), mMediaPlayer, 142 R.raw.fallbackring); 143 startAlarm(mMediaPlayer); 144 } catch (Exception ex2) { 145 // At this point we just don't play anything. 146 LogUtils.e("Failed to play fallback ringtone", ex2); 147 } 148 } 149 150 mPlaying = true; 151 } 152 153 // Do the common stuff when starting the alarm. 154 private void startAlarm(MediaPlayer player) 155 throws java.io.IOException, IllegalArgumentException, 156 IllegalStateException { 157 final AudioManager audioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE); 158 // do not play alarms if stream volume is 0 159 // (typically because ringer mode is silent). 160 if (audioManager.getStreamVolume(AudioManager.STREAM_ALARM) != 0) { 161 player.setAudioStreamType(AudioManager.STREAM_ALARM); 162 player.setLooping(true); 163 player.prepare(); 164 audioManager.requestAudioFocus( 165 this, AudioManager.STREAM_ALARM, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT); 166 player.start(); 167 } 168 } 169 170 private void setDataSourceFromResource(Resources resources, 171 MediaPlayer player, int res) throws java.io.IOException { 172 AssetFileDescriptor afd = resources.openRawResourceFd(res); 173 if (afd != null) { 174 player.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), 175 afd.getLength()); 176 afd.close(); 177 } 178 } 179 180 /** 181 * Stops timer audio 182 */ 183 public void stop() { 184 LogUtils.v("TimerRingService.stop()"); 185 if (mPlaying) { 186 mPlaying = false; 187 188 // Stop audio playing 189 if (mMediaPlayer != null) { 190 mMediaPlayer.stop(); 191 final AudioManager audioManager = 192 (AudioManager)getSystemService(Context.AUDIO_SERVICE); 193 audioManager.abandonAudioFocus(this); 194 mMediaPlayer.release(); 195 mMediaPlayer = null; 196 } 197 } 198 } 199 200 201 @Override 202 public void onAudioFocusChange(int focusChange) { 203 // Do nothing 204 } 205} 206