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