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