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.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             (PhoneApp.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      */
     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() */
     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();
     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      */
    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      */
    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      */
    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      */
    138     private boolean isVibrating() {
    139         synchronized (this) {
    140             return (mVibratorThread != null);
    141         }
    142     }
    143 
    144     /**
    145      * Starts the ringtone and/or vibrator
    146      */
    147     void ring() {
    148         if (DBG) log("ring()...");
    149 
    150         synchronized (this) {
    151             try {
    152                 if (PhoneApp.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 
    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      */
    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 {
    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 
    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 
    275         public Looper getLooper() {
    276             return mLooper;
    277         }
    278 
    279         public void run() {
    280             synchronized (mLock) {
    281                 Looper.prepare();
    282                 mLooper = Looper.myLooper();
    283                 mLock.notifyAll();
    284             }
    285             Looper.loop();
    286         }
    287 
    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      */
    298     void setCustomRingtoneUri (Uri uri) {
    299         if (uri != null) {
    300             mCustomRingtoneUri = uri;
    301         }
    302     }
    303 
    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 
    351     private static void log(String msg) {
    352         Log.d(LOG_TAG, msg);
    353     }
    354 }
    355