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