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.Connection;
     22 import com.android.internal.telephony.Phone;
     23 import com.android.internal.telephony.PhoneConstants;
     24 import com.android.internal.telephony.TelephonyCapabilities;
     25 import com.android.internal.telephony.cdma.CdmaInformationRecords.CdmaDisplayInfoRec;
     26 import com.android.internal.telephony.cdma.CdmaInformationRecords.CdmaSignalInfoRec;
     27 import com.android.internal.telephony.cdma.SignalToneUtil;
     28 
     29 import android.bluetooth.BluetoothAdapter;
     30 import android.bluetooth.BluetoothHeadset;
     31 import android.bluetooth.BluetoothProfile;
     32 import android.content.Context;
     33 import android.media.AudioAttributes;
     34 import android.media.AudioManager;
     35 import android.media.ToneGenerator;
     36 import android.os.AsyncResult;
     37 import android.os.Handler;
     38 import android.os.Message;
     39 import android.os.SystemProperties;
     40 import android.provider.Settings;
     41 import android.telecom.TelecomManager;
     42 import android.telephony.DisconnectCause;
     43 import android.telephony.PhoneNumberUtils;
     44 import android.telephony.PhoneStateListener;
     45 import android.telephony.SubscriptionInfo;
     46 import android.telephony.SubscriptionManager;
     47 import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
     48 import android.telephony.TelephonyManager;
     49 import android.util.ArrayMap;
     50 import android.util.Log;
     51 
     52 import java.util.Iterator;
     53 import java.util.List;
     54 import java.util.Map;
     55 
     56 /**
     57  * Phone app module that listens for phone state changes and various other
     58  * events from the telephony layer, and triggers any resulting UI behavior
     59  * (like starting the Incoming Call UI, playing in-call tones,
     60  * updating notifications, writing call log entries, etc.)
     61  */
     62 public class CallNotifier extends Handler {
     63     private static final String LOG_TAG = "CallNotifier";
     64     private static final boolean DBG =
     65             (PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1);
     66     private static final boolean VDBG = (PhoneGlobals.DBG_LEVEL >= 2);
     67 
     68     // Time to display the message from the underlying phone layers.
     69     private static final int SHOW_MESSAGE_NOTIFICATION_TIME = 3000; // msec
     70 
     71     /** The singleton instance. */
     72     private static CallNotifier sInstance;
     73 
     74     private Map<Integer, CallNotifierPhoneStateListener> mPhoneStateListeners =
     75             new ArrayMap<Integer, CallNotifierPhoneStateListener>();
     76 
     77     private PhoneGlobals mApplication;
     78     private CallManager mCM;
     79     private BluetoothHeadset mBluetoothHeadset;
     80 
     81     // ToneGenerator instance for playing SignalInfo tones
     82     private ToneGenerator mSignalInfoToneGenerator;
     83 
     84     // The tone volume relative to other sounds in the stream SignalInfo
     85     private static final int TONE_RELATIVE_VOLUME_SIGNALINFO = 80;
     86 
     87     private boolean mVoicePrivacyState = false;
     88 
     89     // Cached AudioManager
     90     private AudioManager mAudioManager;
     91     private SubscriptionManager mSubscriptionManager;
     92     private TelephonyManager mTelephonyManager;
     93 
     94     // Events from the Phone object:
     95     public static final int PHONE_DISCONNECT = 3;
     96     public static final int PHONE_STATE_DISPLAYINFO = 6;
     97     public static final int PHONE_STATE_SIGNALINFO = 7;
     98     public static final int PHONE_ENHANCED_VP_ON = 9;
     99     public static final int PHONE_ENHANCED_VP_OFF = 10;
    100     public static final int PHONE_SUPP_SERVICE_FAILED = 14;
    101     public static final int PHONE_TTY_MODE_RECEIVED = 15;
    102     // Events generated internally.
    103     // We should store all the possible event type values in one place to make sure that
    104     // they don't step on each others' toes.
    105     public static final int INTERNAL_SHOW_MESSAGE_NOTIFICATION_DONE = 22;
    106     // Other events from call manager
    107     public static final int EVENT_OTA_PROVISION_CHANGE = 20;
    108 
    109     /**
    110      * Initialize the singleton CallNotifier instance.
    111      * This is only done once, at startup, from PhoneApp.onCreate().
    112      */
    113     /* package */ static CallNotifier init(
    114             PhoneGlobals app) {
    115         synchronized (CallNotifier.class) {
    116             if (sInstance == null) {
    117                 sInstance = new CallNotifier(app);
    118             } else {
    119                 Log.wtf(LOG_TAG, "init() called multiple times!  sInstance = " + sInstance);
    120             }
    121             return sInstance;
    122         }
    123     }
    124 
    125     /** Private constructor; @see init() */
    126     private CallNotifier(
    127             PhoneGlobals app) {
    128         mApplication = app;
    129         mCM = app.mCM;
    130 
    131         mAudioManager = (AudioManager) mApplication.getSystemService(Context.AUDIO_SERVICE);
    132         mTelephonyManager =
    133                 (TelephonyManager) mApplication.getSystemService(Context.TELEPHONY_SERVICE);
    134         mSubscriptionManager = (SubscriptionManager) mApplication.getSystemService(
    135                 Context.TELEPHONY_SUBSCRIPTION_SERVICE);
    136 
    137         registerForNotifications();
    138 
    139         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
    140         if (adapter != null) {
    141             adapter.getProfileProxy(mApplication.getApplicationContext(),
    142                     mBluetoothProfileServiceListener,
    143                     BluetoothProfile.HEADSET);
    144         }
    145 
    146         mSubscriptionManager.addOnSubscriptionsChangedListener(
    147                 new OnSubscriptionsChangedListener() {
    148                     @Override
    149                     public void onSubscriptionsChanged() {
    150                         updatePhoneStateListeners();
    151                     }
    152                 });
    153     }
    154 
    155     private void createSignalInfoToneGenerator() {
    156         // Instantiate the ToneGenerator for SignalInfo and CallWaiting
    157         // TODO: We probably don't need the mSignalInfoToneGenerator instance
    158         // around forever. Need to change it so as to create a ToneGenerator instance only
    159         // when a tone is being played and releases it after its done playing.
    160         if (mSignalInfoToneGenerator == null) {
    161             try {
    162                 mSignalInfoToneGenerator = new ToneGenerator(AudioManager.STREAM_VOICE_CALL,
    163                         TONE_RELATIVE_VOLUME_SIGNALINFO);
    164                 Log.d(LOG_TAG, "CallNotifier: mSignalInfoToneGenerator created when toneplay");
    165             } catch (RuntimeException e) {
    166                 Log.w(LOG_TAG, "CallNotifier: Exception caught while creating " +
    167                         "mSignalInfoToneGenerator: " + e);
    168                 mSignalInfoToneGenerator = null;
    169             }
    170         } else {
    171             Log.d(LOG_TAG, "mSignalInfoToneGenerator created already, hence skipping");
    172         }
    173     }
    174 
    175     /**
    176      * Register for call state notifications with the CallManager.
    177      */
    178     private void registerForNotifications() {
    179         mCM.registerForDisconnect(this, PHONE_DISCONNECT, null);
    180         mCM.registerForCdmaOtaStatusChange(this, EVENT_OTA_PROVISION_CHANGE, null);
    181         mCM.registerForDisplayInfo(this, PHONE_STATE_DISPLAYINFO, null);
    182         mCM.registerForSignalInfo(this, PHONE_STATE_SIGNALINFO, null);
    183         mCM.registerForInCallVoicePrivacyOn(this, PHONE_ENHANCED_VP_ON, null);
    184         mCM.registerForInCallVoicePrivacyOff(this, PHONE_ENHANCED_VP_OFF, null);
    185         mCM.registerForSuppServiceFailed(this, PHONE_SUPP_SERVICE_FAILED, null);
    186         mCM.registerForTtyModeReceived(this, PHONE_TTY_MODE_RECEIVED, null);
    187     }
    188 
    189     @Override
    190     public void handleMessage(Message msg) {
    191         if (DBG) {
    192             Log.d(LOG_TAG, "handleMessage(" + msg.what + ")");
    193         }
    194         switch (msg.what) {
    195             case PHONE_DISCONNECT:
    196                 if (DBG) log("DISCONNECT");
    197                 // Stop any signalInfo tone being played when a call gets ended, the rest of the
    198                 // disconnect functionality in onDisconnect() is handled in ConnectionService.
    199                 stopSignalInfoTone();
    200                 break;
    201 
    202             case PHONE_STATE_DISPLAYINFO:
    203                 if (DBG) log("Received PHONE_STATE_DISPLAYINFO event");
    204                 onDisplayInfo((AsyncResult) msg.obj);
    205                 break;
    206 
    207             case PHONE_STATE_SIGNALINFO:
    208                 if (DBG) log("Received PHONE_STATE_SIGNALINFO event");
    209                 onSignalInfo((AsyncResult) msg.obj);
    210                 break;
    211 
    212             case INTERNAL_SHOW_MESSAGE_NOTIFICATION_DONE:
    213                 if (DBG) log("Received Display Info notification done event ...");
    214                 PhoneDisplayMessage.dismissMessage();
    215                 break;
    216 
    217             case EVENT_OTA_PROVISION_CHANGE:
    218                 if (DBG) log("EVENT_OTA_PROVISION_CHANGE...");
    219                 mApplication.handleOtaspEvent(msg);
    220                 break;
    221 
    222             case PHONE_ENHANCED_VP_ON:
    223                 if (DBG) log("PHONE_ENHANCED_VP_ON...");
    224                 if (!mVoicePrivacyState) {
    225                     int toneToPlay = InCallTonePlayer.TONE_VOICE_PRIVACY;
    226                     new InCallTonePlayer(toneToPlay).start();
    227                     mVoicePrivacyState = true;
    228                 }
    229                 break;
    230 
    231             case PHONE_ENHANCED_VP_OFF:
    232                 if (DBG) log("PHONE_ENHANCED_VP_OFF...");
    233                 if (mVoicePrivacyState) {
    234                     int toneToPlay = InCallTonePlayer.TONE_VOICE_PRIVACY;
    235                     new InCallTonePlayer(toneToPlay).start();
    236                     mVoicePrivacyState = false;
    237                 }
    238                 break;
    239 
    240             case PHONE_SUPP_SERVICE_FAILED:
    241                 if (DBG) log("PHONE_SUPP_SERVICE_FAILED...");
    242                 onSuppServiceFailed((AsyncResult) msg.obj);
    243                 break;
    244 
    245             case PHONE_TTY_MODE_RECEIVED:
    246                 if (DBG) log("Received PHONE_TTY_MODE_RECEIVED event");
    247                 onTtyModeReceived((AsyncResult) msg.obj);
    248                 break;
    249 
    250             default:
    251                 // super.handleMessage(msg);
    252         }
    253     }
    254 
    255     void updateCallNotifierRegistrationsAfterRadioTechnologyChange() {
    256         if (DBG) Log.d(LOG_TAG, "updateCallNotifierRegistrationsAfterRadioTechnologyChange...");
    257 
    258         // Instantiate mSignalInfoToneGenerator
    259         createSignalInfoToneGenerator();
    260     }
    261 
    262     /**
    263      * Resets the audio mode and speaker state when a call ends.
    264      */
    265     private void resetAudioStateAfterDisconnect() {
    266         if (VDBG) log("resetAudioStateAfterDisconnect()...");
    267 
    268         if (mBluetoothHeadset != null) {
    269             mBluetoothHeadset.disconnectAudio();
    270         }
    271 
    272         // call turnOnSpeaker() with state=false and store=true even if speaker
    273         // is already off to reset user requested speaker state.
    274         PhoneUtils.turnOnSpeaker(mApplication, false, true);
    275 
    276         PhoneUtils.setAudioMode(mCM);
    277     }
    278 
    279     /**
    280      * Helper class to play tones through the earpiece (or speaker / BT)
    281      * during a call, using the ToneGenerator.
    282      *
    283      * To use, just instantiate a new InCallTonePlayer
    284      * (passing in the TONE_* constant for the tone you want)
    285      * and start() it.
    286      *
    287      * When we're done playing the tone, if the phone is idle at that
    288      * point, we'll reset the audio routing and speaker state.
    289      * (That means that for tones that get played *after* a call
    290      * disconnects, like "busy" or "congestion" or "call ended", you
    291      * should NOT call resetAudioStateAfterDisconnect() yourself.
    292      * Instead, just start the InCallTonePlayer, which will automatically
    293      * defer the resetAudioStateAfterDisconnect() call until the tone
    294      * finishes playing.)
    295      */
    296     private class InCallTonePlayer extends Thread {
    297         private int mToneId;
    298         private int mState;
    299         // The possible tones we can play.
    300         public static final int TONE_NONE = 0;
    301         public static final int TONE_CALL_WAITING = 1;
    302         public static final int TONE_BUSY = 2;
    303         public static final int TONE_CONGESTION = 3;
    304         public static final int TONE_CALL_ENDED = 4;
    305         public static final int TONE_VOICE_PRIVACY = 5;
    306         public static final int TONE_REORDER = 6;
    307         public static final int TONE_INTERCEPT = 7;
    308         public static final int TONE_CDMA_DROP = 8;
    309         public static final int TONE_OUT_OF_SERVICE = 9;
    310         public static final int TONE_REDIAL = 10;
    311         public static final int TONE_OTA_CALL_END = 11;
    312         public static final int TONE_UNOBTAINABLE_NUMBER = 13;
    313 
    314         // The tone volume relative to other sounds in the stream
    315         static final int TONE_RELATIVE_VOLUME_EMERGENCY = 100;
    316         static final int TONE_RELATIVE_VOLUME_HIPRI = 80;
    317         static final int TONE_RELATIVE_VOLUME_LOPRI = 50;
    318 
    319         // Buffer time (in msec) to add on to tone timeout value.
    320         // Needed mainly when the timeout value for a tone is the
    321         // exact duration of the tone itself.
    322         static final int TONE_TIMEOUT_BUFFER = 20;
    323 
    324         // The tone state
    325         static final int TONE_OFF = 0;
    326         static final int TONE_ON = 1;
    327         static final int TONE_STOPPED = 2;
    328 
    329         InCallTonePlayer(int toneId) {
    330             super();
    331             mToneId = toneId;
    332             mState = TONE_OFF;
    333         }
    334 
    335         @Override
    336         public void run() {
    337             log("InCallTonePlayer.run(toneId = " + mToneId + ")...");
    338 
    339             int toneType = 0;  // passed to ToneGenerator.startTone()
    340             int toneVolume;  // passed to the ToneGenerator constructor
    341             int toneLengthMillis;
    342             int phoneType = mCM.getFgPhone().getPhoneType();
    343 
    344             switch (mToneId) {
    345                 case TONE_CALL_WAITING:
    346                     toneType = ToneGenerator.TONE_SUP_CALL_WAITING;
    347                     toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
    348                     // Call waiting tone is stopped by stopTone() method
    349                     toneLengthMillis = Integer.MAX_VALUE - TONE_TIMEOUT_BUFFER;
    350                     break;
    351                 case TONE_BUSY:
    352                     if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
    353                         toneType = ToneGenerator.TONE_CDMA_NETWORK_BUSY_ONE_SHOT;
    354                         toneVolume = TONE_RELATIVE_VOLUME_LOPRI;
    355                         toneLengthMillis = 1000;
    356                     } else if (phoneType == PhoneConstants.PHONE_TYPE_GSM
    357                             || phoneType == PhoneConstants.PHONE_TYPE_SIP
    358                             || phoneType == PhoneConstants.PHONE_TYPE_IMS
    359                             || phoneType == PhoneConstants.PHONE_TYPE_THIRD_PARTY) {
    360                         toneType = ToneGenerator.TONE_SUP_BUSY;
    361                         toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
    362                         toneLengthMillis = 4000;
    363                     } else {
    364                         throw new IllegalStateException("Unexpected phone type: " + phoneType);
    365                     }
    366                     break;
    367                 case TONE_CONGESTION:
    368                     toneType = ToneGenerator.TONE_SUP_CONGESTION;
    369                     toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
    370                     toneLengthMillis = 4000;
    371                     break;
    372 
    373                 case TONE_CALL_ENDED:
    374                     toneType = ToneGenerator.TONE_PROP_PROMPT;
    375                     toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
    376                     toneLengthMillis = 200;
    377                     break;
    378                  case TONE_OTA_CALL_END:
    379                     if (mApplication.cdmaOtaConfigData.otaPlaySuccessFailureTone ==
    380                             OtaUtils.OTA_PLAY_SUCCESS_FAILURE_TONE_ON) {
    381                         toneType = ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD;
    382                         toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
    383                         toneLengthMillis = 750;
    384                     } else {
    385                         toneType = ToneGenerator.TONE_PROP_PROMPT;
    386                         toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
    387                         toneLengthMillis = 200;
    388                     }
    389                     break;
    390                 case TONE_VOICE_PRIVACY:
    391                     toneType = ToneGenerator.TONE_CDMA_ALERT_NETWORK_LITE;
    392                     toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
    393                     toneLengthMillis = 5000;
    394                     break;
    395                 case TONE_REORDER:
    396                     toneType = ToneGenerator.TONE_CDMA_REORDER;
    397                     toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
    398                     toneLengthMillis = 4000;
    399                     break;
    400                 case TONE_INTERCEPT:
    401                     toneType = ToneGenerator.TONE_CDMA_ABBR_INTERCEPT;
    402                     toneVolume = TONE_RELATIVE_VOLUME_LOPRI;
    403                     toneLengthMillis = 500;
    404                     break;
    405                 case TONE_CDMA_DROP:
    406                 case TONE_OUT_OF_SERVICE:
    407                     toneType = ToneGenerator.TONE_CDMA_CALLDROP_LITE;
    408                     toneVolume = TONE_RELATIVE_VOLUME_LOPRI;
    409                     toneLengthMillis = 375;
    410                     break;
    411                 case TONE_REDIAL:
    412                     toneType = ToneGenerator.TONE_CDMA_ALERT_AUTOREDIAL_LITE;
    413                     toneVolume = TONE_RELATIVE_VOLUME_LOPRI;
    414                     toneLengthMillis = 5000;
    415                     break;
    416                 case TONE_UNOBTAINABLE_NUMBER:
    417                     toneType = ToneGenerator.TONE_SUP_ERROR;
    418                     toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
    419                     toneLengthMillis = 4000;
    420                     break;
    421                 default:
    422                     throw new IllegalArgumentException("Bad toneId: " + mToneId);
    423             }
    424 
    425             // If the mToneGenerator creation fails, just continue without it.  It is
    426             // a local audio signal, and is not as important.
    427             ToneGenerator toneGenerator;
    428             try {
    429                 int stream;
    430                 if (mBluetoothHeadset != null) {
    431                     stream = mBluetoothHeadset.isAudioOn() ? AudioManager.STREAM_BLUETOOTH_SCO:
    432                         AudioManager.STREAM_VOICE_CALL;
    433                 } else {
    434                     stream = AudioManager.STREAM_VOICE_CALL;
    435                 }
    436                 toneGenerator = new ToneGenerator(stream, toneVolume);
    437                 // if (DBG) log("- created toneGenerator: " + toneGenerator);
    438             } catch (RuntimeException e) {
    439                 Log.w(LOG_TAG,
    440                       "InCallTonePlayer: Exception caught while creating ToneGenerator: " + e);
    441                 toneGenerator = null;
    442             }
    443 
    444             // Using the ToneGenerator (with the CALL_WAITING / BUSY /
    445             // CONGESTION tones at least), the ToneGenerator itself knows
    446             // the right pattern of tones to play; we do NOT need to
    447             // manually start/stop each individual tone, or manually
    448             // insert the correct delay between tones.  (We just start it
    449             // and let it run for however long we want the tone pattern to
    450             // continue.)
    451             //
    452             // TODO: When we stop the ToneGenerator in the middle of a
    453             // "tone pattern", it sounds bad if we cut if off while the
    454             // tone is actually playing.  Consider adding API to the
    455             // ToneGenerator to say "stop at the next silent part of the
    456             // pattern", or simply "play the pattern N times and then
    457             // stop."
    458             boolean needToStopTone = true;
    459             boolean okToPlayTone = false;
    460 
    461             if (toneGenerator != null) {
    462                 int ringerMode = mAudioManager.getRingerMode();
    463                 if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
    464                     if (toneType == ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD) {
    465                         if ((ringerMode != AudioManager.RINGER_MODE_SILENT) &&
    466                                 (ringerMode != AudioManager.RINGER_MODE_VIBRATE)) {
    467                             if (DBG) log("- InCallTonePlayer: start playing call tone=" + toneType);
    468                             okToPlayTone = true;
    469                             needToStopTone = false;
    470                         }
    471                     } else if ((toneType == ToneGenerator.TONE_CDMA_NETWORK_BUSY_ONE_SHOT) ||
    472                             (toneType == ToneGenerator.TONE_CDMA_REORDER) ||
    473                             (toneType == ToneGenerator.TONE_CDMA_ABBR_REORDER) ||
    474                             (toneType == ToneGenerator.TONE_CDMA_ABBR_INTERCEPT) ||
    475                             (toneType == ToneGenerator.TONE_CDMA_CALLDROP_LITE)) {
    476                         if (ringerMode != AudioManager.RINGER_MODE_SILENT) {
    477                             if (DBG) log("InCallTonePlayer:playing call fail tone:" + toneType);
    478                             okToPlayTone = true;
    479                             needToStopTone = false;
    480                         }
    481                     } else if ((toneType == ToneGenerator.TONE_CDMA_ALERT_AUTOREDIAL_LITE) ||
    482                                (toneType == ToneGenerator.TONE_CDMA_ALERT_NETWORK_LITE)) {
    483                         if ((ringerMode != AudioManager.RINGER_MODE_SILENT) &&
    484                                 (ringerMode != AudioManager.RINGER_MODE_VIBRATE)) {
    485                             if (DBG) log("InCallTonePlayer:playing tone for toneType=" + toneType);
    486                             okToPlayTone = true;
    487                             needToStopTone = false;
    488                         }
    489                     } else { // For the rest of the tones, always OK to play.
    490                         okToPlayTone = true;
    491                     }
    492                 } else {  // Not "CDMA"
    493                     okToPlayTone = true;
    494                 }
    495 
    496                 synchronized (this) {
    497                     if (okToPlayTone && mState != TONE_STOPPED) {
    498                         mState = TONE_ON;
    499                         toneGenerator.startTone(toneType);
    500                         try {
    501                             wait(toneLengthMillis + TONE_TIMEOUT_BUFFER);
    502                         } catch  (InterruptedException e) {
    503                             Log.w(LOG_TAG,
    504                                   "InCallTonePlayer stopped: " + e);
    505                         }
    506                         if (needToStopTone) {
    507                             toneGenerator.stopTone();
    508                         }
    509                     }
    510                     // if (DBG) log("- InCallTonePlayer: done playing.");
    511                     toneGenerator.release();
    512                     mState = TONE_OFF;
    513                 }
    514             }
    515 
    516             // Finally, do the same cleanup we otherwise would have done
    517             // in onDisconnect().
    518             //
    519             // (But watch out: do NOT do this if the phone is in use,
    520             // since some of our tones get played *during* a call (like
    521             // CALL_WAITING) and we definitely *don't*
    522             // want to reset the audio mode / speaker / bluetooth after
    523             // playing those!
    524             // This call is really here for use with tones that get played
    525             // *after* a call disconnects, like "busy" or "congestion" or
    526             // "call ended", where the phone has already become idle but
    527             // we need to defer the resetAudioStateAfterDisconnect() call
    528             // till the tone finishes playing.)
    529             if (mCM.getState() == PhoneConstants.State.IDLE) {
    530                 resetAudioStateAfterDisconnect();
    531             }
    532         }
    533     }
    534 
    535     /**
    536      * Displays a notification when the phone receives a DisplayInfo record.
    537      */
    538     private void onDisplayInfo(AsyncResult r) {
    539         // Extract the DisplayInfo String from the message
    540         CdmaDisplayInfoRec displayInfoRec = (CdmaDisplayInfoRec)(r.result);
    541 
    542         if (displayInfoRec != null) {
    543             String displayInfo = displayInfoRec.alpha;
    544             if (DBG) log("onDisplayInfo: displayInfo=" + displayInfo);
    545             PhoneDisplayMessage.displayNetworkMessage(mApplication, displayInfo);
    546 
    547             // start a timer that kills the dialog
    548             sendEmptyMessageDelayed(INTERNAL_SHOW_MESSAGE_NOTIFICATION_DONE,
    549                     SHOW_MESSAGE_NOTIFICATION_TIME);
    550         }
    551     }
    552 
    553     /**
    554      * Displays a notification when the phone receives a notice that a supplemental
    555      * service has failed.
    556      * TODO: This is a NOOP if it isn't for conferences or resuming call failures right now.
    557      */
    558     private void onSuppServiceFailed(AsyncResult r) {
    559         if (r.result != Phone.SuppService.CONFERENCE && r.result != Phone.SuppService.RESUME) {
    560             if (DBG) log("onSuppServiceFailed: not a merge or resume failure event");
    561             return;
    562         }
    563 
    564         String mergeFailedString = "";
    565         if (r.result == Phone.SuppService.CONFERENCE) {
    566             if (DBG) log("onSuppServiceFailed: displaying merge failure message");
    567             mergeFailedString = mApplication.getResources().getString(
    568                     R.string.incall_error_supp_service_conference);
    569         } else if (r.result == Phone.SuppService.RESUME) {
    570             if (DBG) log("onSuppServiceFailed: displaying merge failure message");
    571             mergeFailedString = mApplication.getResources().getString(
    572                     R.string.incall_error_supp_service_switch);
    573         } else if (r.result == Phone.SuppService.HOLD) {
    574             mergeFailedString = mApplication.getResources().getString(
    575                     R.string.incall_error_supp_service_hold);
    576         }
    577         PhoneDisplayMessage.displayErrorMessage(mApplication, mergeFailedString);
    578 
    579         // start a timer that kills the dialog
    580         sendEmptyMessageDelayed(INTERNAL_SHOW_MESSAGE_NOTIFICATION_DONE,
    581                 SHOW_MESSAGE_NOTIFICATION_TIME);
    582     }
    583 
    584     public void updatePhoneStateListeners() {
    585         List<SubscriptionInfo> subInfos = mSubscriptionManager.getActiveSubscriptionInfoList();
    586 
    587         // Unregister phone listeners for inactive subscriptions.
    588         Iterator<Integer> itr = mPhoneStateListeners.keySet().iterator();
    589         while (itr.hasNext()) {
    590             int subId = itr.next();
    591             if (subInfos == null || !containsSubId(subInfos, subId)) {
    592                 // Hide the outstanding notifications.
    593                 mApplication.notificationMgr.updateMwi(subId, false);
    594                 mApplication.notificationMgr.updateCfi(subId, false);
    595 
    596                 // Listening to LISTEN_NONE removes the listener.
    597                 mTelephonyManager.listen(
    598                         mPhoneStateListeners.get(subId), PhoneStateListener.LISTEN_NONE);
    599                 itr.remove();
    600             }
    601         }
    602 
    603         if (subInfos == null) {
    604             return;
    605         }
    606 
    607         // Register new phone listeners for active subscriptions.
    608         for (int i = 0; i < subInfos.size(); i++) {
    609             int subId = subInfos.get(i).getSubscriptionId();
    610             if (!mPhoneStateListeners.containsKey(subId)) {
    611                 CallNotifierPhoneStateListener listener = new CallNotifierPhoneStateListener(subId);
    612                 mTelephonyManager.listen(listener,
    613                         PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR
    614                         | PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR);
    615                 mPhoneStateListeners.put(subId, listener);
    616             }
    617         }
    618     }
    619 
    620     /**
    621      * @return {@code true} if the list contains SubscriptionInfo with the given subscription id.
    622      */
    623     private boolean containsSubId(List<SubscriptionInfo> subInfos, int subId) {
    624         if (subInfos == null) {
    625             return false;
    626         }
    627 
    628         for (int i = 0; i < subInfos.size(); i++) {
    629             if (subInfos.get(i).getSubscriptionId() == subId) {
    630                 return true;
    631             }
    632         }
    633         return false;
    634     }
    635 
    636     /**
    637      * Displays a notification when the phone receives a notice that TTY mode
    638      * has changed on remote end.
    639      */
    640     private void onTtyModeReceived(AsyncResult r) {
    641         if (DBG) log("TtyModeReceived: displaying notification message");
    642 
    643         int resId = 0;
    644         switch (((Integer)r.result).intValue()) {
    645             case TelecomManager.TTY_MODE_FULL:
    646                 resId = com.android.internal.R.string.peerTtyModeFull;
    647                 break;
    648             case TelecomManager.TTY_MODE_HCO:
    649                 resId = com.android.internal.R.string.peerTtyModeHco;
    650                 break;
    651             case TelecomManager.TTY_MODE_VCO:
    652                 resId = com.android.internal.R.string.peerTtyModeVco;
    653                 break;
    654             case TelecomManager.TTY_MODE_OFF:
    655                 resId = com.android.internal.R.string.peerTtyModeOff;
    656                 break;
    657             default:
    658                 Log.e(LOG_TAG, "Unsupported TTY mode: " + r.result);
    659                 break;
    660         }
    661         if (resId != 0) {
    662             PhoneDisplayMessage.displayNetworkMessage(mApplication,
    663                     mApplication.getResources().getString(resId));
    664 
    665             // start a timer that kills the dialog
    666             sendEmptyMessageDelayed(INTERNAL_SHOW_MESSAGE_NOTIFICATION_DONE,
    667                     SHOW_MESSAGE_NOTIFICATION_TIME);
    668         }
    669     }
    670 
    671     /**
    672      * Helper class to play SignalInfo tones using the ToneGenerator.
    673      *
    674      * To use, just instantiate a new SignalInfoTonePlayer
    675      * (passing in the ToneID constant for the tone you want)
    676      * and start() it.
    677      */
    678     private class SignalInfoTonePlayer extends Thread {
    679         private int mToneId;
    680 
    681         SignalInfoTonePlayer(int toneId) {
    682             super();
    683             mToneId = toneId;
    684         }
    685 
    686         @Override
    687         public void run() {
    688             log("SignalInfoTonePlayer.run(toneId = " + mToneId + ")...");
    689             createSignalInfoToneGenerator();
    690             if (mSignalInfoToneGenerator != null) {
    691                 //First stop any ongoing SignalInfo tone
    692                 mSignalInfoToneGenerator.stopTone();
    693 
    694                 //Start playing the new tone if its a valid tone
    695                 mSignalInfoToneGenerator.startTone(mToneId);
    696             }
    697         }
    698     }
    699 
    700     /**
    701      * Plays a tone when the phone receives a SignalInfo record.
    702      */
    703     private void onSignalInfo(AsyncResult r) {
    704         // Signal Info are totally ignored on non-voice-capable devices.
    705         if (!PhoneGlobals.sVoiceCapable) {
    706             Log.w(LOG_TAG, "Got onSignalInfo() on non-voice-capable device! Ignoring...");
    707             return;
    708         }
    709 
    710         if (PhoneUtils.isRealIncomingCall(mCM.getFirstActiveRingingCall().getState())) {
    711             // Do not start any new SignalInfo tone when Call state is INCOMING
    712             // and stop any previous SignalInfo tone which is being played
    713             stopSignalInfoTone();
    714         } else {
    715             // Extract the SignalInfo String from the message
    716             CdmaSignalInfoRec signalInfoRec = (CdmaSignalInfoRec)(r.result);
    717             // Only proceed if a Signal info is present.
    718             if (signalInfoRec != null) {
    719                 boolean isPresent = signalInfoRec.isPresent;
    720                 if (DBG) log("onSignalInfo: isPresent=" + isPresent);
    721                 if (isPresent) {// if tone is valid
    722                     int uSignalType = signalInfoRec.signalType;
    723                     int uAlertPitch = signalInfoRec.alertPitch;
    724                     int uSignal = signalInfoRec.signal;
    725 
    726                     if (DBG) log("onSignalInfo: uSignalType=" + uSignalType + ", uAlertPitch=" +
    727                             uAlertPitch + ", uSignal=" + uSignal);
    728                     //Map the Signal to a ToneGenerator ToneID only if Signal info is present
    729                     int toneID = SignalToneUtil.getAudioToneFromSignalInfo
    730                             (uSignalType, uAlertPitch, uSignal);
    731 
    732                     //Create the SignalInfo tone player and pass the ToneID
    733                     new SignalInfoTonePlayer(toneID).start();
    734                 }
    735             }
    736         }
    737     }
    738 
    739     /**
    740      * Stops a SignalInfo tone in the following condition
    741      * 1 - On receiving a New Ringing Call
    742      * 2 - On disconnecting a call
    743      * 3 - On answering a Call Waiting Call
    744      */
    745     /* package */ void stopSignalInfoTone() {
    746         if (DBG) log("stopSignalInfoTone: Stopping SignalInfo tone player");
    747         new SignalInfoTonePlayer(ToneGenerator.TONE_CDMA_SIGNAL_OFF).start();
    748     }
    749 
    750     private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
    751            new BluetoothProfile.ServiceListener() {
    752                 public void onServiceConnected(int profile, BluetoothProfile proxy) {
    753                     mBluetoothHeadset = (BluetoothHeadset) proxy;
    754                     if (VDBG) log("- Got BluetoothHeadset: " + mBluetoothHeadset);
    755                 }
    756 
    757                 public void onServiceDisconnected(int profile) {
    758                     mBluetoothHeadset = null;
    759                 }
    760             };
    761 
    762     private class CallNotifierPhoneStateListener extends PhoneStateListener {
    763         public CallNotifierPhoneStateListener(int subId) {
    764             super(subId);
    765         }
    766 
    767         @Override
    768         public void onMessageWaitingIndicatorChanged(boolean visible) {
    769             if (VDBG) log("onMessageWaitingIndicatorChanged(): " + this.mSubId + " " + visible);
    770             mApplication.notificationMgr.updateMwi(this.mSubId, visible);
    771         }
    772 
    773         @Override
    774         public void onCallForwardingIndicatorChanged(boolean visible) {
    775             if (VDBG) log("onCallForwardingIndicatorChanged(): " + this.mSubId + " " + visible);
    776             mApplication.notificationMgr.updateCfi(this.mSubId, visible);
    777         }
    778     };
    779 
    780     private void log(String msg) {
    781         Log.d(LOG_TAG, msg);
    782     }
    783 }
    784