Home | History | Annotate | Download | only in phone
      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      */
     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() */
     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      */
     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      */
    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      */
    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      */
    135     private boolean isVibrating() {
    136         synchronized (this) {
    137             return (mVibratorThread != null);
    138         }
    139     }
    140 
    141     /**
    142      * Starts the ringtone and/or vibrator
    143      */
    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 
    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      */
    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 {
    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 
    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 
    272         public Looper getLooper() {
    273             return mLooper;
    274         }
    275 
    276         public void run() {
    277             synchronized (mLock) {
    278                 Looper.prepare();
    279                 mLooper = Looper.myLooper();
    280                 mLock.notifyAll();
    281             }
    282             Looper.loop();
    283         }
    284 
    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      */
    295     void setCustomRingtoneUri (Uri uri) {
    296         if (uri != null) {
    297             mCustomRingtoneUri = uri;
    298         }
    299     }
    300 
    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 
    348     private static void log(String msg) {
    349         Log.d(LOG_TAG, msg);
    350     }
    351 }
    352