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