2 * Copyright (C) 2006 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.phone;
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;
35 import com.android.internal.telephony.Phone;
37 * Ringer manager for the Phone app.
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);
44 private static final int PLAY_RING_ONCE = 1;
45 private static final int STOP_RING = 3;
47 private static final int VIBRATE_LENGTH = 1000; // ms
48 private static final int PAUSE_LENGTH = 1000; // ms
50 // Uri for the ringtone.
51 Uri mCustomRingtoneUri;
54 Vibrator mVibrator = new Vibrator();
55 IPowerManager mPowerManager;
56 volatile boolean mContinueVibrating;
57 VibratorThread mVibratorThread;
59 private Worker mRingThread;
60 private Handler mRingHandler;
61 private long mFirstRingEventTime = -1;
62 private long mFirstRingStartTime = -1;
64 Ringer(Context context) {
66 mPowerManager = IPowerManager.Stub.asInterface(ServiceManager.getService(Context.POWER_SERVICE));
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.
73 * @parameter Phone, the new active phone for the appropriate radio
76 void updateRingerContextAfterRadioTechnologyChange(Phone phone) {
77 if(DBG) Log.d(LOG_TAG, "updateRingerContextAfterRadioTechnologyChange...");
78 mContext = phone.getContext();
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.)
89 * @see isRingtonePlaying
93 return (isRingtonePlaying() || isVibrating());
98 * @return true if the ringtone is playing
102 private boolean isRingtonePlaying() {
103 synchronized (this) {
104 return (mRingtone != null && mRingtone.isPlaying()) ||
105 (mRingHandler != null && mRingHandler.hasMessages(PLAY_RING_ONCE));
110 * @return true if we're vibrating in response to an incoming call
114 private boolean isVibrating() {
115 synchronized (this) {
116 return (mVibratorThread != null);
121 * Starts the ringtone and/or vibrator
124 if (DBG) log("ring()...");
126 synchronized (this) {
128 if (PhoneApp.getInstance().showBluetoothIndication()) {
129 mPowerManager.setAttentionLight(true, 0x000000ff);
131 mPowerManager.setAttentionLight(true, 0x00ffffff);
133 } catch (RemoteException ex) {
134 // the other end of this binder call is in the system process.
137 if (shouldVibrate() && mVibratorThread == null) {
138 mContinueVibrating = true;
139 mVibratorThread = new VibratorThread();
140 if (DBG) log("- starting vibrator...");
141 mVibratorThread.start();
143 AudioManager audioManager =
144 (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
146 if (audioManager.getStreamVolume(AudioManager.STREAM_RING) == 0) {
147 if (DBG) log("skipping ring because volume is zero");
152 if (mFirstRingEventTime < 0) {
153 mFirstRingEventTime = SystemClock.elapsedRealtime();
154 mRingHandler.sendEmptyMessage(PLAY_RING_ONCE);
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
163 log("delaying ring by " + (mFirstRingStartTime - mFirstRingEventTime));
165 mRingHandler.sendEmptyMessageDelayed(PLAY_RING_ONCE,
166 mFirstRingStartTime - mFirstRingEventTime);
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();
177 boolean shouldVibrate() {
178 AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
179 return audioManager.shouldVibrate(AudioManager.VIBRATE_TYPE_RINGER);
183 * Stops the ringtone and/or vibrator if any of these are actually
187 synchronized (this) {
188 if (DBG) log("stopRing()...");
191 mPowerManager.setAttentionLight(false, 0x00000000);
192 } catch (RemoteException ex) {
193 // the other end of this binder call is in the system process.
196 if (mRingHandler != null) {
197 mRingHandler.removeCallbacksAndMessages(null);
198 Message msg = mRingHandler.obtainMessage(STOP_RING);
200 mRingHandler.sendMessage(msg);
201 PhoneUtils.setAudioMode();
205 mFirstRingEventTime = -1;
206 mFirstRingStartTime = -1;
208 if (DBG) log("- stopRing: null mRingHandler!");
211 if (mVibratorThread != null) {
212 if (DBG) log("- stopRing: cleaning up vibrator thread...");
213 mContinueVibrating = false;
214 mVibratorThread = null;
216 // Also immediately cancel any vibration in progress.
221 private class VibratorThread extends Thread {
223 while (mContinueVibrating) {
224 mVibrator.vibrate(VIBRATE_LENGTH);
225 SystemClock.sleep(VIBRATE_LENGTH + PAUSE_LENGTH);
229 private class Worker implements Runnable {
230 private final Object mLock = new Object();
231 private Looper mLooper;
233 Worker(String name) {
234 Thread t = new Thread(null, this, name);
236 synchronized (mLock) {
237 while (mLooper == null) {
240 } catch (InterruptedException ex) {
246 public Looper getLooper() {
251 synchronized (mLock) {
253 mLooper = Looper.myLooper();
265 * Sets the ringtone uri in preparation for ringtone creation
266 * in makeLooper(). This uri is defaulted to the phone-wide
269 void setCustomRingtoneUri (Uri uri) {
271 mCustomRingtoneUri = uri;
275 private void makeLooper() {
276 if (mRingThread == null) {
277 mRingThread = new Worker("ringer");
278 mRingHandler = new Handler(mRingThread.getLooper()) {
280 public void handleMessage(Message msg) {
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)) {
296 if (r != null && !hasMessages(STOP_RING) && !r.isPlaying()) {
297 PhoneUtils.setAudioMode();
299 synchronized (Ringer.this) {
300 if (mFirstRingStartTime < 0) {
301 mFirstRingStartTime = SystemClock.elapsedRealtime();
307 if (DBG) log("mRingHandler: STOP_RING...");
308 r = (Ringtone) msg.obj;
312 if (DBG) log("- STOP_RING with null ringtone! msg = " + msg);
322 private static void log(String msg) {