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 android.telecom; 18 19 import android.annotation.Nullable; 20 import android.media.ToneGenerator; 21 import android.os.Parcel; 22 import android.os.Parcelable; 23 import android.telephony.Annotation; 24 import android.telephony.PreciseDisconnectCause; 25 import android.telephony.ims.ImsReasonInfo; 26 import android.text.TextUtils; 27 28 import java.util.Objects; 29 30 /** 31 * Describes the cause of a disconnected call. This always includes a code describing the generic 32 * cause of the disconnect. Optionally, it may include a label and/or description to display to the 33 * user. It is the responsibility of the {@link ConnectionService} to provide localized versions of 34 * the label and description. It also may contain a reason for the disconnect, which is intended for 35 * logging and not for display to the user. 36 */ 37 public final class DisconnectCause implements Parcelable { 38 39 /** Disconnected because of an unknown or unspecified reason. */ 40 public static final int UNKNOWN = TelecomProtoEnums.UNKNOWN; // = 0 41 /** Disconnected because there was an error, such as a problem with the network. */ 42 public static final int ERROR = TelecomProtoEnums.ERROR; // = 1 43 /** Disconnected because of a local user-initiated action, such as hanging up. */ 44 public static final int LOCAL = TelecomProtoEnums.LOCAL; // = 2 45 /** 46 * Disconnected because the remote party hung up an ongoing call, or because an outgoing call 47 * was not answered by the remote party. 48 */ 49 public static final int REMOTE = TelecomProtoEnums.REMOTE; // = 3 50 /** Disconnected because it has been canceled. */ 51 public static final int CANCELED = TelecomProtoEnums.CANCELED; // = 4 52 /** Disconnected because there was no response to an incoming call. */ 53 public static final int MISSED = TelecomProtoEnums.MISSED; // = 5 54 /** Disconnected because the user rejected an incoming call. */ 55 public static final int REJECTED = TelecomProtoEnums.REJECTED; // = 6 56 /** Disconnected because the other party was busy. */ 57 public static final int BUSY = TelecomProtoEnums.BUSY; // = 7 58 /** 59 * Disconnected because of a restriction on placing the call, such as dialing in airplane 60 * mode. 61 */ 62 public static final int RESTRICTED = TelecomProtoEnums.RESTRICTED; // = 8 63 /** Disconnected for reason not described by other disconnect codes. */ 64 public static final int OTHER = TelecomProtoEnums.OTHER; // = 9 65 /** 66 * Disconnected because the connection manager did not support the call. The call will be tried 67 * again without a connection manager. See {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER}. 68 */ 69 public static final int CONNECTION_MANAGER_NOT_SUPPORTED = 70 TelecomProtoEnums.CONNECTION_MANAGER_NOT_SUPPORTED; // = 10 71 72 /** 73 * Disconnected because the user did not locally answer the incoming call, but it was answered 74 * on another device where the call was ringing. 75 */ 76 public static final int ANSWERED_ELSEWHERE = TelecomProtoEnums.ANSWERED_ELSEWHERE; // = 11 77 78 /** 79 * Disconnected because the call was pulled from the current device to another device. 80 */ 81 public static final int CALL_PULLED = TelecomProtoEnums.CALL_PULLED; // = 12 82 83 /** 84 * Reason code (returned via {@link #getReason()}) which indicates that a call could not be 85 * completed because the cellular radio is off or out of service, the device is connected to 86 * a wifi network, but the user has not enabled wifi calling. 87 */ 88 public static final String REASON_WIFI_ON_BUT_WFC_OFF = "REASON_WIFI_ON_BUT_WFC_OFF"; 89 90 /** 91 * Reason code (returned via {@link #getReason()}), which indicates that the call was 92 * disconnected because IMS access is blocked. 93 */ 94 public static final String REASON_IMS_ACCESS_BLOCKED = "REASON_IMS_ACCESS_BLOCKED"; 95 96 /** 97 * Reason code (returned via {@link #getReason()}), which indicates that the connection service 98 * is setting the call's state to {@link Call#STATE_DISCONNECTED} because it is internally 99 * changing the representation of an IMS conference call to simulate a single-party call. 100 * 101 * This reason code is only used for communication between a {@link ConnectionService} and 102 * Telecom and should not be surfaced to the user. 103 */ 104 public static final String REASON_EMULATING_SINGLE_CALL = "EMULATING_SINGLE_CALL"; 105 106 /** 107 * This reason is set when a call is ended in order to place an emergency call when a 108 * {@link PhoneAccount} doesn't support holding an ongoing call to place an emergency call. This 109 * reason string should only be associated with the {@link #LOCAL} disconnect code returned from 110 * {@link #getCode()}. 111 */ 112 public static final String REASON_EMERGENCY_CALL_PLACED = "REASON_EMERGENCY_CALL_PLACED"; 113 114 private int mDisconnectCode; 115 private CharSequence mDisconnectLabel; 116 private CharSequence mDisconnectDescription; 117 private String mDisconnectReason; 118 private int mToneToPlay; 119 private int mTelephonyDisconnectCause; 120 private int mTelephonyPreciseDisconnectCause; 121 private ImsReasonInfo mImsReasonInfo; 122 123 /** 124 * Creates a new DisconnectCause. 125 * 126 * @param code The code for the disconnect cause. 127 */ DisconnectCause(int code)128 public DisconnectCause(int code) { 129 this(code, null, null, null, ToneGenerator.TONE_UNKNOWN); 130 } 131 132 /** 133 * Creates a new DisconnectCause. 134 * 135 * @param code The code for the disconnect cause. 136 * @param reason The reason for the disconnect. 137 */ DisconnectCause(int code, String reason)138 public DisconnectCause(int code, String reason) { 139 this(code, null, null, reason, ToneGenerator.TONE_UNKNOWN); 140 } 141 142 /** 143 * Creates a new DisconnectCause. 144 * 145 * @param code The code for the disconnect cause. 146 * @param label The localized label to show to the user to explain the disconnect. 147 * @param description The localized description to show to the user to explain the disconnect. 148 * @param reason The reason for the disconnect. 149 */ DisconnectCause(int code, CharSequence label, CharSequence description, String reason)150 public DisconnectCause(int code, CharSequence label, CharSequence description, String reason) { 151 this(code, label, description, reason, ToneGenerator.TONE_UNKNOWN); 152 } 153 154 /** 155 * Creates a new DisconnectCause. 156 * 157 * @param code The code for the disconnect cause. 158 * @param label The localized label to show to the user to explain the disconnect. 159 * @param description The localized description to show to the user to explain the disconnect. 160 * @param reason The reason for the disconnect. 161 * @param toneToPlay The tone to play on disconnect, as defined in {@link ToneGenerator}. 162 */ DisconnectCause(int code, CharSequence label, CharSequence description, String reason, int toneToPlay)163 public DisconnectCause(int code, CharSequence label, CharSequence description, String reason, 164 int toneToPlay) { 165 this(code, label, description, reason, toneToPlay, 166 android.telephony.DisconnectCause.ERROR_UNSPECIFIED, 167 PreciseDisconnectCause.ERROR_UNSPECIFIED, 168 null /* imsReasonInfo */); 169 } 170 171 /** 172 * Creates a new DisconnectCause instance. 173 * @param code The code for the disconnect cause. 174 * @param label The localized label to show to the user to explain the disconnect. 175 * @param description The localized description to show to the user to explain the disconnect. 176 * @param reason The reason for the disconnect. 177 * @param toneToPlay The tone to play on disconnect, as defined in {@link ToneGenerator}. 178 * @param telephonyDisconnectCause The Telephony disconnect cause. 179 * @param telephonyPreciseDisconnectCause The Telephony precise disconnect cause. 180 * @param imsReasonInfo The relevant {@link ImsReasonInfo}, or {@code null} if not available. 181 * @hide 182 */ DisconnectCause(int code, CharSequence label, CharSequence description, String reason, int toneToPlay, @Annotation.DisconnectCauses int telephonyDisconnectCause, @Annotation.PreciseDisconnectCauses int telephonyPreciseDisconnectCause, @Nullable ImsReasonInfo imsReasonInfo)183 public DisconnectCause(int code, CharSequence label, CharSequence description, String reason, 184 int toneToPlay, @Annotation.DisconnectCauses int telephonyDisconnectCause, 185 @Annotation.PreciseDisconnectCauses int telephonyPreciseDisconnectCause, 186 @Nullable ImsReasonInfo imsReasonInfo) { 187 mDisconnectCode = code; 188 mDisconnectLabel = label; 189 mDisconnectDescription = description; 190 mDisconnectReason = reason; 191 mToneToPlay = toneToPlay; 192 mTelephonyDisconnectCause = telephonyDisconnectCause; 193 mTelephonyPreciseDisconnectCause = telephonyPreciseDisconnectCause; 194 mImsReasonInfo = imsReasonInfo; 195 } 196 197 /** 198 * Returns the code for the reason for this disconnect. 199 * 200 * @return The disconnect code. 201 */ getCode()202 public int getCode() { 203 return mDisconnectCode; 204 } 205 206 /** 207 * Returns a short label which explains the reason for the disconnect cause and is for display 208 * in the user interface. If not null, it is expected that the In-Call UI should display this 209 * text where it would normally display the call state ("Dialing", "Disconnected") and is 210 * therefore expected to be relatively small. The {@link ConnectionService } is responsible for 211 * providing and localizing this label. If there is no string provided, returns null. 212 * 213 * @return The disconnect label. 214 */ getLabel()215 public CharSequence getLabel() { 216 return mDisconnectLabel; 217 } 218 219 /** 220 * Returns a description which explains the reason for the disconnect cause and is for display 221 * in the user interface. This optional text is generally a longer and more descriptive version 222 * of {@link #getLabel}, however it can exist even if {@link #getLabel} is empty. The In-Call UI 223 * should display this relatively prominently; the traditional implementation displays this as 224 * an alert dialog. The {@link ConnectionService} is responsible for providing and localizing 225 * this message. If there is no string provided, returns null. 226 * 227 * @return The disconnect description. 228 */ getDescription()229 public CharSequence getDescription() { 230 return mDisconnectDescription; 231 } 232 233 /** 234 * Returns an explanation of the reason for the disconnect. This is not intended for display to 235 * the user and is used mainly for logging. 236 * 237 * @return The disconnect reason. 238 */ getReason()239 public String getReason() { 240 return mDisconnectReason; 241 } 242 243 /** 244 * Returns the telephony {@link android.telephony.DisconnectCause} for the call. 245 * @return The disconnect cause. 246 * @hide 247 */ getTelephonyDisconnectCause()248 public @Annotation.DisconnectCauses int getTelephonyDisconnectCause() { 249 return mTelephonyDisconnectCause; 250 } 251 252 /** 253 * Returns the telephony {@link android.telephony.PreciseDisconnectCause} for the call. 254 * @return The precise disconnect cause. 255 * @hide 256 */ getTelephonyPreciseDisconnectCause()257 public @Annotation.PreciseDisconnectCauses int getTelephonyPreciseDisconnectCause() { 258 return mTelephonyPreciseDisconnectCause; 259 } 260 261 /** 262 * Returns the telephony {@link ImsReasonInfo} associated with the call disconnection. 263 * @return The {@link ImsReasonInfo} or {@code null} if not known. 264 * @hide 265 */ getImsReasonInfo()266 public @Nullable ImsReasonInfo getImsReasonInfo() { 267 return mImsReasonInfo; 268 } 269 270 /** 271 * Returns the tone to play when disconnected. 272 * 273 * @return the tone as defined in {@link ToneGenerator} to play when disconnected. 274 */ getTone()275 public int getTone() { 276 return mToneToPlay; 277 } 278 279 public static final @android.annotation.NonNull Creator<DisconnectCause> CREATOR 280 = new Creator<DisconnectCause>() { 281 @Override 282 public DisconnectCause createFromParcel(Parcel source) { 283 int code = source.readInt(); 284 CharSequence label = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source); 285 CharSequence description = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source); 286 String reason = source.readString(); 287 int tone = source.readInt(); 288 int telephonyDisconnectCause = source.readInt(); 289 int telephonyPreciseDisconnectCause = source.readInt(); 290 ImsReasonInfo imsReasonInfo = source.readParcelable(null, android.telephony.ims.ImsReasonInfo.class); 291 return new DisconnectCause(code, label, description, reason, tone, 292 telephonyDisconnectCause, telephonyPreciseDisconnectCause, imsReasonInfo); 293 } 294 295 @Override 296 public DisconnectCause[] newArray(int size) { 297 return new DisconnectCause[size]; 298 } 299 }; 300 301 @Override writeToParcel(Parcel destination, int flags)302 public void writeToParcel(Parcel destination, int flags) { 303 destination.writeInt(mDisconnectCode); 304 TextUtils.writeToParcel(mDisconnectLabel, destination, flags); 305 TextUtils.writeToParcel(mDisconnectDescription, destination, flags); 306 destination.writeString(mDisconnectReason); 307 destination.writeInt(mToneToPlay); 308 destination.writeInt(mTelephonyDisconnectCause); 309 destination.writeInt(mTelephonyPreciseDisconnectCause); 310 destination.writeParcelable(mImsReasonInfo, 0); 311 } 312 313 @Override describeContents()314 public int describeContents() { 315 return 0; 316 } 317 318 @Override hashCode()319 public int hashCode() { 320 return Objects.hashCode(mDisconnectCode) 321 + Objects.hashCode(mDisconnectLabel) 322 + Objects.hashCode(mDisconnectDescription) 323 + Objects.hashCode(mDisconnectReason) 324 + Objects.hashCode(mToneToPlay) 325 + Objects.hashCode(mTelephonyDisconnectCause) 326 + Objects.hashCode(mTelephonyPreciseDisconnectCause) 327 + Objects.hashCode(mImsReasonInfo); 328 } 329 330 @Override equals(Object o)331 public boolean equals(Object o) { 332 if (o instanceof DisconnectCause) { 333 DisconnectCause d = (DisconnectCause) o; 334 return Objects.equals(mDisconnectCode, d.getCode()) 335 && Objects.equals(mDisconnectLabel, d.getLabel()) 336 && Objects.equals(mDisconnectDescription, d.getDescription()) 337 && Objects.equals(mDisconnectReason, d.getReason()) 338 && Objects.equals(mToneToPlay, d.getTone()) 339 && Objects.equals(mTelephonyDisconnectCause, d.getTelephonyDisconnectCause()) 340 && Objects.equals(mTelephonyPreciseDisconnectCause, 341 d.getTelephonyPreciseDisconnectCause()) 342 && Objects.equals(mImsReasonInfo, d.getImsReasonInfo()); 343 } 344 return false; 345 } 346 347 @Override toString()348 public String toString() { 349 String code = ""; 350 switch (mDisconnectCode) { 351 case UNKNOWN: 352 code = "UNKNOWN"; 353 break; 354 case ERROR: 355 code = "ERROR"; 356 break; 357 case LOCAL: 358 code = "LOCAL"; 359 break; 360 case REMOTE: 361 code = "REMOTE"; 362 break; 363 case CANCELED: 364 code = "CANCELED"; 365 break; 366 case MISSED: 367 code = "MISSED"; 368 break; 369 case REJECTED: 370 code = "REJECTED"; 371 break; 372 case BUSY: 373 code = "BUSY"; 374 break; 375 case RESTRICTED: 376 code = "RESTRICTED"; 377 break; 378 case OTHER: 379 code = "OTHER"; 380 break; 381 case CONNECTION_MANAGER_NOT_SUPPORTED: 382 code = "CONNECTION_MANAGER_NOT_SUPPORTED"; 383 break; 384 case CALL_PULLED: 385 code = "CALL_PULLED"; 386 break; 387 case ANSWERED_ELSEWHERE: 388 code = "ANSWERED_ELSEWHERE"; 389 break; 390 default: 391 code = "invalid code: " + mDisconnectCode; 392 break; 393 } 394 String label = mDisconnectLabel == null ? "" : mDisconnectLabel.toString(); 395 String description = mDisconnectDescription == null 396 ? "" : mDisconnectDescription.toString(); 397 String reason = mDisconnectReason == null ? "" : mDisconnectReason; 398 return "DisconnectCause [ Code: (" + code + ")" 399 + " Label: (" + label + ")" 400 + " Description: (" + description + ")" 401 + " Reason: (" + reason + ")" 402 + " Tone: (" + mToneToPlay + ") " 403 + " TelephonyCause: " + mTelephonyDisconnectCause + "/" 404 + mTelephonyPreciseDisconnectCause 405 + " ImsReasonInfo: " 406 + mImsReasonInfo 407 + "]"; 408 } 409 } 410