OSDN Git Service

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