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