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