Ringer.java revision d614b529c8bb38332872992f787a820a461763a0
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
17package com.android.phone;
18
19import android.content.Context;
20import android.media.AudioManager;
21import android.media.Ringtone;
22import android.media.RingtoneManager;
23import android.net.Uri;
24import android.os.Handler;
25import android.os.IHardwareService;
26import android.os.Looper;
27import android.os.Message;
28import android.os.RemoteException;
29import android.os.ServiceManager;
30import android.os.SystemClock;
31import android.os.SystemProperties;
32import android.os.Vibrator;
33import android.util.Log;
34
35import com.android.internal.telephony.Phone;
36/**
37 * Ringer manager for the Phone app.
38 */
39public 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    IHardwareService mHardwareService;
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(Phone phone) {
65        mContext = phone.getContext();
66        mHardwareService = IHardwareService.Stub.asInterface(ServiceManager.getService("hardware"));
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                    mHardwareService.setAttentionLight(true, 0x000000ff);
130		} else {
131                    mHardwareService.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                mHardwareService.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(mContext, AudioManager.MODE_NORMAL);
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(mContext, AudioManager.MODE_RINGTONE);
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}
326