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.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      */
     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() */
     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      */
    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      */
    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      */
    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      */
    149     private boolean isVibrating() {
    150         synchronized (this) {
    151             return (mVibratorThread != null);
    152         }
    153     }
    154 
    155     /**
    156      * Starts the ringtone and/or vibrator
    157      */
    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 
    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      */
    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 {
    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 
    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 
    286         public Looper getLooper() {
    287             return mLooper;
    288         }
    289 
    290         public void run() {
    291             synchronized (mLock) {
    292                 Looper.prepare();
    293                 mLooper = Looper.myLooper();
    294                 mLock.notifyAll();
    295             }
    296             Looper.loop();
    297         }
    298 
    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      */
    309     void setCustomRingtoneUri (Uri uri) {
    310         if (uri != null) {
    311             mCustomRingtoneUri = uri;
    312         }
    313     }
    314 
    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 
    362     private static void log(String msg) {
    363         Log.d(LOG_TAG, msg);
    364     }
    365 }
    366