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
17package com.android.phone;
18
19import com.android.internal.telephony.Call;
20import com.android.internal.telephony.CallManager;
21import com.android.internal.telephony.CallerInfo;
22import com.android.internal.telephony.CallerInfoAsyncQuery;
23import com.android.internal.telephony.Connection;
24import com.android.internal.telephony.Phone;
25import com.android.internal.telephony.PhoneConstants;
26import com.android.internal.telephony.PhoneBase;
27import com.android.internal.telephony.TelephonyCapabilities;
28import com.android.internal.telephony.cdma.CdmaCallWaitingNotification;
29import com.android.internal.telephony.cdma.CdmaInformationRecords.CdmaDisplayInfoRec;
30import com.android.internal.telephony.cdma.CdmaInformationRecords.CdmaSignalInfoRec;
31import com.android.internal.telephony.cdma.SignalToneUtil;
32
33import android.app.ActivityManagerNative;
34import android.bluetooth.BluetoothAdapter;
35import android.bluetooth.BluetoothHeadset;
36import android.bluetooth.BluetoothProfile;
37import android.content.Context;
38import android.media.AudioManager;
39import android.media.ToneGenerator;
40import android.net.Uri;
41import android.os.AsyncResult;
42import android.os.Handler;
43import android.os.Message;
44import android.os.SystemProperties;
45import android.os.SystemVibrator;
46import android.os.Vibrator;
47import android.provider.CallLog.Calls;
48import android.provider.Settings;
49import android.telephony.PhoneNumberUtils;
50import android.telephony.PhoneStateListener;
51import android.telephony.TelephonyManager;
52import android.util.EventLog;
53import 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 */
61public 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.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,
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