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