Home | History | Annotate | Download | only in telephony
      1 /**
      2  * Copyright (C) 2014 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.services.telephony;
     18 
     19 import android.content.Context;
     20 import android.media.ToneGenerator;
     21 import android.telecom.DisconnectCause;
     22 
     23 import com.android.phone.PhoneGlobals;
     24 import com.android.phone.common.R;
     25 import com.android.phone.ImsUtil;
     26 
     27 public class DisconnectCauseUtil {
     28 
     29    /**
     30     * Converts from a disconnect code in {@link android.telephony.DisconnectCause} into a more
     31     * generic {@link android.telecom.DisconnectCause} object, possibly populated with a localized
     32     * message and tone.
     33     *
     34     * @param telephonyDisconnectCause The code for the reason for the disconnect.
     35     */
     36     public static DisconnectCause toTelecomDisconnectCause(int telephonyDisconnectCause) {
     37         return toTelecomDisconnectCause(telephonyDisconnectCause, null /* reason */);
     38     }
     39 
     40    /**
     41     * Converts from a disconnect code in {@link android.telephony.DisconnectCause} into a more
     42     * generic {@link android.telecom.DisconnectCause}.object, possibly populated with a localized
     43     * message and tone.
     44     *
     45     * @param telephonyDisconnectCause The code for the reason for the disconnect.
     46     * @param reason Description of the reason for the disconnect, not intended for the user to see..
     47     */
     48     public static DisconnectCause toTelecomDisconnectCause(
     49             int telephonyDisconnectCause, String reason) {
     50         Context context = PhoneGlobals.getInstance();
     51         return new DisconnectCause(
     52                 toTelecomDisconnectCauseCode(telephonyDisconnectCause),
     53                 toTelecomDisconnectCauseLabel(context, telephonyDisconnectCause),
     54                 toTelecomDisconnectCauseDescription(context, telephonyDisconnectCause),
     55                 toTelecomDisconnectReason(context,telephonyDisconnectCause, reason),
     56                 toTelecomDisconnectCauseTone(telephonyDisconnectCause));
     57     }
     58 
     59     /**
     60      * Convert the {@link android.telephony.DisconnectCause} disconnect code into a
     61      * {@link android.telecom.DisconnectCause} disconnect code.
     62      * @return The disconnect code as defined in {@link android.telecom.DisconnectCause}.
     63      */
     64     private static int toTelecomDisconnectCauseCode(int telephonyDisconnectCause) {
     65         switch (telephonyDisconnectCause) {
     66             case android.telephony.DisconnectCause.LOCAL:
     67                 return DisconnectCause.LOCAL;
     68 
     69             case android.telephony.DisconnectCause.NORMAL:
     70                 return DisconnectCause.REMOTE;
     71 
     72             case android.telephony.DisconnectCause.OUTGOING_CANCELED:
     73                 return DisconnectCause.CANCELED;
     74 
     75             case android.telephony.DisconnectCause.INCOMING_MISSED:
     76                 return DisconnectCause.MISSED;
     77 
     78             case android.telephony.DisconnectCause.INCOMING_REJECTED:
     79                 return DisconnectCause.REJECTED;
     80 
     81             case android.telephony.DisconnectCause.BUSY:
     82                 return DisconnectCause.BUSY;
     83 
     84             case android.telephony.DisconnectCause.CALL_BARRED:
     85             case android.telephony.DisconnectCause.CDMA_ACCESS_BLOCKED:
     86             case android.telephony.DisconnectCause.CDMA_NOT_EMERGENCY:
     87             case android.telephony.DisconnectCause.CS_RESTRICTED:
     88             case android.telephony.DisconnectCause.CS_RESTRICTED_EMERGENCY:
     89             case android.telephony.DisconnectCause.CS_RESTRICTED_NORMAL:
     90             case android.telephony.DisconnectCause.EMERGENCY_ONLY:
     91             case android.telephony.DisconnectCause.FDN_BLOCKED:
     92             case android.telephony.DisconnectCause.LIMIT_EXCEEDED:
     93             case android.telephony.DisconnectCause.VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED:
     94                 return DisconnectCause.RESTRICTED;
     95 
     96             case android.telephony.DisconnectCause.CDMA_ACCESS_FAILURE:
     97             case android.telephony.DisconnectCause.CDMA_ALREADY_ACTIVATED:
     98             case android.telephony.DisconnectCause.CDMA_CALL_LOST:
     99             case android.telephony.DisconnectCause.CDMA_DROP:
    100             case android.telephony.DisconnectCause.CDMA_INTERCEPT:
    101             case android.telephony.DisconnectCause.CDMA_LOCKED_UNTIL_POWER_CYCLE:
    102             case android.telephony.DisconnectCause.CDMA_PREEMPTED:
    103             case android.telephony.DisconnectCause.CDMA_REORDER:
    104             case android.telephony.DisconnectCause.CDMA_RETRY_ORDER:
    105             case android.telephony.DisconnectCause.CDMA_SO_REJECT:
    106             case android.telephony.DisconnectCause.CONGESTION:
    107             case android.telephony.DisconnectCause.ICC_ERROR:
    108             case android.telephony.DisconnectCause.INVALID_CREDENTIALS:
    109             case android.telephony.DisconnectCause.INVALID_NUMBER:
    110             case android.telephony.DisconnectCause.LOST_SIGNAL:
    111             case android.telephony.DisconnectCause.NO_PHONE_NUMBER_SUPPLIED:
    112             case android.telephony.DisconnectCause.NUMBER_UNREACHABLE:
    113             case android.telephony.DisconnectCause.OUTGOING_FAILURE:
    114             case android.telephony.DisconnectCause.OUT_OF_NETWORK:
    115             case android.telephony.DisconnectCause.OUT_OF_SERVICE:
    116             case android.telephony.DisconnectCause.POWER_OFF:
    117             case android.telephony.DisconnectCause.SERVER_ERROR:
    118             case android.telephony.DisconnectCause.SERVER_UNREACHABLE:
    119             case android.telephony.DisconnectCause.TIMED_OUT:
    120             case android.telephony.DisconnectCause.UNOBTAINABLE_NUMBER:
    121             case android.telephony.DisconnectCause.VOICEMAIL_NUMBER_MISSING:
    122             case android.telephony.DisconnectCause.DIAL_MODIFIED_TO_USSD:
    123             case android.telephony.DisconnectCause.DIAL_MODIFIED_TO_SS:
    124             case android.telephony.DisconnectCause.DIAL_MODIFIED_TO_DIAL:
    125             case android.telephony.DisconnectCause.ERROR_UNSPECIFIED:
    126             case android.telephony.DisconnectCause.MAXIMUM_NUMBER_OF_CALLS_REACHED:
    127             case android.telephony.DisconnectCause.DATA_DISABLED:
    128             case android.telephony.DisconnectCause.DATA_LIMIT_REACHED:
    129             case android.telephony.DisconnectCause.DIALED_ON_WRONG_SLOT:
    130             case android.telephony.DisconnectCause.DIALED_CALL_FORWARDING_WHILE_ROAMING:
    131             case android.telephony.DisconnectCause.IMEI_NOT_ACCEPTED:
    132             case android.telephony.DisconnectCause.WIFI_LOST:
    133                 return DisconnectCause.ERROR;
    134 
    135             case android.telephony.DisconnectCause.DIALED_MMI:
    136             case android.telephony.DisconnectCause.EXITED_ECM:
    137             case android.telephony.DisconnectCause.MMI:
    138             case android.telephony.DisconnectCause.IMS_MERGED_SUCCESSFULLY:
    139                 return DisconnectCause.OTHER;
    140 
    141             case android.telephony.DisconnectCause.NOT_VALID:
    142             case android.telephony.DisconnectCause.NOT_DISCONNECTED:
    143                 return DisconnectCause.UNKNOWN;
    144 
    145             case android.telephony.DisconnectCause.CALL_PULLED:
    146                 return DisconnectCause.CALL_PULLED;
    147 
    148             case android.telephony.DisconnectCause.ANSWERED_ELSEWHERE:
    149                 return DisconnectCause.ANSWERED_ELSEWHERE;
    150 
    151             default:
    152                 Log.w("DisconnectCauseUtil.toTelecomDisconnectCauseCode",
    153                         "Unrecognized Telephony DisconnectCause "
    154                         + telephonyDisconnectCause);
    155                 return DisconnectCause.UNKNOWN;
    156         }
    157     }
    158 
    159     /**
    160      * Returns a label for to the disconnect cause to be shown to the user.
    161      */
    162     private static CharSequence toTelecomDisconnectCauseLabel(
    163             Context context, int telephonyDisconnectCause) {
    164         if (context == null ) {
    165             return "";
    166         }
    167 
    168         Integer resourceId = null;
    169         switch (telephonyDisconnectCause) {
    170             case android.telephony.DisconnectCause.BUSY:
    171                 resourceId = R.string.callFailed_userBusy;
    172                 break;
    173 
    174             case android.telephony.DisconnectCause.CONGESTION:
    175                 resourceId = R.string.callFailed_congestion;
    176                 break;
    177 
    178             case android.telephony.DisconnectCause.TIMED_OUT:
    179                 resourceId = R.string.callFailed_timedOut;
    180                 break;
    181 
    182             case android.telephony.DisconnectCause.SERVER_UNREACHABLE:
    183                 resourceId = R.string.callFailed_server_unreachable;
    184                 break;
    185 
    186             case android.telephony.DisconnectCause.NUMBER_UNREACHABLE:
    187                 resourceId = R.string.callFailed_number_unreachable;
    188                 break;
    189 
    190             case android.telephony.DisconnectCause.INVALID_CREDENTIALS:
    191                 resourceId = R.string.callFailed_invalid_credentials;
    192                 break;
    193 
    194             case android.telephony.DisconnectCause.SERVER_ERROR:
    195                 resourceId = R.string.callFailed_server_error;
    196                 break;
    197 
    198             case android.telephony.DisconnectCause.OUT_OF_NETWORK:
    199                 resourceId = R.string.callFailed_out_of_network;
    200                 break;
    201 
    202             case android.telephony.DisconnectCause.LOST_SIGNAL:
    203             case android.telephony.DisconnectCause.CDMA_DROP:
    204                 resourceId = R.string.callFailed_noSignal;
    205                 break;
    206 
    207             case android.telephony.DisconnectCause.LIMIT_EXCEEDED:
    208                 resourceId = R.string.callFailed_limitExceeded;
    209                 break;
    210 
    211             case android.telephony.DisconnectCause.POWER_OFF:
    212                 resourceId = R.string.callFailed_powerOff;
    213                 break;
    214 
    215             case android.telephony.DisconnectCause.ICC_ERROR:
    216                 resourceId = R.string.callFailed_simError;
    217                 break;
    218 
    219             case android.telephony.DisconnectCause.OUT_OF_SERVICE:
    220                 resourceId = R.string.callFailed_outOfService;
    221                 break;
    222 
    223             case android.telephony.DisconnectCause.INVALID_NUMBER:
    224             case android.telephony.DisconnectCause.UNOBTAINABLE_NUMBER:
    225                 resourceId = R.string.callFailed_unobtainable_number;
    226                 break;
    227 
    228             case android.telephony.DisconnectCause.CALL_PULLED:
    229                 resourceId = R.string.callEnded_pulled;
    230                 break;
    231 
    232             case android.telephony.DisconnectCause.MAXIMUM_NUMBER_OF_CALLS_REACHED:
    233                 resourceId = R.string.callFailed_maximum_reached;
    234                 break;
    235 
    236             case android.telephony.DisconnectCause.DATA_DISABLED:
    237                 resourceId = R.string.callFailed_data_disabled;
    238                 break;
    239 
    240             case android.telephony.DisconnectCause.DATA_LIMIT_REACHED:
    241                 resourceId = R.string.callFailed_data_limit_reached;
    242                 break;
    243 
    244             default:
    245                 break;
    246         }
    247         return resourceId == null ? "" : context.getResources().getString(resourceId);
    248     }
    249 
    250     /**
    251      * Returns a description of the disconnect cause to be shown to the user.
    252      */
    253     private static CharSequence toTelecomDisconnectCauseDescription(
    254             Context context, int telephonyDisconnectCause) {
    255         if (context == null ) {
    256             return "";
    257         }
    258 
    259         Integer resourceId = null;
    260         switch (telephonyDisconnectCause) {
    261             case android.telephony.DisconnectCause.CALL_BARRED:
    262                 resourceId = R.string.callFailed_cb_enabled;
    263                 break;
    264 
    265             case android.telephony.DisconnectCause.CDMA_ALREADY_ACTIVATED:
    266                 resourceId = R.string.callFailed_cdma_activation;
    267                 break;
    268 
    269             case android.telephony.DisconnectCause.FDN_BLOCKED:
    270                 resourceId = R.string.callFailed_fdn_only;
    271                 break;
    272 
    273             case android.telephony.DisconnectCause.CS_RESTRICTED:
    274                 resourceId = R.string.callFailed_dsac_restricted;
    275                 break;
    276 
    277             case android.telephony.DisconnectCause.CS_RESTRICTED_EMERGENCY:
    278                 resourceId = R.string.callFailed_dsac_restricted_emergency;
    279                 break;
    280 
    281             case android.telephony.DisconnectCause.CS_RESTRICTED_NORMAL:
    282                 resourceId = R.string.callFailed_dsac_restricted_normal;
    283                 break;
    284 
    285             case android.telephony.DisconnectCause.DIAL_MODIFIED_TO_USSD:
    286                 resourceId = R.string.callFailed_dialToUssd;
    287                 break;
    288 
    289             case android.telephony.DisconnectCause.DIAL_MODIFIED_TO_SS:
    290                 resourceId = R.string.callFailed_dialToSs;
    291                 break;
    292 
    293             case android.telephony.DisconnectCause.DIAL_MODIFIED_TO_DIAL:
    294                 resourceId = R.string.callFailed_dialToDial;
    295                 break;
    296 
    297             case android.telephony.DisconnectCause.OUTGOING_FAILURE:
    298                 // We couldn't successfully place the call; there was some
    299                 // failure in the telephony layer.
    300                 // TODO: Need UI spec for this failure case; for now just
    301                 // show a generic error.
    302                 resourceId = R.string.incall_error_call_failed;
    303                 break;
    304 
    305             case android.telephony.DisconnectCause.POWER_OFF:
    306                 // Radio is explictly powered off because the device is in airplane mode.
    307 
    308                 // TODO: Offer the option to turn the radio on, and automatically retry the call
    309                 // once network registration is complete.
    310 
    311                 if (ImsUtil.shouldPromoteWfc(context)) {
    312                     resourceId = R.string.incall_error_promote_wfc;
    313                 } else if (ImsUtil.isWfcModeWifiOnly(context)) {
    314                     resourceId = R.string.incall_error_wfc_only_no_wireless_network;
    315                 } else if (ImsUtil.isWfcEnabled(context)) {
    316                     resourceId = R.string.incall_error_power_off_wfc;
    317                 } else {
    318                     resourceId = R.string.incall_error_power_off;
    319                 }
    320                 break;
    321 
    322             case android.telephony.DisconnectCause.CDMA_NOT_EMERGENCY:
    323                 // Only emergency calls are allowed when in emergency callback mode.
    324                 resourceId = R.string.incall_error_ecm_emergency_only;
    325                 break;
    326 
    327             case android.telephony.DisconnectCause.EMERGENCY_ONLY:
    328                 // Only emergency numbers are allowed, but we tried to dial
    329                 // a non-emergency number.
    330                 resourceId = R.string.incall_error_emergency_only;
    331                 break;
    332 
    333             case android.telephony.DisconnectCause.OUT_OF_SERVICE:
    334                 // No network connection.
    335                 if (ImsUtil.shouldPromoteWfc(context)) {
    336                     resourceId = R.string.incall_error_promote_wfc;
    337                 } else if (ImsUtil.isWfcModeWifiOnly(context)) {
    338                     resourceId = R.string.incall_error_wfc_only_no_wireless_network;
    339                 } else if (ImsUtil.isWfcEnabled(context)) {
    340                     resourceId = R.string.incall_error_out_of_service_wfc;
    341                 } else {
    342                     resourceId = R.string.incall_error_out_of_service;
    343                 }
    344                 break;
    345 
    346             case android.telephony.DisconnectCause.NO_PHONE_NUMBER_SUPPLIED:
    347                 // The supplied Intent didn't contain a valid phone number.
    348                 // (This is rare and should only ever happen with broken
    349                 // 3rd-party apps.) For now just show a generic error.
    350                 resourceId = R.string.incall_error_no_phone_number_supplied;
    351                 break;
    352 
    353             case android.telephony.DisconnectCause.VOICEMAIL_NUMBER_MISSING:
    354                 // TODO: Need to bring up the "Missing Voicemail Number" dialog, which
    355                 // will ultimately take us to the Call Settings.
    356                 resourceId = R.string.incall_error_missing_voicemail_number;
    357                 break;
    358 
    359             case android.telephony.DisconnectCause.VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED:
    360                 resourceId = R.string.callFailed_video_call_tty_enabled;
    361                 break;
    362 
    363             case android.telephony.DisconnectCause.CALL_PULLED:
    364                 resourceId = R.string.callEnded_pulled;
    365                 break;
    366 
    367             case android.telephony.DisconnectCause.MAXIMUM_NUMBER_OF_CALLS_REACHED:
    368                 resourceId = R.string.callFailed_maximum_reached;
    369 
    370             case android.telephony.DisconnectCause.OUTGOING_CANCELED:
    371                 // We don't want to show any dialog for the canceled case since the call was
    372                 // either canceled by the user explicitly (end-call button pushed immediately)
    373                 // or some other app canceled the call and immediately issued a new CALL to
    374                 // replace it.
    375                 break;
    376 
    377             case android.telephony.DisconnectCause.DATA_DISABLED:
    378                 resourceId = R.string.callFailed_data_disabled;
    379                 break;
    380 
    381             case android.telephony.DisconnectCause.DATA_LIMIT_REACHED:
    382                 resourceId = R.string.callFailed_data_limit_reached_description;
    383                 break;
    384             case android.telephony.DisconnectCause.DIALED_CALL_FORWARDING_WHILE_ROAMING:
    385                 resourceId = com.android.internal.R.string.mmiErrorWhileRoaming;
    386                 break;
    387 
    388             case android.telephony.DisconnectCause.IMEI_NOT_ACCEPTED:
    389                 resourceId = R.string.callFailed_imei_not_accepted;
    390                 break;
    391 
    392             case android.telephony.DisconnectCause.WIFI_LOST:
    393                 resourceId = R.string.callFailed_wifi_lost;
    394                 break;
    395 
    396             default:
    397                 break;
    398         }
    399         return resourceId == null ? "" : context.getResources().getString(resourceId);
    400     }
    401 
    402     /**
    403      * Maps the telephony {@link android.telephony.DisconnectCause} into a reason string which is
    404      * returned in the Telecom {@link DisconnectCause#getReason()}.
    405      *
    406      * @param context The current context.
    407      * @param telephonyDisconnectCause The {@link android.telephony.DisconnectCause} code.
    408      * @param reason A reason provided by the caller; only used if a more specific reason cannot
    409      *               be determined here.
    410      * @return The disconnect reason.
    411      */
    412     private static String toTelecomDisconnectReason(Context context, int telephonyDisconnectCause,
    413             String reason) {
    414 
    415         if (context == null) {
    416             return "";
    417         }
    418 
    419         switch (telephonyDisconnectCause) {
    420             case android.telephony.DisconnectCause.POWER_OFF:
    421                 // Airplane mode (radio off)
    422                 // intentional fall-through
    423             case android.telephony.DisconnectCause.OUT_OF_SERVICE:
    424                 // No network connection.
    425                 if (ImsUtil.shouldPromoteWfc(context)) {
    426                     return android.telecom.DisconnectCause.REASON_WIFI_ON_BUT_WFC_OFF;
    427                 }
    428                 break;
    429         }
    430 
    431         // If no specific code-mapping found, then fall back to using the reason.
    432         String causeAsString = android.telephony.DisconnectCause.toString(telephonyDisconnectCause);
    433         if (reason == null) {
    434             return causeAsString;
    435         } else {
    436             return reason + ", " + causeAsString;
    437         }
    438     }
    439 
    440     /**
    441      * Returns the tone to play for the disconnect cause, or UNKNOWN if none should be played.
    442      */
    443     private static int toTelecomDisconnectCauseTone(int telephonyDisconnectCause) {
    444         switch (telephonyDisconnectCause) {
    445             case android.telephony.DisconnectCause.BUSY:
    446                 return ToneGenerator.TONE_SUP_BUSY;
    447 
    448             case android.telephony.DisconnectCause.CONGESTION:
    449                 return ToneGenerator.TONE_SUP_CONGESTION;
    450 
    451             case android.telephony.DisconnectCause.CDMA_REORDER:
    452                 return ToneGenerator.TONE_CDMA_REORDER;
    453 
    454             case android.telephony.DisconnectCause.CDMA_INTERCEPT:
    455                 return ToneGenerator.TONE_CDMA_ABBR_INTERCEPT;
    456 
    457             case android.telephony.DisconnectCause.CDMA_DROP:
    458             case android.telephony.DisconnectCause.OUT_OF_SERVICE:
    459                 return ToneGenerator.TONE_CDMA_CALLDROP_LITE;
    460 
    461             case android.telephony.DisconnectCause.UNOBTAINABLE_NUMBER:
    462                 return ToneGenerator.TONE_SUP_ERROR;
    463 
    464             case android.telephony.DisconnectCause.ERROR_UNSPECIFIED:
    465             case android.telephony.DisconnectCause.LOCAL:
    466             case android.telephony.DisconnectCause.NORMAL:
    467             case android.telephony.DisconnectCause.VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED:
    468                 return ToneGenerator.TONE_PROP_PROMPT;
    469 
    470             case android.telephony.DisconnectCause.IMS_MERGED_SUCCESSFULLY:
    471                 // Do not play any tones if disconnected because of a successful merge.
    472             default:
    473                 return ToneGenerator.TONE_UNKNOWN;
    474         }
    475     }
    476 }
    477