Home | History | Annotate | Download | only in phone
      1 /*
      2  * Copyright (C) 2006 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.phone;
     18 
     19 import com.android.internal.telephony.Call;
     20 import com.android.internal.telephony.CallManager;
     21 import com.android.internal.telephony.CallerInfo;
     22 import com.android.internal.telephony.CallerInfoAsyncQuery;
     23 import com.android.internal.telephony.Connection;
     24 import com.android.internal.telephony.Phone;
     25 import com.android.internal.telephony.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      */
    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() */
    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
    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      */
    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      */
    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      */
    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      */
    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 
    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      */
    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      */
    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 
    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 
    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
    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      */
    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 
   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      */
   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 
   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      */
   1326     /* package */ void sendMwiChangedDelayed(long delayMillis) {
   1327         Message message = Message.obtain(this, PHONE_MWI_CHANGED);
   1328         sendMessageDelayed(message, delayMillis);
   1329     }
   1330 
   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      */
   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      */
   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      */
   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 
   1424         InCallTonePlayer(int toneId) {
   1425             super();
   1426             mToneId = toneId;
   1427             mState = TONE_OFF;
   1428         }
   1429 
   1430         @Override
   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 
   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      */
   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 
   1671         SignalInfoTonePlayer(int toneId) {
   1672             super();
   1673             mToneId = toneId;
   1674         }
   1675 
   1676         @Override
   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      */
   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      */
   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      */
   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      */
   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      */
   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      */
   1869     /* package */ Call.State getPreviousCdmaCallState() {
   1870         return mPreviousCdmaCallState;
   1871     }
   1872 
   1873     /**
   1874      * Return the private variable mVoicePrivacyState.
   1875      */
   1876     /* package */ boolean getVoicePrivacyState() {
   1877         return mVoicePrivacyState;
   1878     }
   1879 
   1880     /**
   1881      * Return the private variable mIsCdmaRedialCall.
   1882      */
   1883     /* package */ boolean getIsCdmaRedialCall() {
   1884         return mIsCdmaRedialCall;
   1885     }
   1886 
   1887     /**
   1888      * Helper function used to show a missed call notification.
   1889      */
   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          */
   1941         public EmergencyTonePlayerVibrator() {
   1942         }
   1943 
   1944         /**
   1945          * Start the emergency tone or vibrator.
   1946          */
   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          */
   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 
   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      */
   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      */
   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      */
   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      */
   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 
   2119     private void log(String msg) {
   2120         Log.d(LOG_TAG, msg);
   2121     }
   2122 }
   2123