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.Vibrator; 33 import android.util.Log; 34 35 import com.android.internal.telephony.Phone; 36 /** 37 * Ringer manager for the Phone app. 38 */ 39 public class Ringer { 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); 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 IPowerManager mPowerManager; 56 volatile boolean mContinueVibrating; 57 VibratorThread mVibratorThread; 58 Context mContext; 59 private Worker mRingThread; 60 private Handler mRingHandler; 61 private long mFirstRingEventTime = -1; 62 private long mFirstRingStartTime = -1; 63 64 Ringer(Phone phone) { 65 mContext = phone.getContext(); 66 mPowerManager = IPowerManager.Stub.asInterface(ServiceManager.getService(Context.POWER_SERVICE)); 67 } 68 69 /** 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. 72 * 73 * @parameter Phone, the new active phone for the appropriate radio 74 * technology 75 */ 76 void updateRingerContextAfterRadioTechnologyChange(Phone phone) { 77 if(DBG) Log.d(LOG_TAG, "updateRingerContextAfterRadioTechnologyChange..."); 78 mContext = phone.getContext(); 79 } 80 81 /** 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.) 87 * 88 * @see isVibrating 89 * @see isRingtonePlaying 90 */ 91 boolean isRinging() { 92 synchronized (this) { 93 return (isRingtonePlaying() || isVibrating()); 94 } 95 } 96 97 /** 98 * @return true if the ringtone is playing 99 * @see isVibrating 100 * @see isRinging 101 */ 102 private boolean isRingtonePlaying() { 103 synchronized (this) { 104 return (mRingtone != null && mRingtone.isPlaying()) || 105 (mRingHandler != null && mRingHandler.hasMessages(PLAY_RING_ONCE)); 106 } 107 } 108 109 /** 110 * @return true if we're vibrating in response to an incoming call 111 * @see isVibrating 112 * @see isRinging 113 */ 114 private boolean isVibrating() { 115 synchronized (this) { 116 return (mVibratorThread != null); 117 } 118 } 119 120 /** 121 * Starts the ringtone and/or vibrator 122 */ 123 void ring() { 124 if (DBG) log("ring()..."); 125 126 synchronized (this) { 127 try { 128 if (PhoneApp.getInstance().showBluetoothIndication()) { 129 mPowerManager.setAttentionLight(true, 0x000000ff); 130 } else { 131 mPowerManager.setAttentionLight(true, 0x00ffffff); 132 } 133 } catch (RemoteException ex) { 134 // the other end of this binder call is in the system process. 135 } 136 137 if (shouldVibrate() && mVibratorThread == null) { 138 mContinueVibrating = true; 139 mVibratorThread = new VibratorThread(); 140 if (DBG) log("- starting vibrator..."); 141 mVibratorThread.start(); 142 } 143 AudioManager audioManager = 144 (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); 145 146 if (audioManager.getStreamVolume(AudioManager.STREAM_RING) == 0) { 147 if (DBG) log("skipping ring because volume is zero"); 148 return; 149 } 150 151 makeLooper(); 152 if (mFirstRingEventTime < 0) { 153 mFirstRingEventTime = SystemClock.elapsedRealtime(); 154 mRingHandler.sendEmptyMessage(PLAY_RING_ONCE); 155 } else { 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 162 if (DBG) { 163 log("delaying ring by " + (mFirstRingStartTime - mFirstRingEventTime)); 164 } 165 mRingHandler.sendEmptyMessageDelayed(PLAY_RING_ONCE, 166 mFirstRingStartTime - mFirstRingEventTime); 167 } else { 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(); 172 } 173 } 174 } 175 } 176 177 boolean shouldVibrate() { 178 AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); 179 return audioManager.shouldVibrate(AudioManager.VIBRATE_TYPE_RINGER); 180 } 181 182 /** 183 * Stops the ringtone and/or vibrator if any of these are actually 184 * ringing/vibrating. 185 */ 186 void stopRing() { 187 synchronized (this) { 188 if (DBG) log("stopRing()..."); 189 190 try { 191 mPowerManager.setAttentionLight(false, 0x00000000); 192 } catch (RemoteException ex) { 193 // the other end of this binder call is in the system process. 194 } 195 196 if (mRingHandler != null) { 197 mRingHandler.removeCallbacksAndMessages(null); 198 Message msg = mRingHandler.obtainMessage(STOP_RING); 199 msg.obj = mRingtone; 200 mRingHandler.sendMessage(msg); 201 PhoneUtils.setAudioMode(mContext, AudioManager.MODE_NORMAL); 202 mRingThread = null; 203 mRingHandler = null; 204 mRingtone = null; 205 mFirstRingEventTime = -1; 206 mFirstRingStartTime = -1; 207 } else { 208 if (DBG) log("- stopRing: null mRingHandler!"); 209 } 210 211 if (mVibratorThread != null) { 212 if (DBG) log("- stopRing: cleaning up vibrator thread..."); 213 mContinueVibrating = false; 214 mVibratorThread = null; 215 } 216 // Also immediately cancel any vibration in progress. 217 mVibrator.cancel(); 218 } 219 } 220 221 private class VibratorThread extends Thread { 222 public void run() { 223 while (mContinueVibrating) { 224 mVibrator.vibrate(VIBRATE_LENGTH); 225 SystemClock.sleep(VIBRATE_LENGTH + PAUSE_LENGTH); 226 } 227 } 228 } 229 private class Worker implements Runnable { 230 private final Object mLock = new Object(); 231 private Looper mLooper; 232 233 Worker(String name) { 234 Thread t = new Thread(null, this, name); 235 t.start(); 236 synchronized (mLock) { 237 while (mLooper == null) { 238 try { 239 mLock.wait(); 240 } catch (InterruptedException ex) { 241 } 242 } 243 } 244 } 245 246 public Looper getLooper() { 247 return mLooper; 248 } 249 250 public void run() { 251 synchronized (mLock) { 252 Looper.prepare(); 253 mLooper = Looper.myLooper(); 254 mLock.notifyAll(); 255 } 256 Looper.loop(); 257 } 258 259 public void quit() { 260 mLooper.quit(); 261 } 262 } 263 264 /** 265 * Sets the ringtone uri in preparation for ringtone creation 266 * in makeLooper(). This uri is defaulted to the phone-wide 267 * default ringtone. 268 */ 269 void setCustomRingtoneUri (Uri uri) { 270 if (uri != null) { 271 mCustomRingtoneUri = uri; 272 } 273 } 274 275 private void makeLooper() { 276 if (mRingThread == null) { 277 mRingThread = new Worker("ringer"); 278 mRingHandler = new Handler(mRingThread.getLooper()) { 279 @Override 280 public void handleMessage(Message msg) { 281 Ringtone r = null; 282 switch (msg.what) { 283 case PLAY_RING_ONCE: 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)) { 291 mRingtone = r; 292 } 293 } 294 } 295 r = mRingtone; 296 if (r != null && !hasMessages(STOP_RING) && !r.isPlaying()) { 297 PhoneUtils.setAudioMode(mContext, AudioManager.MODE_RINGTONE); 298 r.play(); 299 synchronized (Ringer.this) { 300 if (mFirstRingStartTime < 0) { 301 mFirstRingStartTime = SystemClock.elapsedRealtime(); 302 } 303 } 304 } 305 break; 306 case STOP_RING: 307 if (DBG) log("mRingHandler: STOP_RING..."); 308 r = (Ringtone) msg.obj; 309 if (r != null) { 310 r.stop(); 311 } else { 312 if (DBG) log("- STOP_RING with null ringtone! msg = " + msg); 313 } 314 getLooper().quit(); 315 break; 316 } 317 } 318 }; 319 } 320 } 321 322 private static void log(String msg) { 323 Log.d(LOG_TAG, msg); 324 } 325 } 326