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             (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      */
     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() */
     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      */
    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      */
    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      */
    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      */
    141     private boolean isVibrating() {
    142         synchronized (this) {
    143             return (mVibratorThread != null);
    144         }
    145     }
    146 
    147     /**
    148      * Starts the ringtone and/or vibrator
    149      */
    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 
    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      */
    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 {
    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 
    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 
    278         public Looper getLooper() {
    279             return mLooper;
    280         }
    281 
    282         public void run() {
    283             synchronized (mLock) {
    284                 Looper.prepare();
    285                 mLooper = Looper.myLooper();
    286                 mLock.notifyAll();
    287             }
    288             Looper.loop();
    289         }
    290 
    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      */
    301     void setCustomRingtoneUri (Uri uri) {
    302         if (uri != null) {
    303             mCustomRingtoneUri = uri;
    304         }
    305     }
    306 
    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 
    354     private static void log(String msg) {
    355         Log.d(LOG_TAG, msg);
    356     }
    357 }
    358