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