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