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