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