AI 144185: Integrate cdma into the main code base.
[android/platform/packages/apps/Phone.git] / src / com / android / phone / Ringer.java
1 /*
2  * Copyright (C) 2006 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.phone;
18
19 import android.content.Context;
20 import android.media.AudioManager;
21 import android.media.Ringtone;
22 import android.media.RingtoneManager;
23 import android.net.Uri;
24 import android.os.Handler;
25 import android.os.IHardwareService;
26 import android.os.Looper;
27 import android.os.Message;
28 import android.os.RemoteException;
29 import android.os.ServiceManager;
30 import android.os.SystemClock;
31 import android.os.SystemProperties;
32 import android.os.Vibrator;
33 import android.util.Log;
34
35 import com.android.internal.telephony.Phone;
36
37 /**
38  * Ringer manager for the Phone app.
39  */
40 public class Ringer {
41     private static final String LOG_TAG = "Ringer";
42     private static final boolean DBG =
43             (PhoneApp.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1);
44
45     private static final int PLAY_RING_ONCE = 1;
46     private static final int STOP_RING = 3;
47
48     private static final int VIBRATE_LENGTH = 1000; // ms
49     private static final int PAUSE_LENGTH = 1000; // ms
50
51     // Uri for the ringtone.
52     Uri mCustomRingtoneUri;
53
54     Ringtone mRingtone;
55     Vibrator mVibrator = new Vibrator();
56     IHardwareService mHardwareService;
57     volatile boolean mContinueVibrating;
58     VibratorThread mVibratorThread;
59     Context mContext;
60     private Worker mRingThread;
61     private Handler mRingHandler;
62     private boolean mRingPending;
63     private long mFirstRingEventTime = -1;
64     private long mFirstRingStartTime = -1;
65
66     Ringer(Phone phone) {
67         mContext = phone.getContext();
68         mHardwareService = IHardwareService.Stub.asInterface(ServiceManager.getService("hardware"));
69     }
70
71     /**
72      * After a radio technology change, e.g. from CDMA to GSM or vice versa,
73      * the Context of the Ringer has to be updated. This is done by that function.
74      * 
75      * @parameter Phone, the new active phone for the appropriate radio
76      * technology
77      */
78     void updateRingerContextAfterRadioTechnologyChange(Phone phone) {
79         if(DBG) Log.d(LOG_TAG, "updateRingerContextAfterRadioTechnologyChange...");
80         mContext = phone.getContext();
81     }
82
83     /**
84      * @return true if we're playing a ringtone and/or vibrating
85      *     to indicate that there's an incoming call.
86      *     ("Ringing" here is used in the general sense.  If you literally
87      *     need to know if we're playing a ringtone or vibrating, use
88      *     isRingtonePlaying() or isVibrating() instead.)
89      *
90      * @see isVibrating
91      * @see isRingtonePlaying
92      */
93     boolean isRinging() {
94         synchronized (this) {
95             return (isRingtonePlaying() || isVibrating());
96         }
97     }
98
99     /**
100      * @return true if the ringtone is playing
101      * @see isVibrating
102      * @see isRinging
103      */
104     private boolean isRingtonePlaying() {
105         synchronized (this) {
106             return (mRingtone != null && mRingtone.isPlaying()) ||
107                     (mRingHandler != null && mRingHandler.hasMessages(PLAY_RING_ONCE));
108         }
109     }
110
111     /**
112      * @return true if we're vibrating in response to an incoming call
113      * @see isVibrating
114      * @see isRinging
115      */
116     private boolean isVibrating() {
117         synchronized (this) {
118             return (mVibratorThread != null);
119         }
120     }
121
122     /**
123      * Starts the ringtone and/or vibrator
124      */
125     void ring() {
126         if (DBG) log("ring()...");
127
128         synchronized (this) {
129             try {
130                 mHardwareService.setAttentionLight(true);
131             } catch (RemoteException ex) {
132                 // the other end of this binder call is in the system process.
133             }
134
135             if (shouldVibrate() && mVibratorThread == null) {
136                 mContinueVibrating = true;
137                 mVibratorThread = new VibratorThread();
138                 if (DBG) log("- starting vibrator...");
139                 mVibratorThread.start();
140             }
141             AudioManager audioManager =
142                     (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
143
144             if (audioManager.getStreamVolume(AudioManager.STREAM_RING) == 0) {
145                 if (DBG) log("skipping ring because volume is zero");
146                 return;
147             }
148
149             if (!isRingtonePlaying() && !mRingPending) {
150                 makeLooper();
151                 mRingHandler.removeCallbacksAndMessages(null);
152                 mRingPending = true;
153                 if (mFirstRingEventTime < 0) {
154                     mFirstRingEventTime = SystemClock.elapsedRealtime();
155                     mRingHandler.sendEmptyMessage(PLAY_RING_ONCE);
156                 } else {
157                     // For repeat rings, figure out by how much to delay
158                     // the ring so that it happens the correct amount of
159                     // time after the previous ring
160                     if (mFirstRingStartTime > 0) {
161                         // Delay subsequent rings by the delta between event
162                         // and play time of the first ring
163                         if (DBG) {
164                             log("delaying ring by " + (mFirstRingStartTime - mFirstRingEventTime));
165                         }
166                         mRingHandler.sendEmptyMessageDelayed(PLAY_RING_ONCE,
167                                 mFirstRingStartTime - mFirstRingEventTime);
168                     } else {
169                         // We've gotten two ring events so far, but the ring
170                         // still hasn't started. Reset the event time to the
171                         // time of this event to maintain correct spacing.
172                         mFirstRingEventTime = SystemClock.elapsedRealtime();
173                     }
174                 }
175             } else {
176                 if (DBG) log("skipping ring, already playing or pending: "
177                              + mRingtone + "/" + mRingHandler);
178             }
179         }
180     }
181
182     boolean shouldVibrate() {
183         AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
184         return audioManager.shouldVibrate(AudioManager.VIBRATE_TYPE_RINGER);
185     }
186
187     /**
188      * Stops the ringtone and/or vibrator if any of these are actually
189      * ringing/vibrating.
190      */
191     void stopRing() {
192         synchronized (this) {
193             if (DBG) log("stopRing()...");
194
195             try {
196                 mHardwareService.setAttentionLight(false);
197             } catch (RemoteException ex) {
198                 // the other end of this binder call is in the system process.
199             }
200
201             if (mRingHandler != null) {
202                 mRingHandler.removeCallbacksAndMessages(null);
203                 Message msg = mRingHandler.obtainMessage(STOP_RING);
204                 msg.obj = mRingtone;
205                 mRingHandler.sendMessage(msg);
206                 PhoneUtils.setAudioMode(mContext, AudioManager.MODE_NORMAL);
207                 mRingThread = null;
208                 mRingHandler = null;
209                 mRingtone = null;
210                 mFirstRingEventTime = -1;
211                 mFirstRingStartTime = -1;
212                 mRingPending = false;
213             } else {
214                 if (DBG) log("- stopRing: null mRingHandler!");
215             }
216
217             if (mVibratorThread != null) {
218                 if (DBG) log("- stopRing: cleaning up vibrator thread...");
219                 mContinueVibrating = false;
220                 mVibratorThread = null;
221             }
222             // Also immediately cancel any vibration in progress.
223             mVibrator.cancel();
224         }
225     }
226
227     private class VibratorThread extends Thread {
228         public void run() {
229             while (mContinueVibrating) {
230                 mVibrator.vibrate(VIBRATE_LENGTH);
231                 SystemClock.sleep(VIBRATE_LENGTH + PAUSE_LENGTH);
232             }
233         }
234     }
235     private class Worker implements Runnable {
236         private final Object mLock = new Object();
237         private Looper mLooper;
238         
239         Worker(String name) {
240             Thread t = new Thread(null, this, name);
241             t.start();
242             synchronized (mLock) {
243                 while (mLooper == null) {
244                     try {
245                         mLock.wait();
246                     } catch (InterruptedException ex) {
247                     }
248                 }
249             }
250         }
251         
252         public Looper getLooper() {
253             return mLooper;
254         }
255         
256         public void run() {
257             synchronized (mLock) {
258                 Looper.prepare();
259                 mLooper = Looper.myLooper();
260                 mLock.notifyAll();
261             }
262             Looper.loop();
263         }
264         
265         public void quit() {
266             mLooper.quit();
267         }
268     }
269
270     /**
271      * Sets the ringtone uri in preparation for ringtone creation
272      * in makeLooper().  This uri is defaulted to the phone-wide
273      * default ringtone.
274      */
275     void setCustomRingtoneUri (Uri uri) {
276         if (uri != null) {
277             mCustomRingtoneUri = uri;
278         }
279     }
280
281     private void makeLooper() {
282         if (mRingThread == null) {
283             mRingThread = new Worker("ringer");
284             mRingHandler = new Handler(mRingThread.getLooper()) {
285                 @Override
286                 public void handleMessage(Message msg) {
287                     Ringtone r = null;
288                     switch (msg.what) {
289                         case PLAY_RING_ONCE:
290                             if (DBG) log("mRingHandler: PLAY_RING_ONCE...");
291                             if (mRingtone == null && !hasMessages(STOP_RING)) {
292                                 // create the ringtone with the uri
293                                 if (DBG) log("creating ringtone: " + mCustomRingtoneUri);
294                                 r = RingtoneManager.getRingtone(mContext, mCustomRingtoneUri);
295                                 synchronized (Ringer.this) {
296                                     if (!hasMessages(STOP_RING)) {
297                                         mRingtone = r;
298                                     }
299                                 }
300                             }
301                             r = mRingtone;
302                             if (r != null && !hasMessages(STOP_RING)) {
303                                 PhoneUtils.setAudioMode(mContext, AudioManager.MODE_RINGTONE);
304                                 r.play();
305                                 synchronized (Ringer.this) {
306                                     mRingPending = false;
307                                     if (mFirstRingStartTime < 0) {
308                                         mFirstRingStartTime = SystemClock.elapsedRealtime();
309                                     }
310                                 }
311                             }
312                             break;
313                         case STOP_RING:
314                             if (DBG) log("mRingHandler: STOP_RING...");
315                             r = (Ringtone) msg.obj;
316                             if (r != null) {
317                                 r.stop();
318                             } else {
319                                 if (DBG) log("- STOP_RING with null ringtone!  msg = " + msg);
320                             }
321                             getLooper().quit();
322                             break;
323                     }
324                 }
325             };
326         }
327     }
328
329     private static void log(String msg) {
330         Log.d(LOG_TAG, msg);
331     }
332 }