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