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 89 // newest state. 90 mInitialCallState = mTelephonyManager.getCallState(); 91 92 return START_STICKY; 93 } 94 95 // Volume suggested by media team for in-call alarms. 96 private static final float IN_CALL_VOLUME = 0.125f; 97 98 private void play() { 99 100 if (mPlaying) { 101 return; 102 } 103 104 LogUtils.v("TimerRingService.play()"); 105 106 // TODO: Reuse mMediaPlayer instead of creating a new one and/or use 107 // RingtoneManager. 108 mMediaPlayer = new MediaPlayer(); 109 mMediaPlayer.setOnErrorListener(new OnErrorListener() { 110 @Override 111 public boolean onError(MediaPlayer mp, int what, int extra) { 112 LogUtils.e("Error occurred while playing audio."); 113 mp.stop(); 114 mp.release(); 115 mMediaPlayer = null; 116 return true; 117 } 118 }); 119 120 try { 121 // Check if we are in a call. If we are, use the in-call alarm 122 // resource at a low volume to not disrupt the call. 123 if (mTelephonyManager.getCallState() 124 != TelephonyManager.CALL_STATE_IDLE) { 125 LogUtils.v("Using the in-call alarm"); 126 mMediaPlayer.setVolume(IN_CALL_VOLUME, IN_CALL_VOLUME); 127 setDataSourceFromResource(getResources(), mMediaPlayer, 128 R.raw.in_call_alarm); 129 } else { 130 AssetFileDescriptor afd = getAssets().openFd("sounds/Timer_Expire.ogg"); 131 mMediaPlayer.setDataSource( 132 afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength()); 133 } 134 startAlarm(mMediaPlayer); 135 } catch (Exception ex) { 136 LogUtils.v("Using the fallback ringtone"); 137 // The alert may be on the sd card which could be busy right 138 // now. Use the fallback ringtone. 139 try { 140 // Must reset the media player to clear the error state. 141 mMediaPlayer.reset(); 142 setDataSourceFromResource(getResources(), mMediaPlayer, 143 R.raw.fallbackring); 144 startAlarm(mMediaPlayer); 145 } catch (Exception ex2) { 146 // At this point we just don't play anything. 147 LogUtils.e("Failed to play fallback ringtone", ex2); 148 } 149 } 150 151 mPlaying = true; 152 } 153 154 // Do the common stuff when starting the alarm. 155 private void startAlarm(MediaPlayer player) 156 throws java.io.IOException, IllegalArgumentException, 157 IllegalStateException { 158 final AudioManager audioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE); 159 // do not play alarms if stream volume is 0 160 // (typically because ringer mode is silent). 161 if (audioManager.getStreamVolume(AudioManager.STREAM_ALARM) != 0) { 162 player.setAudioStreamType(AudioManager.STREAM_ALARM); 163 player.setLooping(true); 164 player.prepare(); 165 audioManager.requestAudioFocus( 166 this, AudioManager.STREAM_ALARM, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT); 167 player.start(); 168 } 169 } 170 171 private void setDataSourceFromResource(Resources resources, 172 MediaPlayer player, int res) throws java.io.IOException { 173 AssetFileDescriptor afd = resources.openRawResourceFd(res); 174 if (afd != null) { 175 player.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), 176 afd.getLength()); 177 afd.close(); 178 } 179 } 180 181 /** 182 * Stops timer audio 183 */ 184 public void stop() { 185 LogUtils.v("TimerRingService.stop()"); 186 if (mPlaying) { 187 mPlaying = false; 188 189 // Stop audio playing 190 if (mMediaPlayer != null) { 191 mMediaPlayer.stop(); 192 final AudioManager audioManager = 193 (AudioManager)getSystemService(Context.AUDIO_SERVICE); 194 audioManager.abandonAudioFocus(this); 195 mMediaPlayer.release(); 196 mMediaPlayer = null; 197 } 198 } 199 } 200 201 202 @Override 203 public void onAudioFocusChange(int focusChange) { 204 // Do nothing 205 } 206} 207