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