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 com.android.internal.telephony.Call;
     20 import com.android.internal.telephony.CallManager;
     21 import com.android.internal.telephony.CallerInfo;
     22 import com.android.internal.telephony.CallerInfoAsyncQuery;
     23 import com.android.internal.telephony.Connection;
     24 import com.android.internal.telephony.Phone;
     25 import com.android.internal.telephony.PhoneConstants;
     26 import com.android.internal.telephony.PhoneBase;
     27 import com.android.internal.telephony.TelephonyCapabilities;
     28 import com.android.internal.telephony.cdma.CdmaCallWaitingNotification;
     29 import com.android.internal.telephony.cdma.CdmaInformationRecords.CdmaDisplayInfoRec;
     30 import com.android.internal.telephony.cdma.CdmaInformationRecords.CdmaSignalInfoRec;
     31 import com.android.internal.telephony.cdma.SignalToneUtil;
     32 
     33 import android.app.ActivityManagerNative;
     34 import android.bluetooth.BluetoothAdapter;
     35 import android.bluetooth.BluetoothHeadset;
     36 import android.bluetooth.BluetoothProfile;
     37 import android.content.Context;
     38 import android.media.AudioManager;
     39 import android.media.ToneGenerator;
     40 import android.net.Uri;
     41 import android.os.AsyncResult;
     42 import android.os.Handler;
     43 import android.os.Message;
     44 import android.os.SystemProperties;
     45 import android.os.SystemVibrator;
     46 import android.os.Vibrator;
     47 import android.provider.CallLog.Calls;
     48 import android.provider.Settings;
     49 import android.telephony.PhoneNumberUtils;
     50 import android.telephony.PhoneStateListener;
     51 import android.telephony.TelephonyManager;
     52 import android.util.EventLog;
     53 import android.util.Log;
     54 
     55 /**
     56  * Phone app module that listens for phone state changes and various other
     57  * events from the telephony layer, and triggers any resulting UI behavior
     58  * (like starting the Ringer and Incoming Call UI, playing in-call tones,
     59  * updating notifications, writing call log entries, etc.)
     60  */
     61 public class CallNotifier extends Handler
     62         implements CallerInfoAsyncQuery.OnQueryCompleteListener {
     63     private static final String LOG_TAG = "CallNotifier";
     64     private static final boolean DBG =
     65             (PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1);
     66     private static final boolean VDBG = (PhoneGlobals.DBG_LEVEL >= 2);
     67 
     68     // Maximum time we allow the CallerInfo query to run,
     69     // before giving up and falling back to the default ringtone.
     70     private static final int RINGTONE_QUERY_WAIT_TIME = 500;  // msec
     71 
     72     // Timers related to CDMA Call Waiting
     73     // 1) For displaying Caller Info
     74     // 2) For disabling "Add Call" menu option once User selects Ignore or CW Timeout occures
     75     private static final int CALLWAITING_CALLERINFO_DISPLAY_TIME = 20000; // msec
     76     private static final int CALLWAITING_ADDCALL_DISABLE_TIME = 30000; // msec
     77 
     78     // Time to display the  DisplayInfo Record sent by CDMA network
     79     private static final int DISPLAYINFO_NOTIFICATION_TIME = 2000; // msec
     80 
     81     /** The singleton instance. */
     82     private static CallNotifier sInstance;
     83 
     84     // Boolean to keep track of whether or not a CDMA Call Waiting call timed out.
     85     //
     86     // This is CDMA-specific, because with CDMA we *don't* get explicit
     87     // notification from the telephony layer that a call-waiting call has
     88     // stopped ringing.  Instead, when a call-waiting call first comes in we
     89     // start a 20-second timer (see CALLWAITING_CALLERINFO_DISPLAY_DONE), and
     90     // if the timer expires we clean up the call and treat it as a missed call.
     91     //
     92     // If this field is true, that means that the current Call Waiting call
     93     // "timed out" and should be logged in Call Log as a missed call.  If it's
     94     // false when we reach onCdmaCallWaitingReject(), we can assume the user
     95     // explicitly rejected this call-waiting call.
     96     //
     97     // This field is reset to false any time a call-waiting call first comes
     98     // in, and after cleaning up a missed call-waiting call.  It's only ever
     99     // set to true when the CALLWAITING_CALLERINFO_DISPLAY_DONE timer fires.
    100     //
    101     // TODO: do we really need a member variable for this?  Don't we always
    102     // know at the moment we call onCdmaCallWaitingReject() whether this is an
    103     // explicit rejection or not?
    104     // (Specifically: when we call onCdmaCallWaitingReject() from
    105     // PhoneUtils.hangupRingingCall() that means the user deliberately rejected
    106     // the call, and if we call onCdmaCallWaitingReject() because of a
    107     // CALLWAITING_CALLERINFO_DISPLAY_DONE event that means that it timed
    108     // out...)
    109     private boolean mCallWaitingTimeOut = false;
    110 
    111     // values used to track the query state
    112     private static final int CALLERINFO_QUERY_READY = 0;
    113     private static final int CALLERINFO_QUERYING = -1;
    114 
    115     // the state of the CallerInfo Query.
    116     private int mCallerInfoQueryState;
    117 
    118     // object used to synchronize access to mCallerInfoQueryState
    119     private Object mCallerInfoQueryStateGuard = new Object();
    120 
    121     // Event used to indicate a query timeout.
    122     private static final int RINGER_CUSTOM_RINGTONE_QUERY_TIMEOUT = 100;
    123 
    124     // Events generated internally:
    125     private static final int PHONE_MWI_CHANGED = 21;
    126     private static final int CALLWAITING_CALLERINFO_DISPLAY_DONE = 22;
    127     private static final int CALLWAITING_ADDCALL_DISABLE_TIMEOUT = 23;
    128     private static final int DISPLAYINFO_NOTIFICATION_DONE = 24;
    129     private static final int CDMA_CALL_WAITING_REJECT = 26;
    130     private static final int UPDATE_IN_CALL_NOTIFICATION = 27;
    131 
    132     // Emergency call related defines:
    133     private static final int EMERGENCY_TONE_OFF = 0;
    134     private static final int EMERGENCY_TONE_ALERT = 1;
    135     private static final int EMERGENCY_TONE_VIBRATE = 2;
    136 
    137     private PhoneGlobals mApplication;
    138     private CallManager mCM;
    139     private Ringer mRinger;
    140     private BluetoothHeadset mBluetoothHeadset;
    141     private CallLogger mCallLogger;
    142     private CallModeler mCallModeler;
    143     private boolean mSilentRingerRequested;
    144 
    145     // ToneGenerator instance for playing SignalInfo tones
    146     private ToneGenerator mSignalInfoToneGenerator;
    147 
    148     // The tone volume relative to other sounds in the stream SignalInfo
    149     private static final int TONE_RELATIVE_VOLUME_SIGNALINFO = 80;
    150 
    151     private Call.State mPreviousCdmaCallState;
    152     private boolean mVoicePrivacyState = false;
    153     private boolean mIsCdmaRedialCall = false;
    154 
    155     // Emergency call tone and vibrate:
    156     private int mIsEmergencyToneOn;
    157     private int mCurrentEmergencyToneState = EMERGENCY_TONE_OFF;
    158     private EmergencyTonePlayerVibrator mEmergencyTonePlayerVibrator;
    159 
    160     // Ringback tone player
    161     private InCallTonePlayer mInCallRingbackTonePlayer;
    162 
    163     // Call waiting tone player
    164     private InCallTonePlayer mCallWaitingTonePlayer;
    165 
    166     // Cached AudioManager
    167     private AudioManager mAudioManager;
    168 
    169     private final BluetoothManager mBluetoothManager;
    170 
    171     /**
    172      * Initialize the singleton CallNotifier instance.
    173      * This is only done once, at startup, from PhoneApp.onCreate().
    174      */
    175     /* package */ static CallNotifier init(PhoneGlobals app, Phone phone, Ringer ringer,
    176             CallLogger callLogger, CallStateMonitor callStateMonitor,
    177             BluetoothManager bluetoothManager, CallModeler callModeler) {
    178         synchronized (CallNotifier.class) {
    179             if (sInstance == null) {
    180                 sInstance = new CallNotifier(app, phone, ringer, callLogger, callStateMonitor,
    181                         bluetoothManager, callModeler);
    182             } else {
    183                 Log.wtf(LOG_TAG, "init() called multiple times!  sInstance = " + sInstance);
    184             }
    185             return sInstance;
    186         }
    187     }
    188 
    189     /** Private constructor; @see init() */
    190     private CallNotifier(PhoneGlobals app, Phone phone, Ringer ringer, CallLogger callLogger,
    191             CallStateMonitor callStateMonitor, BluetoothManager bluetoothManager,
    192             CallModeler callModeler) {
    193         mApplication = app;
    194         mCM = app.mCM;
    195         mCallLogger = callLogger;
    196         mBluetoothManager = bluetoothManager;
    197         mCallModeler = callModeler;
    198 
    199         mAudioManager = (AudioManager) mApplication.getSystemService(Context.AUDIO_SERVICE);
    200 
    201         callStateMonitor.addListener(this);
    202 
    203         createSignalInfoToneGenerator();
    204 
    205         mRinger = ringer;
    206         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
    207         if (adapter != null) {
    208             adapter.getProfileProxy(mApplication.getApplicationContext(),
    209                                     mBluetoothProfileServiceListener,
    210                                     BluetoothProfile.HEADSET);
    211         }
    212 
    213         TelephonyManager telephonyManager = (TelephonyManager)app.getSystemService(
    214                 Context.TELEPHONY_SERVICE);
    215         telephonyManager.listen(mPhoneStateListener,
    216                 PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR
    217                 | PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR);
    218     }
    219 
    220     private void createSignalInfoToneGenerator() {
    221         // Instantiate the ToneGenerator for SignalInfo and CallWaiting
    222         // TODO: We probably don't need the mSignalInfoToneGenerator instance
    223         // around forever. Need to change it so as to create a ToneGenerator instance only
    224         // when a tone is being played and releases it after its done playing.
    225         if (mSignalInfoToneGenerator == null) {
    226             try {
    227                 mSignalInfoToneGenerator = new ToneGenerator(AudioManager.STREAM_VOICE_CALL,
    228                         TONE_RELATIVE_VOLUME_SIGNALINFO);
    229                 Log.d(LOG_TAG, "CallNotifier: mSignalInfoToneGenerator created when toneplay");
    230             } catch (RuntimeException e) {
    231                 Log.w(LOG_TAG, "CallNotifier: Exception caught while creating " +
    232                         "mSignalInfoToneGenerator: " + e);
    233                 mSignalInfoToneGenerator = null;
    234             }
    235         } else {
    236             Log.d(LOG_TAG, "mSignalInfoToneGenerator created already, hence skipping");
    237         }
    238     }
    239 
    240     @Override
    241     public void handleMessage(Message msg) {
    242         switch (msg.what) {
    243             case CallStateMonitor.PHONE_NEW_RINGING_CONNECTION:
    244                 log("RINGING... (new)");
    245                 onNewRingingConnection((AsyncResult) msg.obj);
    246                 mSilentRingerRequested = false;
    247                 break;
    248 
    249             case CallStateMonitor.PHONE_INCOMING_RING:
    250                 // repeat the ring when requested by the RIL, and when the user has NOT
    251                 // specifically requested silence.
    252                 if (msg.obj != null && ((AsyncResult) msg.obj).result != null) {
    253                     PhoneBase pb =  (PhoneBase)((AsyncResult)msg.obj).result;
    254 
    255                     if ((pb.getState() == PhoneConstants.State.RINGING)
    256                             && (mSilentRingerRequested == false)) {
    257                         if (DBG) log("RINGING... (PHONE_INCOMING_RING event)");
    258                         mRinger.ring();
    259                     } else {
    260                         if (DBG) log("RING before NEW_RING, skipping");
    261                     }
    262                 }
    263                 break;
    264 
    265             case CallStateMonitor.PHONE_STATE_CHANGED:
    266                 onPhoneStateChanged((AsyncResult) msg.obj);
    267                 break;
    268 
    269             case CallStateMonitor.PHONE_DISCONNECT:
    270                 if (DBG) log("DISCONNECT");
    271                 onDisconnect((AsyncResult) msg.obj);
    272                 break;
    273 
    274             case CallStateMonitor.PHONE_UNKNOWN_CONNECTION_APPEARED:
    275                 onUnknownConnectionAppeared((AsyncResult) msg.obj);
    276                 break;
    277 
    278             case RINGER_CUSTOM_RINGTONE_QUERY_TIMEOUT:
    279                 onCustomRingtoneQueryTimeout((Connection) msg.obj);
    280                 break;
    281 
    282             case PHONE_MWI_CHANGED:
    283                 onMwiChanged(mApplication.phone.getMessageWaitingIndicator());
    284                 break;
    285 
    286             case CallStateMonitor.PHONE_CDMA_CALL_WAITING:
    287                 if (DBG) log("Received PHONE_CDMA_CALL_WAITING event");
    288                 onCdmaCallWaiting((AsyncResult) msg.obj);
    289                 break;
    290 
    291             case CDMA_CALL_WAITING_REJECT:
    292                 Log.i(LOG_TAG, "Received CDMA_CALL_WAITING_REJECT event");
    293                 onCdmaCallWaitingReject();
    294                 break;
    295 
    296             case CALLWAITING_CALLERINFO_DISPLAY_DONE:
    297                 Log.i(LOG_TAG, "Received CALLWAITING_CALLERINFO_DISPLAY_DONE event");
    298                 mCallWaitingTimeOut = true;
    299                 onCdmaCallWaitingReject();
    300                 break;
    301 
    302             case CALLWAITING_ADDCALL_DISABLE_TIMEOUT:
    303                 if (DBG) log("Received CALLWAITING_ADDCALL_DISABLE_TIMEOUT event ...");
    304                 // Set the mAddCallMenuStateAfterCW state to true
    305                 mApplication.cdmaPhoneCallState.setAddCallMenuStateAfterCallWaiting(true);
    306                 break;
    307 
    308             case CallStateMonitor.PHONE_STATE_DISPLAYINFO:
    309                 if (DBG) log("Received PHONE_STATE_DISPLAYINFO event");
    310                 onDisplayInfo((AsyncResult) msg.obj);
    311                 break;
    312 
    313             case CallStateMonitor.PHONE_STATE_SIGNALINFO:
    314                 if (DBG) log("Received PHONE_STATE_SIGNALINFO event");
    315                 onSignalInfo((AsyncResult) msg.obj);
    316                 break;
    317 
    318             case DISPLAYINFO_NOTIFICATION_DONE:
    319                 if (DBG) log("Received Display Info notification done event ...");
    320                 CdmaDisplayInfo.dismissDisplayInfoRecord();
    321                 break;
    322 
    323             case CallStateMonitor.EVENT_OTA_PROVISION_CHANGE:
    324                 if (DBG) log("EVENT_OTA_PROVISION_CHANGE...");
    325                 mApplication.handleOtaspEvent(msg);
    326                 break;
    327 
    328             case CallStateMonitor.PHONE_ENHANCED_VP_ON:
    329                 if (DBG) log("PHONE_ENHANCED_VP_ON...");
    330                 if (!mVoicePrivacyState) {
    331                     int toneToPlay = InCallTonePlayer.TONE_VOICE_PRIVACY;
    332                     new InCallTonePlayer(toneToPlay).start();
    333                     mVoicePrivacyState = true;
    334                 }
    335                 break;
    336 
    337             case CallStateMonitor.PHONE_ENHANCED_VP_OFF:
    338                 if (DBG) log("PHONE_ENHANCED_VP_OFF...");
    339                 if (mVoicePrivacyState) {
    340                     int toneToPlay = InCallTonePlayer.TONE_VOICE_PRIVACY;
    341                     new InCallTonePlayer(toneToPlay).start();
    342                     mVoicePrivacyState = false;
    343                 }
    344                 break;
    345 
    346             case CallStateMonitor.PHONE_RINGBACK_TONE:
    347                 onRingbackTone((AsyncResult) msg.obj);
    348                 break;
    349 
    350             case CallStateMonitor.PHONE_RESEND_MUTE:
    351                 onResendMute();
    352                 break;
    353 
    354             default:
    355                 // super.handleMessage(msg);
    356         }
    357     }
    358 
    359     PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
    360         @Override
    361         public void onMessageWaitingIndicatorChanged(boolean mwi) {
    362             onMwiChanged(mwi);
    363         }
    364 
    365         @Override
    366         public void onCallForwardingIndicatorChanged(boolean cfi) {
    367             onCfiChanged(cfi);
    368         }
    369     };
    370 
    371     /**
    372      * Handles a "new ringing connection" event from the telephony layer.
    373      */
    374     private void onNewRingingConnection(AsyncResult r) {
    375         Connection c = (Connection) r.result;
    376         log("onNewRingingConnection(): state = " + mCM.getState() + ", conn = { " + c + " }");
    377         Call ringing = c.getCall();
    378         Phone phone = ringing.getPhone();
    379 
    380         // Check for a few cases where we totally ignore incoming calls.
    381         if (ignoreAllIncomingCalls(phone)) {
    382             // Immediately reject the call, without even indicating to the user
    383             // that an incoming call occurred.  (This will generally send the
    384             // caller straight to voicemail, just as if we *had* shown the
    385             // incoming-call UI and the user had declined the call.)
    386             PhoneUtils.hangupRingingCall(ringing);
    387             return;
    388         }
    389 
    390         if (!c.isRinging()) {
    391             Log.i(LOG_TAG, "CallNotifier.onNewRingingConnection(): connection not ringing!");
    392             // This is a very strange case: an incoming call that stopped
    393             // ringing almost instantly after the onNewRingingConnection()
    394             // event.  There's nothing we can do here, so just bail out
    395             // without doing anything.  (But presumably we'll log it in
    396             // the call log when the disconnect event comes in...)
    397             return;
    398         }
    399 
    400         // Stop any signalInfo tone being played on receiving a Call
    401         stopSignalInfoTone();
    402 
    403         Call.State state = c.getState();
    404         // State will be either INCOMING or WAITING.
    405         if (VDBG) log("- connection is ringing!  state = " + state);
    406         // if (DBG) PhoneUtils.dumpCallState(mPhone);
    407 
    408         // No need to do any service state checks here (like for
    409         // "emergency mode"), since in those states the SIM won't let
    410         // us get incoming connections in the first place.
    411 
    412         // TODO: Consider sending out a serialized broadcast Intent here
    413         // (maybe "ACTION_NEW_INCOMING_CALL"), *before* starting the
    414         // ringer and going to the in-call UI.  The intent should contain
    415         // the caller-id info for the current connection, and say whether
    416         // it would be a "call waiting" call or a regular ringing call.
    417         // If anybody consumed the broadcast, we'd bail out without
    418         // ringing or bringing up the in-call UI.
    419         //
    420         // This would give 3rd party apps a chance to listen for (and
    421         // intercept) new ringing connections.  An app could reject the
    422         // incoming call by consuming the broadcast and doing nothing, or
    423         // it could "pick up" the call (without any action by the user!)
    424         // via some future TelephonyManager API.
    425         //
    426         // See bug 1312336 for more details.
    427         // We'd need to protect this with a new "intercept incoming calls"
    428         // system permission.
    429 
    430         // Obtain a partial wake lock to make sure the CPU doesn't go to
    431         // sleep before we finish bringing up the InCallScreen.
    432         // (This will be upgraded soon to a full wake lock; see
    433         // showIncomingCall().)
    434         if (VDBG) log("Holding wake lock on new incoming connection.");
    435         mApplication.requestWakeState(PhoneGlobals.WakeState.PARTIAL);
    436 
    437         // - don't ring for call waiting connections
    438         // - do this before showing the incoming call panel
    439         startIncomingCallQuery(c);
    440 
    441 
    442 
    443         // Note we *don't* post a status bar notification here, since
    444         // we're not necessarily ready to actually show the incoming call
    445         // to the user.  (For calls in the INCOMING state, at least, we
    446         // still need to run a caller-id query, and we may not even ring
    447         // at all if the "send directly to voicemail" flag is set.)
    448         //
    449         // Instead, we update the notification (and potentially launch the
    450         // InCallScreen) from the showIncomingCall() method, which runs
    451         // when the caller-id query completes or times out.
    452 
    453         if (VDBG) log("- onNewRingingConnection() done.");
    454     }
    455 
    456     /**
    457      * Determines whether or not we're allowed to present incoming calls to the
    458      * user, based on the capabilities and/or current state of the device.
    459      *
    460      * If this method returns true, that means we should immediately reject the
    461      * current incoming call, without even indicating to the user that an
    462      * incoming call occurred.
    463      *
    464      * (We only reject incoming calls in a few cases, like during an OTASP call
    465      * when we can't interrupt the user, or if the device hasn't completed the
    466      * SetupWizard yet.  We also don't allow incoming calls on non-voice-capable
    467      * devices.  But note that we *always* allow incoming calls while in ECM.)
    468      *
    469      * @return true if we're *not* allowed to present an incoming call to
    470      * the user.
    471      */
    472     private boolean ignoreAllIncomingCalls(Phone phone) {
    473         // Incoming calls are totally ignored on non-voice-capable devices.
    474         if (!PhoneGlobals.sVoiceCapable) {
    475             // ...but still log a warning, since we shouldn't have gotten this
    476             // event in the first place!  (Incoming calls *should* be blocked at
    477             // the telephony layer on non-voice-capable capable devices.)
    478             Log.w(LOG_TAG, "Got onNewRingingConnection() on non-voice-capable device! Ignoring...");
    479             return true;
    480         }
    481 
    482         // In ECM (emergency callback mode), we ALWAYS allow incoming calls
    483         // to get through to the user.  (Note that ECM is applicable only to
    484         // voice-capable CDMA devices).
    485         if (PhoneUtils.isPhoneInEcm(phone)) {
    486             if (DBG) log("Incoming call while in ECM: always allow...");
    487             return false;
    488         }
    489 
    490         // Incoming calls are totally ignored if the device isn't provisioned yet.
    491         boolean provisioned = Settings.Global.getInt(mApplication.getContentResolver(),
    492             Settings.Global.DEVICE_PROVISIONED, 0) != 0;
    493         if (!provisioned) {
    494             Log.i(LOG_TAG, "Ignoring incoming call: not provisioned");
    495             return true;
    496         }
    497 
    498         // Incoming calls are totally ignored if an OTASP call is active.
    499         if (TelephonyCapabilities.supportsOtasp(phone)) {
    500             boolean activateState = (mApplication.cdmaOtaScreenState.otaScreenState
    501                     == OtaUtils.CdmaOtaScreenState.OtaScreenState.OTA_STATUS_ACTIVATION);
    502             boolean dialogState = (mApplication.cdmaOtaScreenState.otaScreenState
    503                     == OtaUtils.CdmaOtaScreenState.OtaScreenState.OTA_STATUS_SUCCESS_FAILURE_DLG);
    504             boolean spcState = mApplication.cdmaOtaProvisionData.inOtaSpcState;
    505 
    506             if (spcState) {
    507                 Log.i(LOG_TAG, "Ignoring incoming call: OTA call is active");
    508                 return true;
    509             } else if (activateState || dialogState) {
    510                 // We *are* allowed to receive incoming calls at this point.
    511                 // But clear out any residual OTASP UI first.
    512                 // TODO: It's an MVC violation to twiddle the OTA UI state here;
    513                 // we should instead provide a higher-level API via OtaUtils.
    514                 if (dialogState) mApplication.dismissOtaDialogs();
    515                 mApplication.clearOtaState();
    516                 return false;
    517             }
    518         }
    519 
    520         // Normal case: allow this call to be presented to the user.
    521         return false;
    522     }
    523 
    524     /**
    525      * Helper method to manage the start of incoming call queries
    526      */
    527     private void startIncomingCallQuery(Connection c) {
    528         // TODO: cache the custom ringer object so that subsequent
    529         // calls will not need to do this query work.  We can keep
    530         // the MRU ringtones in memory.  We'll still need to hit
    531         // the database to get the callerinfo to act as a key,
    532         // but at least we can save the time required for the
    533         // Media player setup.  The only issue with this is that
    534         // we may need to keep an eye on the resources the Media
    535         // player uses to keep these ringtones around.
    536 
    537         // make sure we're in a state where we can be ready to
    538         // query a ringtone uri.
    539         boolean shouldStartQuery = false;
    540         synchronized (mCallerInfoQueryStateGuard) {
    541             if (mCallerInfoQueryState == CALLERINFO_QUERY_READY) {
    542                 mCallerInfoQueryState = CALLERINFO_QUERYING;
    543                 shouldStartQuery = true;
    544             }
    545         }
    546         if (shouldStartQuery) {
    547             // Reset the ringtone to the default first.
    548             mRinger.setCustomRingtoneUri(Settings.System.DEFAULT_RINGTONE_URI);
    549 
    550             // query the callerinfo to try to get the ringer.
    551             PhoneUtils.CallerInfoToken cit = PhoneUtils.startGetCallerInfo(
    552                     mApplication, c, this, c);
    553 
    554             // if this has already been queried then just ring, otherwise
    555             // we wait for the alloted time before ringing.
    556             if (cit.isFinal) {
    557                 if (VDBG) log("- CallerInfo already up to date, using available data");
    558                 onQueryComplete(0, c, cit.currentInfo);
    559             } else {
    560                 if (VDBG) log("- Starting query, posting timeout message.");
    561 
    562                 // Phone number (via getAddress()) is stored in the message to remember which
    563                 // number is actually used for the look up.
    564                 sendMessageDelayed(
    565                         Message.obtain(this, RINGER_CUSTOM_RINGTONE_QUERY_TIMEOUT, c),
    566                         RINGTONE_QUERY_WAIT_TIME);
    567             }
    568             // The call to showIncomingCall() will happen after the
    569             // queries are complete (or time out).
    570         } else {
    571             // This should never happen; its the case where an incoming call
    572             // arrives at the same time that the query is still being run,
    573             // and before the timeout window has closed.
    574             EventLog.writeEvent(EventLogTags.PHONE_UI_MULTIPLE_QUERY);
    575 
    576             ringAndNotifyOfIncomingCall(c);
    577         }
    578     }
    579 
    580     /**
    581      * Performs the final steps of the onNewRingingConnection sequence:
    582      * starts the ringer, and brings up the "incoming call" UI.
    583      *
    584      * Normally, this is called when the CallerInfo query completes (see
    585      * onQueryComplete()).  In this case, onQueryComplete() has already
    586      * configured the Ringer object to use the custom ringtone (if there
    587      * is one) for this caller.  So we just tell the Ringer to start, and
    588      * proceed to the InCallScreen.
    589      *
    590      * But this method can *also* be called if the
    591      * RINGTONE_QUERY_WAIT_TIME timeout expires, which means that the
    592      * CallerInfo query is taking too long.  In that case, we log a
    593      * warning but otherwise we behave the same as in the normal case.
    594      * (We still tell the Ringer to start, but it's going to use the
    595      * default ringtone.)
    596      */
    597     private void onCustomRingQueryComplete(Connection c) {
    598         boolean isQueryExecutionTimeExpired = false;
    599         synchronized (mCallerInfoQueryStateGuard) {
    600             if (mCallerInfoQueryState == CALLERINFO_QUERYING) {
    601                 mCallerInfoQueryState = CALLERINFO_QUERY_READY;
    602                 isQueryExecutionTimeExpired = true;
    603             }
    604         }
    605         if (isQueryExecutionTimeExpired) {
    606             // There may be a problem with the query here, since the
    607             // default ringtone is playing instead of the custom one.
    608             Log.w(LOG_TAG, "CallerInfo query took too long; falling back to default ringtone");
    609             EventLog.writeEvent(EventLogTags.PHONE_UI_RINGER_QUERY_ELAPSED);
    610         }
    611 
    612         // Make sure we still have an incoming call!
    613         //
    614         // (It's possible for the incoming call to have been disconnected
    615         // while we were running the query.  In that case we better not
    616         // start the ringer here, since there won't be any future
    617         // DISCONNECT event to stop it!)
    618         //
    619         // Note we don't have to worry about the incoming call going away
    620         // *after* this check but before we call mRinger.ring() below,
    621         // since in that case we *will* still get a DISCONNECT message sent
    622         // to our handler.  (And we will correctly stop the ringer when we
    623         // process that event.)
    624         if (mCM.getState() != PhoneConstants.State.RINGING) {
    625             Log.i(LOG_TAG, "onCustomRingQueryComplete: No incoming call! Bailing out...");
    626             // Don't start the ringer *or* bring up the "incoming call" UI.
    627             // Just bail out.
    628             return;
    629         }
    630 
    631         // If the ringing call still does not have any connection anymore, do not send the
    632         // notification to the CallModeler.
    633         final Call ringingCall = mCM.getFirstActiveRingingCall();
    634 
    635         if (ringingCall != null && ringingCall.getLatestConnection() == c) {
    636             ringAndNotifyOfIncomingCall(c);
    637         }
    638     }
    639 
    640     private void onUnknownConnectionAppeared(AsyncResult r) {
    641         PhoneConstants.State state = mCM.getState();
    642 
    643         if (state == PhoneConstants.State.OFFHOOK) {
    644             if (DBG) log("unknown connection appeared...");
    645 
    646             onPhoneStateChanged(r);
    647         }
    648     }
    649 
    650     /**
    651      * Notifies the Call Modeler that there is a new ringing connection.
    652      * If it is not a waiting call (there is no other active call in foreground), we will ring the
    653      * ringtone. Otherwise we will play the call waiting tone instead.
    654      * @param c The new ringing connection.
    655      */
    656     private void ringAndNotifyOfIncomingCall(Connection c) {
    657         if (PhoneUtils.isRealIncomingCall(c.getState())) {
    658             mRinger.ring();
    659         } else {
    660             if (VDBG) log("- starting call waiting tone...");
    661             if (mCallWaitingTonePlayer == null) {
    662                 mCallWaitingTonePlayer = new InCallTonePlayer(InCallTonePlayer.TONE_CALL_WAITING);
    663                 mCallWaitingTonePlayer.start();
    664             }
    665         }
    666         mCallModeler.onNewRingingConnection(c);
    667     }
    668 
    669     /**
    670      * Updates the phone UI in response to phone state changes.
    671      *
    672      * Watch out: certain state changes are actually handled by their own
    673      * specific methods:
    674      *   - see onNewRingingConnection() for new incoming calls
    675      *   - see onDisconnect() for calls being hung up or disconnected
    676      */
    677     private void onPhoneStateChanged(AsyncResult r) {
    678         PhoneConstants.State state = mCM.getState();
    679         if (VDBG) log("onPhoneStateChanged: state = " + state);
    680 
    681         // Turn status bar notifications on or off depending upon the state
    682         // of the phone.  Notification Alerts (audible or vibrating) should
    683         // be on if and only if the phone is IDLE.
    684         mApplication.notificationMgr.statusBarHelper
    685                 .enableNotificationAlerts(state == PhoneConstants.State.IDLE);
    686 
    687         Phone fgPhone = mCM.getFgPhone();
    688         if (fgPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
    689             if ((fgPhone.getForegroundCall().getState() == Call.State.ACTIVE)
    690                     && ((mPreviousCdmaCallState == Call.State.DIALING)
    691                     ||  (mPreviousCdmaCallState == Call.State.ALERTING))) {
    692                 if (mIsCdmaRedialCall) {
    693                     int toneToPlay = InCallTonePlayer.TONE_REDIAL;
    694                     new InCallTonePlayer(toneToPlay).start();
    695                 }
    696                 // Stop any signal info tone when call moves to ACTIVE state
    697                 stopSignalInfoTone();
    698             }
    699             mPreviousCdmaCallState = fgPhone.getForegroundCall().getState();
    700         }
    701 
    702         // Have the PhoneApp recompute its mShowBluetoothIndication
    703         // flag based on the (new) telephony state.
    704         // There's no need to force a UI update since we update the
    705         // in-call notification ourselves (below), and the InCallScreen
    706         // listens for phone state changes itself.
    707         // TODO: Have BluetoothManager listen to CallModeler instead of relying on
    708         // CallNotifier
    709         mBluetoothManager.updateBluetoothIndication();
    710 
    711 
    712         // Update the phone state and other sensor/lock.
    713         mApplication.updatePhoneState(state);
    714 
    715         if (state == PhoneConstants.State.OFFHOOK) {
    716             // stop call waiting tone if needed when answering
    717             if (mCallWaitingTonePlayer != null) {
    718                 mCallWaitingTonePlayer.stopTone();
    719                 mCallWaitingTonePlayer = null;
    720             }
    721 
    722             if (VDBG) log("onPhoneStateChanged: OFF HOOK");
    723             // make sure audio is in in-call mode now
    724             PhoneUtils.setAudioMode(mCM);
    725 
    726             // Since we're now in-call, the Ringer should definitely *not*
    727             // be ringing any more.  (This is just a sanity-check; we
    728             // already stopped the ringer explicitly back in
    729             // PhoneUtils.answerCall(), before the call to phone.acceptCall().)
    730             // TODO: Confirm that this call really *is* unnecessary, and if so,
    731             // remove it!
    732             if (DBG) log("stopRing()... (OFFHOOK state)");
    733             mRinger.stopRing();
    734         }
    735 
    736         if (fgPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
    737             Connection c = fgPhone.getForegroundCall().getLatestConnection();
    738             if ((c != null) && (PhoneNumberUtils.isLocalEmergencyNumber(c.getAddress(),
    739                                                                         mApplication))) {
    740                 if (VDBG) log("onPhoneStateChanged: it is an emergency call.");
    741                 Call.State callState = fgPhone.getForegroundCall().getState();
    742                 if (mEmergencyTonePlayerVibrator == null) {
    743                     mEmergencyTonePlayerVibrator = new EmergencyTonePlayerVibrator();
    744                 }
    745 
    746                 if (callState == Call.State.DIALING || callState == Call.State.ALERTING) {
    747                     mIsEmergencyToneOn = Settings.Global.getInt(
    748                             mApplication.getContentResolver(),
    749                             Settings.Global.EMERGENCY_TONE, EMERGENCY_TONE_OFF);
    750                     if (mIsEmergencyToneOn != EMERGENCY_TONE_OFF &&
    751                         mCurrentEmergencyToneState == EMERGENCY_TONE_OFF) {
    752                         if (mEmergencyTonePlayerVibrator != null) {
    753                             mEmergencyTonePlayerVibrator.start();
    754                         }
    755                     }
    756                 } else if (callState == Call.State.ACTIVE) {
    757                     if (mCurrentEmergencyToneState != EMERGENCY_TONE_OFF) {
    758                         if (mEmergencyTonePlayerVibrator != null) {
    759                             mEmergencyTonePlayerVibrator.stop();
    760                         }
    761                     }
    762                 }
    763             }
    764         }
    765 
    766         if ((fgPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM)
    767                 || (fgPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_SIP)) {
    768             Call.State callState = mCM.getActiveFgCallState();
    769             if (!callState.isDialing()) {
    770                 // If call get activated or disconnected before the ringback
    771                 // tone stops, we have to stop it to prevent disturbing.
    772                 if (mInCallRingbackTonePlayer != null) {
    773                     mInCallRingbackTonePlayer.stopTone();
    774                     mInCallRingbackTonePlayer = null;
    775                 }
    776             }
    777         }
    778     }
    779 
    780     void updateCallNotifierRegistrationsAfterRadioTechnologyChange() {
    781         if (DBG) Log.d(LOG_TAG, "updateCallNotifierRegistrationsAfterRadioTechnologyChange...");
    782 
    783         // Clear ringback tone player
    784         mInCallRingbackTonePlayer = null;
    785 
    786         // Clear call waiting tone player
    787         mCallWaitingTonePlayer = null;
    788 
    789         // Instantiate mSignalInfoToneGenerator
    790         createSignalInfoToneGenerator();
    791     }
    792 
    793     /**
    794      * Implemented for CallerInfoAsyncQuery.OnQueryCompleteListener interface.
    795      * refreshes the CallCard data when it called.  If called with this
    796      * class itself, it is assumed that we have been waiting for the ringtone
    797      * and direct to voicemail settings to update.
    798      */
    799     @Override
    800     public void onQueryComplete(int token, Object cookie, CallerInfo ci) {
    801         if (cookie instanceof Long) {
    802             if (VDBG) log("CallerInfo query complete, posting missed call notification");
    803 
    804             mApplication.notificationMgr.notifyMissedCall(ci.name, ci.phoneNumber,
    805                     ci.numberPresentation, ci.phoneLabel, ci.cachedPhoto, ci.cachedPhotoIcon,
    806                     ((Long) cookie).longValue());
    807         } else if (cookie instanceof Connection) {
    808             final Connection c = (Connection) cookie;
    809             if (VDBG) log("CallerInfo query complete (for CallNotifier), "
    810                     + "updating state for incoming call..");
    811 
    812             // get rid of the timeout messages
    813             removeMessages(RINGER_CUSTOM_RINGTONE_QUERY_TIMEOUT);
    814 
    815             boolean isQueryExecutionTimeOK = false;
    816             synchronized (mCallerInfoQueryStateGuard) {
    817                 if (mCallerInfoQueryState == CALLERINFO_QUERYING) {
    818                     mCallerInfoQueryState = CALLERINFO_QUERY_READY;
    819                     isQueryExecutionTimeOK = true;
    820                 }
    821             }
    822             //if we're in the right state
    823             if (isQueryExecutionTimeOK) {
    824 
    825                 // send directly to voicemail.
    826                 if (ci.shouldSendToVoicemail) {
    827                     if (DBG) log("send to voicemail flag detected. hanging up.");
    828                     final Call ringingCall = mCM.getFirstActiveRingingCall();
    829                     if (ringingCall != null && ringingCall.getLatestConnection() == c) {
    830                         PhoneUtils.hangupRingingCall(ringingCall);
    831                         return;
    832                     }
    833                 }
    834 
    835                 // set the ringtone uri to prepare for the ring.
    836                 if (ci.contactRingtoneUri != null) {
    837                     if (DBG) log("custom ringtone found, setting up ringer.");
    838                     Ringer r = mRinger;
    839                     r.setCustomRingtoneUri(ci.contactRingtoneUri);
    840                 }
    841                 // ring, and other post-ring actions.
    842                 onCustomRingQueryComplete(c);
    843             }
    844         }
    845     }
    846 
    847     /**
    848      * Called when asynchronous CallerInfo query is taking too long (more than
    849      * {@link #RINGTONE_QUERY_WAIT_TIME} msec), but we cannot wait any more.
    850      *
    851      * This looks up in-memory fallback cache and use it when available. If not, it just calls
    852      * {@link #onCustomRingQueryComplete()} with default ringtone ("Send to voicemail" flag will
    853      * be just ignored).
    854      *
    855      * @param number The phone number used for the async query. This method will take care of
    856      * formatting or normalization of the number.
    857      */
    858     private void onCustomRingtoneQueryTimeout(Connection c) {
    859         // First of all, this case itself should be rare enough, though we cannot avoid it in
    860         // some situations (e.g. IPC is slow due to system overload, database is in sync, etc.)
    861         Log.w(LOG_TAG, "CallerInfo query took too long; look up local fallback cache.");
    862 
    863         // This method is intentionally verbose for now to detect possible bad side-effect for it.
    864         // TODO: Remove the verbose log when it looks stable and reliable enough.
    865 
    866 
    867         if (c != null) {
    868             final CallerInfoCache.CacheEntry entry =
    869                     mApplication.callerInfoCache.getCacheEntry(c.getAddress());
    870             if (entry != null) {
    871                 if (entry.sendToVoicemail) {
    872                     log("send to voicemail flag detected (in fallback cache). hanging up.");
    873                     if (mCM.getFirstActiveRingingCall().getLatestConnection() == c) {
    874                         PhoneUtils.hangupRingingCall(mCM.getFirstActiveRingingCall());
    875                         return;
    876                     }
    877                 }
    878 
    879                 if (entry.customRingtone != null) {
    880                     log("custom ringtone found (in fallback cache), setting up ringer: "
    881                             + entry.customRingtone);
    882                     this.mRinger.setCustomRingtoneUri(Uri.parse(entry.customRingtone));
    883                 }
    884             } else {
    885                 // In this case we call onCustomRingQueryComplete(), just
    886                 // like if the query had completed normally.  (But we're
    887                 // going to get the default ringtone, since we never got
    888                 // the chance to call Ringer.setCustomRingtoneUri()).
    889                 log("Failed to find fallback cache. Use default ringer tone.");
    890             }
    891         }
    892 
    893         onCustomRingQueryComplete(c);
    894     }
    895 
    896     private void onDisconnect(AsyncResult r) {
    897         if (VDBG) log("onDisconnect()...  CallManager state: " + mCM.getState());
    898 
    899         mVoicePrivacyState = false;
    900         Connection c = (Connection) r.result;
    901         if (c != null) {
    902             log("onDisconnect: cause = " + c.getDisconnectCause()
    903                   + ", incoming = " + c.isIncoming()
    904                   + ", date = " + c.getCreateTime());
    905         } else {
    906             Log.w(LOG_TAG, "onDisconnect: null connection");
    907         }
    908 
    909         int autoretrySetting = 0;
    910         if ((c != null) && (c.getCall().getPhone().getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA)) {
    911             autoretrySetting = android.provider.Settings.Global.getInt(mApplication.
    912                     getContentResolver(),android.provider.Settings.Global.CALL_AUTO_RETRY, 0);
    913         }
    914 
    915         // Stop any signalInfo tone being played when a call gets ended
    916         stopSignalInfoTone();
    917 
    918         if ((c != null) && (c.getCall().getPhone().getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA)) {
    919             // Resetting the CdmaPhoneCallState members
    920             mApplication.cdmaPhoneCallState.resetCdmaPhoneCallState();
    921 
    922             // Remove Call waiting timers
    923             removeMessages(CALLWAITING_CALLERINFO_DISPLAY_DONE);
    924             removeMessages(CALLWAITING_ADDCALL_DISABLE_TIMEOUT);
    925         }
    926 
    927         // Stop the ringer if it was ringing (for an incoming call that
    928         // either disconnected by itself, or was rejected by the user.)
    929         //
    930         // TODO: We technically *shouldn't* stop the ringer if the
    931         // foreground or background call disconnects while an incoming call
    932         // is still ringing, but that's a really rare corner case.
    933         // It's safest to just unconditionally stop the ringer here.
    934 
    935         // CDMA: For Call collision cases i.e. when the user makes an out going call
    936         // and at the same time receives an Incoming Call, the Incoming Call is given
    937         // higher preference. At this time framework sends a disconnect for the Out going
    938         // call connection hence we should *not* be stopping the ringer being played for
    939         // the Incoming Call
    940         Call ringingCall = mCM.getFirstActiveRingingCall();
    941         if (ringingCall.getPhone().getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
    942             if (PhoneUtils.isRealIncomingCall(ringingCall.getState())) {
    943                 // Also we need to take off the "In Call" icon from the Notification
    944                 // area as the Out going Call never got connected
    945                 if (DBG) log("cancelCallInProgressNotifications()... (onDisconnect)");
    946                 mApplication.notificationMgr.cancelCallInProgressNotifications();
    947             } else {
    948                 if (DBG) log("stopRing()... (onDisconnect)");
    949                 mRinger.stopRing();
    950             }
    951         } else { // GSM
    952             if (DBG) log("stopRing()... (onDisconnect)");
    953             mRinger.stopRing();
    954         }
    955 
    956         // stop call waiting tone if needed when disconnecting
    957         if (mCallWaitingTonePlayer != null) {
    958             mCallWaitingTonePlayer.stopTone();
    959             mCallWaitingTonePlayer = null;
    960         }
    961 
    962         // If this is the end of an OTASP call, pass it on to the PhoneApp.
    963         if (c != null && TelephonyCapabilities.supportsOtasp(c.getCall().getPhone())) {
    964             final String number = c.getAddress();
    965             if (c.getCall().getPhone().isOtaSpNumber(number)) {
    966                 if (DBG) log("onDisconnect: this was an OTASP call!");
    967                 mApplication.handleOtaspDisconnect();
    968             }
    969         }
    970 
    971         // Check for the various tones we might need to play (thru the
    972         // earpiece) after a call disconnects.
    973         int toneToPlay = InCallTonePlayer.TONE_NONE;
    974 
    975         // The "Busy" or "Congestion" tone is the highest priority:
    976         if (c != null) {
    977             Connection.DisconnectCause cause = c.getDisconnectCause();
    978             if (cause == Connection.DisconnectCause.BUSY) {
    979                 if (DBG) log("- need to play BUSY tone!");
    980                 toneToPlay = InCallTonePlayer.TONE_BUSY;
    981             } else if (cause == Connection.DisconnectCause.CONGESTION) {
    982                 if (DBG) log("- need to play CONGESTION tone!");
    983                 toneToPlay = InCallTonePlayer.TONE_CONGESTION;
    984             } else if (((cause == Connection.DisconnectCause.NORMAL)
    985                     || (cause == Connection.DisconnectCause.LOCAL))
    986                     && (mApplication.isOtaCallInActiveState())) {
    987                 if (DBG) log("- need to play OTA_CALL_END tone!");
    988                 toneToPlay = InCallTonePlayer.TONE_OTA_CALL_END;
    989             } else if (cause == Connection.DisconnectCause.CDMA_REORDER) {
    990                 if (DBG) log("- need to play CDMA_REORDER tone!");
    991                 toneToPlay = InCallTonePlayer.TONE_REORDER;
    992             } else if (cause == Connection.DisconnectCause.CDMA_INTERCEPT) {
    993                 if (DBG) log("- need to play CDMA_INTERCEPT tone!");
    994                 toneToPlay = InCallTonePlayer.TONE_INTERCEPT;
    995             } else if (cause == Connection.DisconnectCause.CDMA_DROP) {
    996                 if (DBG) log("- need to play CDMA_DROP tone!");
    997                 toneToPlay = InCallTonePlayer.TONE_CDMA_DROP;
    998             } else if (cause == Connection.DisconnectCause.OUT_OF_SERVICE) {
    999                 if (DBG) log("- need to play OUT OF SERVICE tone!");
   1000                 toneToPlay = InCallTonePlayer.TONE_OUT_OF_SERVICE;
   1001             } else if (cause == Connection.DisconnectCause.UNOBTAINABLE_NUMBER) {
   1002                 if (DBG) log("- need to play TONE_UNOBTAINABLE_NUMBER tone!");
   1003                 toneToPlay = InCallTonePlayer.TONE_UNOBTAINABLE_NUMBER;
   1004             } else if (cause == Connection.DisconnectCause.ERROR_UNSPECIFIED) {
   1005                 if (DBG) log("- DisconnectCause is ERROR_UNSPECIFIED: play TONE_CALL_ENDED!");
   1006                 toneToPlay = InCallTonePlayer.TONE_CALL_ENDED;
   1007             }
   1008         }
   1009 
   1010         // If we don't need to play BUSY or CONGESTION, then play the
   1011         // "call ended" tone if this was a "regular disconnect" (i.e. a
   1012         // normal call where one end or the other hung up) *and* this
   1013         // disconnect event caused the phone to become idle.  (In other
   1014         // words, we *don't* play the sound if one call hangs up but
   1015         // there's still an active call on the other line.)
   1016         // TODO: We may eventually want to disable this via a preference.
   1017         if ((toneToPlay == InCallTonePlayer.TONE_NONE)
   1018             && (mCM.getState() == PhoneConstants.State.IDLE)
   1019             && (c != null)) {
   1020             Connection.DisconnectCause cause = c.getDisconnectCause();
   1021             if ((cause == Connection.DisconnectCause.NORMAL)  // remote hangup
   1022                 || (cause == Connection.DisconnectCause.LOCAL)) {  // local hangup
   1023                 if (VDBG) log("- need to play CALL_ENDED tone!");
   1024                 toneToPlay = InCallTonePlayer.TONE_CALL_ENDED;
   1025                 mIsCdmaRedialCall = false;
   1026             }
   1027         }
   1028 
   1029         // All phone calls are disconnected.
   1030         if (mCM.getState() == PhoneConstants.State.IDLE) {
   1031             // Don't reset the audio mode or bluetooth/speakerphone state
   1032             // if we still need to let the user hear a tone through the earpiece.
   1033             if (toneToPlay == InCallTonePlayer.TONE_NONE) {
   1034                 resetAudioStateAfterDisconnect();
   1035             }
   1036 
   1037             mApplication.notificationMgr.cancelCallInProgressNotifications();
   1038         }
   1039 
   1040         if (c != null) {
   1041             mCallLogger.logCall(c);
   1042 
   1043             final String number = c.getAddress();
   1044             final Phone phone = c.getCall().getPhone();
   1045             final boolean isEmergencyNumber =
   1046                     PhoneNumberUtils.isLocalEmergencyNumber(number, mApplication);
   1047 
   1048             if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
   1049                 if ((isEmergencyNumber)
   1050                         && (mCurrentEmergencyToneState != EMERGENCY_TONE_OFF)) {
   1051                     if (mEmergencyTonePlayerVibrator != null) {
   1052                         mEmergencyTonePlayerVibrator.stop();
   1053                     }
   1054                 }
   1055             }
   1056 
   1057             final long date = c.getCreateTime();
   1058             final Connection.DisconnectCause cause = c.getDisconnectCause();
   1059             final boolean missedCall = c.isIncoming() &&
   1060                     (cause == Connection.DisconnectCause.INCOMING_MISSED);
   1061             if (missedCall) {
   1062                 // Show the "Missed call" notification.
   1063                 // (Note we *don't* do this if this was an incoming call that
   1064                 // the user deliberately rejected.)
   1065                 showMissedCallNotification(c, date);
   1066             }
   1067 
   1068             // Possibly play a "post-disconnect tone" thru the earpiece.
   1069             // We do this here, rather than from the InCallScreen
   1070             // activity, since we need to do this even if you're not in
   1071             // the Phone UI at the moment the connection ends.
   1072             if (toneToPlay != InCallTonePlayer.TONE_NONE) {
   1073                 if (VDBG) log("- starting post-disconnect tone (" + toneToPlay + ")...");
   1074                 new InCallTonePlayer(toneToPlay).start();
   1075 
   1076                 // TODO: alternatively, we could start an InCallTonePlayer
   1077                 // here with an "unlimited" tone length,
   1078                 // and manually stop it later when this connection truly goes
   1079                 // away.  (The real connection over the network was closed as soon
   1080                 // as we got the BUSY message.  But our telephony layer keeps the
   1081                 // connection open for a few extra seconds so we can show the
   1082                 // "busy" indication to the user.  We could stop the busy tone
   1083                 // when *that* connection's "disconnect" event comes in.)
   1084             }
   1085 
   1086             if (((mPreviousCdmaCallState == Call.State.DIALING)
   1087                     || (mPreviousCdmaCallState == Call.State.ALERTING))
   1088                     && (!isEmergencyNumber)
   1089                     && (cause != Connection.DisconnectCause.INCOMING_MISSED )
   1090                     && (cause != Connection.DisconnectCause.NORMAL)
   1091                     && (cause != Connection.DisconnectCause.LOCAL)
   1092                     && (cause != Connection.DisconnectCause.INCOMING_REJECTED)) {
   1093                 if (!mIsCdmaRedialCall) {
   1094                     if (autoretrySetting == InCallScreen.AUTO_RETRY_ON) {
   1095                         // TODO: (Moto): The contact reference data may need to be stored and use
   1096                         // here when redialing a call. For now, pass in NULL as the URI parameter.
   1097                         final int status =
   1098                                 PhoneUtils.placeCall(mApplication, phone, number, null, false);
   1099                         if (status != PhoneUtils.CALL_STATUS_FAILED) {
   1100                             mIsCdmaRedialCall = true;
   1101                         }
   1102                     } else {
   1103                         mIsCdmaRedialCall = false;
   1104                     }
   1105                 } else {
   1106                     mIsCdmaRedialCall = false;
   1107                 }
   1108             }
   1109         }
   1110     }
   1111 
   1112     /**
   1113      * Resets the audio mode and speaker state when a call ends.
   1114      */
   1115     private void resetAudioStateAfterDisconnect() {
   1116         if (VDBG) log("resetAudioStateAfterDisconnect()...");
   1117 
   1118         if (mBluetoothHeadset != null) {
   1119             mBluetoothHeadset.disconnectAudio();
   1120         }
   1121 
   1122         // call turnOnSpeaker() with state=false and store=true even if speaker
   1123         // is already off to reset user requested speaker state.
   1124         PhoneUtils.turnOnSpeaker(mApplication, false, true);
   1125 
   1126         PhoneUtils.setAudioMode(mCM);
   1127     }
   1128 
   1129     private void onMwiChanged(boolean visible) {
   1130         if (VDBG) log("onMwiChanged(): " + visible);
   1131 
   1132         // "Voicemail" is meaningless on non-voice-capable devices,
   1133         // so ignore MWI events.
   1134         if (!PhoneGlobals.sVoiceCapable) {
   1135             // ...but still log a warning, since we shouldn't have gotten this
   1136             // event in the first place!
   1137             // (PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR events
   1138             // *should* be blocked at the telephony layer on non-voice-capable
   1139             // capable devices.)
   1140             Log.w(LOG_TAG, "Got onMwiChanged() on non-voice-capable device! Ignoring...");
   1141             return;
   1142         }
   1143 
   1144         mApplication.notificationMgr.updateMwi(visible);
   1145     }
   1146 
   1147     /**
   1148      * Posts a delayed PHONE_MWI_CHANGED event, to schedule a "retry" for a
   1149      * failed NotificationMgr.updateMwi() call.
   1150      */
   1151     /* package */ void sendMwiChangedDelayed(long delayMillis) {
   1152         Message message = Message.obtain(this, PHONE_MWI_CHANGED);
   1153         sendMessageDelayed(message, delayMillis);
   1154     }
   1155 
   1156     private void onCfiChanged(boolean visible) {
   1157         if (VDBG) log("onCfiChanged(): " + visible);
   1158         mApplication.notificationMgr.updateCfi(visible);
   1159     }
   1160 
   1161     /**
   1162      * Indicates whether or not this ringer is ringing.
   1163      */
   1164     boolean isRinging() {
   1165         return mRinger.isRinging();
   1166     }
   1167 
   1168     /**
   1169      * Stops the current ring, and tells the notifier that future
   1170      * ring requests should be ignored.
   1171      */
   1172     void silenceRinger() {
   1173         mSilentRingerRequested = true;
   1174         if (DBG) log("stopRing()... (silenceRinger)");
   1175         mRinger.stopRing();
   1176     }
   1177 
   1178     /**
   1179      * Restarts the ringer after having previously silenced it.
   1180      *
   1181      * (This is a no-op if the ringer is actually still ringing, or if the
   1182      * incoming ringing call no longer exists.)
   1183      */
   1184     /* package */ void restartRinger() {
   1185         if (DBG) log("restartRinger()...");
   1186         // Already ringing or Silent requested; no need to restart.
   1187         if (isRinging() || mSilentRingerRequested) return;
   1188 
   1189         final Call ringingCall = mCM.getFirstActiveRingingCall();
   1190         // Don't check ringingCall.isRinging() here, since that'll be true
   1191         // for the WAITING state also.  We only allow the ringer for
   1192         // regular INCOMING calls.
   1193         if (DBG) log("- ringingCall state: " + ringingCall.getState());
   1194         if (ringingCall.getState() == Call.State.INCOMING) {
   1195             mRinger.ring();
   1196         }
   1197     }
   1198 
   1199     /**
   1200      * Helper class to play tones through the earpiece (or speaker / BT)
   1201      * during a call, using the ToneGenerator.
   1202      *
   1203      * To use, just instantiate a new InCallTonePlayer
   1204      * (passing in the TONE_* constant for the tone you want)
   1205      * and start() it.
   1206      *
   1207      * When we're done playing the tone, if the phone is idle at that
   1208      * point, we'll reset the audio routing and speaker state.
   1209      * (That means that for tones that get played *after* a call
   1210      * disconnects, like "busy" or "congestion" or "call ended", you
   1211      * should NOT call resetAudioStateAfterDisconnect() yourself.
   1212      * Instead, just start the InCallTonePlayer, which will automatically
   1213      * defer the resetAudioStateAfterDisconnect() call until the tone
   1214      * finishes playing.)
   1215      */
   1216     private class InCallTonePlayer extends Thread {
   1217         private int mToneId;
   1218         private int mState;
   1219         // The possible tones we can play.
   1220         public static final int TONE_NONE = 0;
   1221         public static final int TONE_CALL_WAITING = 1;
   1222         public static final int TONE_BUSY = 2;
   1223         public static final int TONE_CONGESTION = 3;
   1224         public static final int TONE_CALL_ENDED = 4;
   1225         public static final int TONE_VOICE_PRIVACY = 5;
   1226         public static final int TONE_REORDER = 6;
   1227         public static final int TONE_INTERCEPT = 7;
   1228         public static final int TONE_CDMA_DROP = 8;
   1229         public static final int TONE_OUT_OF_SERVICE = 9;
   1230         public static final int TONE_REDIAL = 10;
   1231         public static final int TONE_OTA_CALL_END = 11;
   1232         public static final int TONE_RING_BACK = 12;
   1233         public static final int TONE_UNOBTAINABLE_NUMBER = 13;
   1234 
   1235         // The tone volume relative to other sounds in the stream
   1236         static final int TONE_RELATIVE_VOLUME_EMERGENCY = 100;
   1237         static final int TONE_RELATIVE_VOLUME_HIPRI = 80;
   1238         static final int TONE_RELATIVE_VOLUME_LOPRI = 50;
   1239 
   1240         // Buffer time (in msec) to add on to tone timeout value.
   1241         // Needed mainly when the timeout value for a tone is the
   1242         // exact duration of the tone itself.
   1243         static final int TONE_TIMEOUT_BUFFER = 20;
   1244 
   1245         // The tone state
   1246         static final int TONE_OFF = 0;
   1247         static final int TONE_ON = 1;
   1248         static final int TONE_STOPPED = 2;
   1249 
   1250         InCallTonePlayer(int toneId) {
   1251             super();
   1252             mToneId = toneId;
   1253             mState = TONE_OFF;
   1254         }
   1255 
   1256         @Override
   1257         public void run() {
   1258             log("InCallTonePlayer.run(toneId = " + mToneId + ")...");
   1259 
   1260             int toneType = 0;  // passed to ToneGenerator.startTone()
   1261             int toneVolume;  // passed to the ToneGenerator constructor
   1262             int toneLengthMillis;
   1263             int phoneType = mCM.getFgPhone().getPhoneType();
   1264 
   1265             switch (mToneId) {
   1266                 case TONE_CALL_WAITING:
   1267                     toneType = ToneGenerator.TONE_SUP_CALL_WAITING;
   1268                     toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
   1269                     // Call waiting tone is stopped by stopTone() method
   1270                     toneLengthMillis = Integer.MAX_VALUE - TONE_TIMEOUT_BUFFER;
   1271                     break;
   1272                 case TONE_BUSY:
   1273                     if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
   1274                         toneType = ToneGenerator.TONE_CDMA_NETWORK_BUSY_ONE_SHOT;
   1275                         toneVolume = TONE_RELATIVE_VOLUME_LOPRI;
   1276                         toneLengthMillis = 1000;
   1277                     } else if ((phoneType == PhoneConstants.PHONE_TYPE_GSM)
   1278                             || (phoneType == PhoneConstants.PHONE_TYPE_SIP)) {
   1279                         toneType = ToneGenerator.TONE_SUP_BUSY;
   1280                         toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
   1281                         toneLengthMillis = 4000;
   1282                     } else {
   1283                         throw new IllegalStateException("Unexpected phone type: " + phoneType);
   1284                     }
   1285                     break;
   1286                 case TONE_CONGESTION:
   1287                     toneType = ToneGenerator.TONE_SUP_CONGESTION;
   1288                     toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
   1289                     toneLengthMillis = 4000;
   1290                     break;
   1291 
   1292                 case TONE_CALL_ENDED:
   1293                     toneType = ToneGenerator.TONE_PROP_PROMPT;
   1294                     toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
   1295                     toneLengthMillis = 200;
   1296                     break;
   1297                  case TONE_OTA_CALL_END:
   1298                     if (mApplication.cdmaOtaConfigData.otaPlaySuccessFailureTone ==
   1299                             OtaUtils.OTA_PLAY_SUCCESS_FAILURE_TONE_ON) {
   1300                         toneType = ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD;
   1301                         toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
   1302                         toneLengthMillis = 750;
   1303                     } else {
   1304                         toneType = ToneGenerator.TONE_PROP_PROMPT;
   1305                         toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
   1306                         toneLengthMillis = 200;
   1307                     }
   1308                     break;
   1309                 case TONE_VOICE_PRIVACY:
   1310                     toneType = ToneGenerator.TONE_CDMA_ALERT_NETWORK_LITE;
   1311                     toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
   1312                     toneLengthMillis = 5000;
   1313                     break;
   1314                 case TONE_REORDER:
   1315                     toneType = ToneGenerator.TONE_CDMA_REORDER;
   1316                     toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
   1317                     toneLengthMillis = 4000;
   1318                     break;
   1319                 case TONE_INTERCEPT:
   1320                     toneType = ToneGenerator.TONE_CDMA_ABBR_INTERCEPT;
   1321                     toneVolume = TONE_RELATIVE_VOLUME_LOPRI;
   1322                     toneLengthMillis = 500;
   1323                     break;
   1324                 case TONE_CDMA_DROP:
   1325                 case TONE_OUT_OF_SERVICE:
   1326                     toneType = ToneGenerator.TONE_CDMA_CALLDROP_LITE;
   1327                     toneVolume = TONE_RELATIVE_VOLUME_LOPRI;
   1328                     toneLengthMillis = 375;
   1329                     break;
   1330                 case TONE_REDIAL:
   1331                     toneType = ToneGenerator.TONE_CDMA_ALERT_AUTOREDIAL_LITE;
   1332                     toneVolume = TONE_RELATIVE_VOLUME_LOPRI;
   1333                     toneLengthMillis = 5000;
   1334                     break;
   1335                 case TONE_RING_BACK:
   1336                     toneType = ToneGenerator.TONE_SUP_RINGTONE;
   1337                     toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
   1338                     // Call ring back tone is stopped by stopTone() method
   1339                     toneLengthMillis = Integer.MAX_VALUE - TONE_TIMEOUT_BUFFER;
   1340                     break;
   1341                 case TONE_UNOBTAINABLE_NUMBER:
   1342                     toneType = ToneGenerator.TONE_SUP_ERROR;
   1343                     toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
   1344                     toneLengthMillis = 4000;
   1345                     break;
   1346                 default:
   1347                     throw new IllegalArgumentException("Bad toneId: " + mToneId);
   1348             }
   1349 
   1350             // If the mToneGenerator creation fails, just continue without it.  It is
   1351             // a local audio signal, and is not as important.
   1352             ToneGenerator toneGenerator;
   1353             try {
   1354                 int stream;
   1355                 if (mBluetoothHeadset != null) {
   1356                     stream = mBluetoothHeadset.isAudioOn() ? AudioManager.STREAM_BLUETOOTH_SCO:
   1357                         AudioManager.STREAM_VOICE_CALL;
   1358                 } else {
   1359                     stream = AudioManager.STREAM_VOICE_CALL;
   1360                 }
   1361                 toneGenerator = new ToneGenerator(stream, toneVolume);
   1362                 // if (DBG) log("- created toneGenerator: " + toneGenerator);
   1363             } catch (RuntimeException e) {
   1364                 Log.w(LOG_TAG,
   1365                       "InCallTonePlayer: Exception caught while creating ToneGenerator: " + e);
   1366                 toneGenerator = null;
   1367             }
   1368 
   1369             // Using the ToneGenerator (with the CALL_WAITING / BUSY /
   1370             // CONGESTION tones at least), the ToneGenerator itself knows
   1371             // the right pattern of tones to play; we do NOT need to
   1372             // manually start/stop each individual tone, or manually
   1373             // insert the correct delay between tones.  (We just start it
   1374             // and let it run for however long we want the tone pattern to
   1375             // continue.)
   1376             //
   1377             // TODO: When we stop the ToneGenerator in the middle of a
   1378             // "tone pattern", it sounds bad if we cut if off while the
   1379             // tone is actually playing.  Consider adding API to the
   1380             // ToneGenerator to say "stop at the next silent part of the
   1381             // pattern", or simply "play the pattern N times and then
   1382             // stop."
   1383             boolean needToStopTone = true;
   1384             boolean okToPlayTone = false;
   1385 
   1386             if (toneGenerator != null) {
   1387                 int ringerMode = mAudioManager.getRingerMode();
   1388                 if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
   1389                     if (toneType == ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD) {
   1390                         if ((ringerMode != AudioManager.RINGER_MODE_SILENT) &&
   1391                                 (ringerMode != AudioManager.RINGER_MODE_VIBRATE)) {
   1392                             if (DBG) log("- InCallTonePlayer: start playing call tone=" + toneType);
   1393                             okToPlayTone = true;
   1394                             needToStopTone = false;
   1395                         }
   1396                     } else if ((toneType == ToneGenerator.TONE_CDMA_NETWORK_BUSY_ONE_SHOT) ||
   1397                             (toneType == ToneGenerator.TONE_CDMA_REORDER) ||
   1398                             (toneType == ToneGenerator.TONE_CDMA_ABBR_REORDER) ||
   1399                             (toneType == ToneGenerator.TONE_CDMA_ABBR_INTERCEPT) ||
   1400                             (toneType == ToneGenerator.TONE_CDMA_CALLDROP_LITE)) {
   1401                         if (ringerMode != AudioManager.RINGER_MODE_SILENT) {
   1402                             if (DBG) log("InCallTonePlayer:playing call fail tone:" + toneType);
   1403                             okToPlayTone = true;
   1404                             needToStopTone = false;
   1405                         }
   1406                     } else if ((toneType == ToneGenerator.TONE_CDMA_ALERT_AUTOREDIAL_LITE) ||
   1407                                (toneType == ToneGenerator.TONE_CDMA_ALERT_NETWORK_LITE)) {
   1408                         if ((ringerMode != AudioManager.RINGER_MODE_SILENT) &&
   1409                                 (ringerMode != AudioManager.RINGER_MODE_VIBRATE)) {
   1410                             if (DBG) log("InCallTonePlayer:playing tone for toneType=" + toneType);
   1411                             okToPlayTone = true;
   1412                             needToStopTone = false;
   1413                         }
   1414                     } else { // For the rest of the tones, always OK to play.
   1415                         okToPlayTone = true;
   1416                     }
   1417                 } else {  // Not "CDMA"
   1418                     okToPlayTone = true;
   1419                 }
   1420 
   1421                 synchronized (this) {
   1422                     if (okToPlayTone && mState != TONE_STOPPED) {
   1423                         mState = TONE_ON;
   1424                         toneGenerator.startTone(toneType);
   1425                         try {
   1426                             wait(toneLengthMillis + TONE_TIMEOUT_BUFFER);
   1427                         } catch  (InterruptedException e) {
   1428                             Log.w(LOG_TAG,
   1429                                   "InCallTonePlayer stopped: " + e);
   1430                         }
   1431                         if (needToStopTone) {
   1432                             toneGenerator.stopTone();
   1433                         }
   1434                     }
   1435                     // if (DBG) log("- InCallTonePlayer: done playing.");
   1436                     toneGenerator.release();
   1437                     mState = TONE_OFF;
   1438                 }
   1439             }
   1440 
   1441             // Finally, do the same cleanup we otherwise would have done
   1442             // in onDisconnect().
   1443             //
   1444             // (But watch out: do NOT do this if the phone is in use,
   1445             // since some of our tones get played *during* a call (like
   1446             // CALL_WAITING) and we definitely *don't*
   1447             // want to reset the audio mode / speaker / bluetooth after
   1448             // playing those!
   1449             // This call is really here for use with tones that get played
   1450             // *after* a call disconnects, like "busy" or "congestion" or
   1451             // "call ended", where the phone has already become idle but
   1452             // we need to defer the resetAudioStateAfterDisconnect() call
   1453             // till the tone finishes playing.)
   1454             if (mCM.getState() == PhoneConstants.State.IDLE) {
   1455                 resetAudioStateAfterDisconnect();
   1456             }
   1457         }
   1458 
   1459         public void stopTone() {
   1460             synchronized (this) {
   1461                 if (mState == TONE_ON) {
   1462                     notify();
   1463                 }
   1464                 mState = TONE_STOPPED;
   1465             }
   1466         }
   1467     }
   1468 
   1469     /**
   1470      * Displays a notification when the phone receives a DisplayInfo record.
   1471      */
   1472     private void onDisplayInfo(AsyncResult r) {
   1473         // Extract the DisplayInfo String from the message
   1474         CdmaDisplayInfoRec displayInfoRec = (CdmaDisplayInfoRec)(r.result);
   1475 
   1476         if (displayInfoRec != null) {
   1477             String displayInfo = displayInfoRec.alpha;
   1478             if (DBG) log("onDisplayInfo: displayInfo=" + displayInfo);
   1479             CdmaDisplayInfo.displayInfoRecord(mApplication, displayInfo);
   1480 
   1481             // start a 2 second timer
   1482             sendEmptyMessageDelayed(DISPLAYINFO_NOTIFICATION_DONE,
   1483                     DISPLAYINFO_NOTIFICATION_TIME);
   1484         }
   1485     }
   1486 
   1487     /**
   1488      * Helper class to play SignalInfo tones using the ToneGenerator.
   1489      *
   1490      * To use, just instantiate a new SignalInfoTonePlayer
   1491      * (passing in the ToneID constant for the tone you want)
   1492      * and start() it.
   1493      */
   1494     private class SignalInfoTonePlayer extends Thread {
   1495         private int mToneId;
   1496 
   1497         SignalInfoTonePlayer(int toneId) {
   1498             super();
   1499             mToneId = toneId;
   1500         }
   1501 
   1502         @Override
   1503         public void run() {
   1504             log("SignalInfoTonePlayer.run(toneId = " + mToneId + ")...");
   1505 
   1506             if (mSignalInfoToneGenerator != null) {
   1507                 //First stop any ongoing SignalInfo tone
   1508                 mSignalInfoToneGenerator.stopTone();
   1509 
   1510                 //Start playing the new tone if its a valid tone
   1511                 mSignalInfoToneGenerator.startTone(mToneId);
   1512             }
   1513         }
   1514     }
   1515 
   1516     /**
   1517      * Plays a tone when the phone receives a SignalInfo record.
   1518      */
   1519     private void onSignalInfo(AsyncResult r) {
   1520         // Signal Info are totally ignored on non-voice-capable devices.
   1521         if (!PhoneGlobals.sVoiceCapable) {
   1522             Log.w(LOG_TAG, "Got onSignalInfo() on non-voice-capable device! Ignoring...");
   1523             return;
   1524         }
   1525 
   1526         if (PhoneUtils.isRealIncomingCall(mCM.getFirstActiveRingingCall().getState())) {
   1527             // Do not start any new SignalInfo tone when Call state is INCOMING
   1528             // and stop any previous SignalInfo tone which is being played
   1529             stopSignalInfoTone();
   1530         } else {
   1531             // Extract the SignalInfo String from the message
   1532             CdmaSignalInfoRec signalInfoRec = (CdmaSignalInfoRec)(r.result);
   1533             // Only proceed if a Signal info is present.
   1534             if (signalInfoRec != null) {
   1535                 boolean isPresent = signalInfoRec.isPresent;
   1536                 if (DBG) log("onSignalInfo: isPresent=" + isPresent);
   1537                 if (isPresent) {// if tone is valid
   1538                     int uSignalType = signalInfoRec.signalType;
   1539                     int uAlertPitch = signalInfoRec.alertPitch;
   1540                     int uSignal = signalInfoRec.signal;
   1541 
   1542                     if (DBG) log("onSignalInfo: uSignalType=" + uSignalType + ", uAlertPitch=" +
   1543                             uAlertPitch + ", uSignal=" + uSignal);
   1544                     //Map the Signal to a ToneGenerator ToneID only if Signal info is present
   1545                     int toneID = SignalToneUtil.getAudioToneFromSignalInfo
   1546                             (uSignalType, uAlertPitch, uSignal);
   1547 
   1548                     //Create the SignalInfo tone player and pass the ToneID
   1549                     new SignalInfoTonePlayer(toneID).start();
   1550                 }
   1551             }
   1552         }
   1553     }
   1554 
   1555     /**
   1556      * Stops a SignalInfo tone in the following condition
   1557      * 1 - On receiving a New Ringing Call
   1558      * 2 - On disconnecting a call
   1559      * 3 - On answering a Call Waiting Call
   1560      */
   1561     /* package */ void stopSignalInfoTone() {
   1562         if (DBG) log("stopSignalInfoTone: Stopping SignalInfo tone player");
   1563         new SignalInfoTonePlayer(ToneGenerator.TONE_CDMA_SIGNAL_OFF).start();
   1564     }
   1565 
   1566     /**
   1567      * Plays a Call waiting tone if it is present in the second incoming call.
   1568      */
   1569     private void onCdmaCallWaiting(AsyncResult r) {
   1570         // Remove any previous Call waiting timers in the queue
   1571         removeMessages(CALLWAITING_CALLERINFO_DISPLAY_DONE);
   1572         removeMessages(CALLWAITING_ADDCALL_DISABLE_TIMEOUT);
   1573 
   1574         // Set the Phone Call State to SINGLE_ACTIVE as there is only one connection
   1575         // else we would not have received Call waiting
   1576         mApplication.cdmaPhoneCallState.setCurrentCallState(
   1577                 CdmaPhoneCallState.PhoneCallState.SINGLE_ACTIVE);
   1578 
   1579         // Start timer for CW display
   1580         mCallWaitingTimeOut = false;
   1581         sendEmptyMessageDelayed(CALLWAITING_CALLERINFO_DISPLAY_DONE,
   1582                 CALLWAITING_CALLERINFO_DISPLAY_TIME);
   1583 
   1584         // Set the mAddCallMenuStateAfterCW state to false
   1585         mApplication.cdmaPhoneCallState.setAddCallMenuStateAfterCallWaiting(false);
   1586 
   1587         // Start the timer for disabling "Add Call" menu option
   1588         sendEmptyMessageDelayed(CALLWAITING_ADDCALL_DISABLE_TIMEOUT,
   1589                 CALLWAITING_ADDCALL_DISABLE_TIME);
   1590 
   1591         // Extract the Call waiting information
   1592         CdmaCallWaitingNotification infoCW = (CdmaCallWaitingNotification) r.result;
   1593         int isPresent = infoCW.isPresent;
   1594         if (DBG) log("onCdmaCallWaiting: isPresent=" + isPresent);
   1595         if (isPresent == 1 ) {//'1' if tone is valid
   1596             int uSignalType = infoCW.signalType;
   1597             int uAlertPitch = infoCW.alertPitch;
   1598             int uSignal = infoCW.signal;
   1599             if (DBG) log("onCdmaCallWaiting: uSignalType=" + uSignalType + ", uAlertPitch="
   1600                     + uAlertPitch + ", uSignal=" + uSignal);
   1601             //Map the Signal to a ToneGenerator ToneID only if Signal info is present
   1602             int toneID =
   1603                 SignalToneUtil.getAudioToneFromSignalInfo(uSignalType, uAlertPitch, uSignal);
   1604 
   1605             //Create the SignalInfo tone player and pass the ToneID
   1606             new SignalInfoTonePlayer(toneID).start();
   1607         }
   1608 
   1609         mCallModeler.onCdmaCallWaiting(infoCW);
   1610     }
   1611 
   1612     /**
   1613      * Posts a event causing us to clean up after rejecting (or timing-out) a
   1614      * CDMA call-waiting call.
   1615      *
   1616      * This method is safe to call from any thread.
   1617      * @see #onCdmaCallWaitingReject()
   1618      */
   1619     /* package */ void sendCdmaCallWaitingReject() {
   1620         sendEmptyMessage(CDMA_CALL_WAITING_REJECT);
   1621     }
   1622 
   1623     /**
   1624      * Performs Call logging based on Timeout or Ignore Call Waiting Call for CDMA,
   1625      * and finally calls Hangup on the Call Waiting connection.
   1626      *
   1627      * This method should be called only from the UI thread.
   1628      * @see #sendCdmaCallWaitingReject()
   1629      */
   1630     private void onCdmaCallWaitingReject() {
   1631         final Call ringingCall = mCM.getFirstActiveRingingCall();
   1632 
   1633         // Call waiting timeout scenario
   1634         if (ringingCall.getState() == Call.State.WAITING) {
   1635             // Code for perform Call logging and missed call notification
   1636             Connection c = ringingCall.getLatestConnection();
   1637 
   1638             if (c != null) {
   1639                 final int callLogType = mCallWaitingTimeOut ?
   1640                         Calls.MISSED_TYPE : Calls.INCOMING_TYPE;
   1641 
   1642                 // TODO: This callLogType override is not ideal. Connection should be astracted away
   1643                 // at a telephony-phone layer that can understand and edit the callTypes within
   1644                 // the abstraction for CDMA devices.
   1645                 mCallLogger.logCall(c, callLogType);
   1646 
   1647                 final long date = c.getCreateTime();
   1648                 if (callLogType == Calls.MISSED_TYPE) {
   1649                     // Add missed call notification
   1650                     showMissedCallNotification(c, date);
   1651                 } else {
   1652                     // Remove Call waiting 20 second display timer in the queue
   1653                     removeMessages(CALLWAITING_CALLERINFO_DISPLAY_DONE);
   1654                 }
   1655 
   1656                 // Hangup the RingingCall connection for CW
   1657                 PhoneUtils.hangup(c);
   1658             }
   1659 
   1660             //Reset the mCallWaitingTimeOut boolean
   1661             mCallWaitingTimeOut = false;
   1662         }
   1663 
   1664         // Call modeler needs to know about this event regardless of the
   1665         // state conditionals in the previous code.
   1666         mCallModeler.onCdmaCallWaitingReject();
   1667     }
   1668 
   1669     /**
   1670      * Return the private variable mPreviousCdmaCallState.
   1671      */
   1672     /* package */ Call.State getPreviousCdmaCallState() {
   1673         return mPreviousCdmaCallState;
   1674     }
   1675 
   1676     /**
   1677      * Return the private variable mVoicePrivacyState.
   1678      */
   1679     /* package */ boolean getVoicePrivacyState() {
   1680         return mVoicePrivacyState;
   1681     }
   1682 
   1683     /**
   1684      * Return the private variable mIsCdmaRedialCall.
   1685      */
   1686     /* package */ boolean getIsCdmaRedialCall() {
   1687         return mIsCdmaRedialCall;
   1688     }
   1689 
   1690     /**
   1691      * Helper function used to show a missed call notification.
   1692      */
   1693     private void showMissedCallNotification(Connection c, final long date) {
   1694         PhoneUtils.CallerInfoToken info =
   1695                 PhoneUtils.startGetCallerInfo(mApplication, c, this, Long.valueOf(date));
   1696         if (info != null) {
   1697             // at this point, we've requested to start a query, but it makes no
   1698             // sense to log this missed call until the query comes back.
   1699             if (VDBG) log("showMissedCallNotification: Querying for CallerInfo on missed call...");
   1700             if (info.isFinal) {
   1701                 // it seems that the query we have actually is up to date.
   1702                 // send the notification then.
   1703                 CallerInfo ci = info.currentInfo;
   1704 
   1705                 // Check number presentation value; if we have a non-allowed presentation,
   1706                 // then display an appropriate presentation string instead as the missed
   1707                 // call.
   1708                 String name = ci.name;
   1709                 String number = ci.phoneNumber;
   1710                 if (ci.numberPresentation == PhoneConstants.PRESENTATION_RESTRICTED) {
   1711                     name = mApplication.getString(R.string.private_num);
   1712                 } else if (ci.numberPresentation != PhoneConstants.PRESENTATION_ALLOWED) {
   1713                     name = mApplication.getString(R.string.unknown);
   1714                 } else {
   1715                     number = PhoneUtils.modifyForSpecialCnapCases(mApplication,
   1716                             ci, number, ci.numberPresentation);
   1717                 }
   1718                 mApplication.notificationMgr.notifyMissedCall(name, number, ci.numberPresentation,
   1719                         ci.phoneLabel, ci.cachedPhoto, ci.cachedPhotoIcon, date);
   1720             }
   1721         } else {
   1722             // getCallerInfo() can return null in rare cases, like if we weren't
   1723             // able to get a valid phone number out of the specified Connection.
   1724             Log.w(LOG_TAG, "showMissedCallNotification: got null CallerInfo for Connection " + c);
   1725         }
   1726     }
   1727 
   1728     /**
   1729      *  Inner class to handle emergency call tone and vibrator
   1730      */
   1731     private class EmergencyTonePlayerVibrator {
   1732         private final int EMG_VIBRATE_LENGTH = 1000;  // ms.
   1733         private final int EMG_VIBRATE_PAUSE  = 1000;  // ms.
   1734         private final long[] mVibratePattern =
   1735                 new long[] { EMG_VIBRATE_LENGTH, EMG_VIBRATE_PAUSE };
   1736 
   1737         private ToneGenerator mToneGenerator;
   1738         // We don't rely on getSystemService(Context.VIBRATOR_SERVICE) to make sure this vibrator
   1739         // object will be isolated from others.
   1740         private Vibrator mEmgVibrator = new SystemVibrator();
   1741         private int mInCallVolume;
   1742 
   1743         /**
   1744          * constructor
   1745          */
   1746         public EmergencyTonePlayerVibrator() {
   1747         }
   1748 
   1749         /**
   1750          * Start the emergency tone or vibrator.
   1751          */
   1752         private void start() {
   1753             if (VDBG) log("call startEmergencyToneOrVibrate.");
   1754             int ringerMode = mAudioManager.getRingerMode();
   1755 
   1756             if ((mIsEmergencyToneOn == EMERGENCY_TONE_ALERT) &&
   1757                     (ringerMode == AudioManager.RINGER_MODE_NORMAL)) {
   1758                 log("EmergencyTonePlayerVibrator.start(): emergency tone...");
   1759                 mToneGenerator = new ToneGenerator (AudioManager.STREAM_VOICE_CALL,
   1760                         InCallTonePlayer.TONE_RELATIVE_VOLUME_EMERGENCY);
   1761                 if (mToneGenerator != null) {
   1762                     mInCallVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL);
   1763                     mAudioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL,
   1764                             mAudioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL),
   1765                             0);
   1766                     mToneGenerator.startTone(ToneGenerator.TONE_CDMA_EMERGENCY_RINGBACK);
   1767                     mCurrentEmergencyToneState = EMERGENCY_TONE_ALERT;
   1768                 }
   1769             } else if (mIsEmergencyToneOn == EMERGENCY_TONE_VIBRATE) {
   1770                 log("EmergencyTonePlayerVibrator.start(): emergency vibrate...");
   1771                 if (mEmgVibrator != null) {
   1772                     mEmgVibrator.vibrate(mVibratePattern, 0);
   1773                     mCurrentEmergencyToneState = EMERGENCY_TONE_VIBRATE;
   1774                 }
   1775             }
   1776         }
   1777 
   1778         /**
   1779          * If the emergency tone is active, stop the tone or vibrator accordingly.
   1780          */
   1781         private void stop() {
   1782             if (VDBG) log("call stopEmergencyToneOrVibrate.");
   1783 
   1784             if ((mCurrentEmergencyToneState == EMERGENCY_TONE_ALERT)
   1785                     && (mToneGenerator != null)) {
   1786                 mToneGenerator.stopTone();
   1787                 mToneGenerator.release();
   1788                 mAudioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL,
   1789                         mInCallVolume,
   1790                         0);
   1791             } else if ((mCurrentEmergencyToneState == EMERGENCY_TONE_VIBRATE)
   1792                     && (mEmgVibrator != null)) {
   1793                 mEmgVibrator.cancel();
   1794             }
   1795             mCurrentEmergencyToneState = EMERGENCY_TONE_OFF;
   1796         }
   1797     }
   1798 
   1799      private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
   1800         new BluetoothProfile.ServiceListener() {
   1801         public void onServiceConnected(int profile, BluetoothProfile proxy) {
   1802             mBluetoothHeadset = (BluetoothHeadset) proxy;
   1803             if (VDBG) log("- Got BluetoothHeadset: " + mBluetoothHeadset);
   1804         }
   1805 
   1806         public void onServiceDisconnected(int profile) {
   1807             mBluetoothHeadset = null;
   1808         }
   1809     };
   1810 
   1811     private void onRingbackTone(AsyncResult r) {
   1812         boolean playTone = (Boolean)(r.result);
   1813 
   1814         if (playTone == true) {
   1815             // Only play when foreground call is in DIALING or ALERTING.
   1816             // to prevent a late coming playtone after ALERTING.
   1817             // Don't play ringback tone if it is in play, otherwise it will cut
   1818             // the current tone and replay it
   1819             if (mCM.getActiveFgCallState().isDialing() &&
   1820                 mInCallRingbackTonePlayer == null) {
   1821                 mInCallRingbackTonePlayer = new InCallTonePlayer(InCallTonePlayer.TONE_RING_BACK);
   1822                 mInCallRingbackTonePlayer.start();
   1823             }
   1824         } else {
   1825             if (mInCallRingbackTonePlayer != null) {
   1826                 mInCallRingbackTonePlayer.stopTone();
   1827                 mInCallRingbackTonePlayer = null;
   1828             }
   1829         }
   1830     }
   1831 
   1832     /**
   1833      * Toggle mute and unmute requests while keeping the same mute state
   1834      */
   1835     private void onResendMute() {
   1836         boolean muteState = PhoneUtils.getMute();
   1837         PhoneUtils.setMute(!muteState);
   1838         PhoneUtils.setMute(muteState);
   1839     }
   1840 
   1841     private void log(String msg) {
   1842         Log.d(LOG_TAG, msg);
   1843     }
   1844 }
   1845