• 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.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      */
init(Context context)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() */
Ringer(Context context)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      */
updateRingerContextAfterRadioTechnologyChange(Phone phone)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      */
isRinging()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      */
isRingtonePlaying()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      */
isVibrating()138     private boolean isVibrating() {
139         synchronized (this) {
140             return (mVibratorThread != null);
141         }
142     }
143 
144     /**
145      * Starts the ringtone and/or vibrator
146      */
ring()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 
shouldVibrate()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      */
stopRing()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 {
run()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 
Worker(String name)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 
getLooper()275         public Looper getLooper() {
276             return mLooper;
277         }
278 
run()279         public void run() {
280             synchronized (mLock) {
281                 Looper.prepare();
282                 mLooper = Looper.myLooper();
283                 mLock.notifyAll();
284             }
285             Looper.loop();
286         }
287 
quit()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      */
setCustomRingtoneUri(Uri uri)298     void setCustomRingtoneUri (Uri uri) {
299         if (uri != null) {
300             mCustomRingtoneUri = uri;
301         }
302     }
303 
makeLooper()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 
log(String msg)351     private static void log(String msg) {
352         Log.d(LOG_TAG, msg);
353     }
354 }
355