1 /* 2 * Copyright (C) 2013 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.internal.telephony.imsphone; 18 19 import static android.telephony.CarrierConfigManager.USSD_OVER_CS_ONLY; 20 import static android.telephony.CarrierConfigManager.USSD_OVER_CS_PREFERRED; 21 import static android.telephony.CarrierConfigManager.USSD_OVER_IMS_ONLY; 22 import static android.telephony.CarrierConfigManager.USSD_OVER_IMS_PREFERRED; 23 import static android.telephony.ServiceState.STATE_IN_SERVICE; 24 25 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_DATA; 26 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_DATA_ASYNC; 27 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_DATA_SYNC; 28 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_FAX; 29 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_MAX; 30 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_NONE; 31 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_PACKET; 32 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_PAD; 33 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_SMS; 34 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE; 35 36 import android.compat.annotation.UnsupportedAppUsage; 37 import android.content.Context; 38 import android.content.res.Resources; 39 import android.os.AsyncResult; 40 import android.os.Build; 41 import android.os.Handler; 42 import android.os.Message; 43 import android.os.PersistableBundle; 44 import android.os.ResultReceiver; 45 import android.telephony.CarrierConfigManager; 46 import android.telephony.PhoneNumberUtils; 47 import android.telephony.TelephonyManager; 48 import android.telephony.ims.ImsCallForwardInfo; 49 import android.telephony.ims.ImsReasonInfo; 50 import android.telephony.ims.ImsSsData; 51 import android.telephony.ims.ImsSsInfo; 52 import android.telephony.ims.ImsUtListener; 53 import android.telephony.ims.stub.ImsUtImplBase; 54 import android.text.SpannableStringBuilder; 55 import android.text.TextUtils; 56 57 import com.android.ims.ImsException; 58 import com.android.internal.annotations.VisibleForTesting; 59 import com.android.internal.telephony.CallForwardInfo; 60 import com.android.internal.telephony.CallStateException; 61 import com.android.internal.telephony.CallWaitingController; 62 import com.android.internal.telephony.CommandException; 63 import com.android.internal.telephony.CommandsInterface; 64 import com.android.internal.telephony.MmiCode; 65 import com.android.internal.telephony.Phone; 66 import com.android.internal.telephony.flags.FeatureFlags; 67 import com.android.internal.telephony.gsm.GsmMmiCode; 68 import com.android.internal.telephony.uicc.IccRecords; 69 import com.android.telephony.Rlog; 70 71 import java.util.Arrays; 72 import java.util.List; 73 import java.util.regex.Matcher; 74 import java.util.regex.Pattern; 75 76 /** 77 * The motto for this file is: 78 * 79 * "NOTE: By using the # as a separator, most cases are expected to be unambiguous." 80 * -- TS 22.030 6.5.2 81 * 82 * {@hide} 83 * 84 */ 85 public final class ImsPhoneMmiCode extends Handler implements MmiCode { 86 static final String LOG_TAG = "ImsPhoneMmiCode"; 87 88 //***** Constants 89 90 // Max Size of the Short Code (aka Short String from TS 22.030 6.5.2) 91 private static final int MAX_LENGTH_SHORT_CODE = 2; 92 93 // TS 22.030 6.5.2 Every Short String USSD command will end with #-key 94 // (known as #-String) 95 private static final char END_OF_USSD_COMMAND = '#'; 96 97 // From TS 22.030 6.5.2 98 private static final String ACTION_ACTIVATE = "*"; 99 private static final String ACTION_DEACTIVATE = "#"; 100 private static final String ACTION_INTERROGATE = "*#"; 101 private static final String ACTION_REGISTER = "**"; 102 private static final String ACTION_ERASURE = "##"; 103 104 // Supp Service codes from TS 22.030 Annex B 105 106 //Called line presentation 107 private static final String SC_CLIP = "30"; 108 private static final String SC_CLIR = "31"; 109 private static final String SC_COLP = "76"; 110 private static final String SC_COLR = "77"; 111 112 //Calling name presentation 113 private static final String SC_CNAP = "300"; 114 115 // Call Forwarding 116 private static final String SC_CFU = "21"; 117 private static final String SC_CFB = "67"; 118 private static final String SC_CFNRy = "61"; 119 private static final String SC_CFNR = "62"; 120 // Call Forwarding unconditional Timer 121 private static final String SC_CFUT = "22"; 122 123 private static final String SC_CF_All = "002"; 124 private static final String SC_CF_All_Conditional = "004"; 125 126 // Call Waiting 127 private static final String SC_WAIT = "43"; 128 129 // Call Barring 130 private static final String SC_BAOC = "33"; 131 private static final String SC_BAOIC = "331"; 132 private static final String SC_BAOICxH = "332"; 133 private static final String SC_BAIC = "35"; 134 private static final String SC_BAICr = "351"; 135 136 private static final String SC_BA_ALL = "330"; 137 private static final String SC_BA_MO = "333"; 138 private static final String SC_BA_MT = "353"; 139 140 // Incoming/Anonymous call barring 141 private static final String SC_BS_MT = "156"; 142 private static final String SC_BAICa = "157"; 143 144 // Supp Service Password registration 145 private static final String SC_PWD = "03"; 146 147 // PIN/PIN2/PUK/PUK2 148 private static final String SC_PIN = "04"; 149 private static final String SC_PIN2 = "042"; 150 private static final String SC_PUK = "05"; 151 private static final String SC_PUK2 = "052"; 152 153 //***** Event Constants 154 155 private static final int EVENT_SET_COMPLETE = 0; 156 private static final int EVENT_QUERY_CF_COMPLETE = 1; 157 private static final int EVENT_USSD_COMPLETE = 2; 158 private static final int EVENT_QUERY_COMPLETE = 3; 159 private static final int EVENT_SET_CFF_COMPLETE = 4; 160 private static final int EVENT_USSD_CANCEL_COMPLETE = 5; 161 private static final int EVENT_GET_CLIR_COMPLETE = 6; 162 private static final int EVENT_SUPP_SVC_QUERY_COMPLETE = 7; 163 private static final int EVENT_QUERY_ICB_COMPLETE = 10; 164 165 //***** Calling Line Presentation Constants 166 private static final int NUM_PRESENTATION_ALLOWED = 0; 167 private static final int NUM_PRESENTATION_RESTRICTED = 1; 168 169 // Using same value with CallForwardEditPreference#DEFAULT_NO_REPLY_TIMER_FOR_CFNRY 170 private static final int DEFAULT_NO_REPLY_TIMER_FOR_CFNRY = 20; 171 172 //***** Supplementary Service Query Bundle Keys 173 // Used by IMS Service layer to put supp. serv. query 174 // responses into the ssInfo Bundle. 175 /** 176 * @deprecated Use {@link ImsUtListener#onLineIdentificationSupplementaryServiceResponse(int, 177 * ImsSsInfo)} API instead. 178 */ 179 @Deprecated 180 // Not used, only kept around to not break vendors using this key. 181 public static final String UT_BUNDLE_KEY_CLIR = "queryClir"; 182 /** 183 * @deprecated Use {@link ImsUtListener#onLineIdentificationSupplementaryServiceResponse(int, 184 * ImsSsInfo)} API instead. 185 */ 186 @Deprecated 187 // Not used, only kept around to not break vendors using this key. 188 public static final String UT_BUNDLE_KEY_SSINFO = "imsSsInfo"; 189 190 //***** Instance Variables 191 192 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 193 private ImsPhone mPhone; 194 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 195 private Context mContext; 196 private IccRecords mIccRecords; 197 198 private String mAction; // One of ACTION_* 199 private String mSc; // Service Code 200 private String mSia, mSib, mSic; // Service Info a,b,c 201 private String mPoundString; // Entire MMI string up to and including # 202 private String mDialingNumber; 203 private String mPwd; // For password registration 204 private ResultReceiver mCallbackReceiver; 205 206 private boolean mIsPendingUSSD; 207 208 private boolean mIsUssdRequest; 209 210 private boolean mIsCallFwdReg; 211 212 private boolean mIsNetworkInitiatedUSSD; 213 214 private State mState = State.PENDING; 215 private CharSequence mMessage; 216 private boolean mIsSsInfo = false; 217 //resgister/erasure of ICB (Specific DN) 218 static final String IcbDnMmi = "Specific Incoming Call Barring"; 219 //ICB (Anonymous) 220 static final String IcbAnonymousMmi = "Anonymous Incoming Call Barring"; 221 //***** Class Variables 222 223 224 // See TS 22.030 6.5.2 "Structure of the MMI" 225 226 private static Pattern sPatternSuppService = Pattern.compile( 227 "((\\*|#|\\*#|\\*\\*|##)(\\d{2,3})(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*))?)?)?)?#)(.*)"); 228 /* 1 2 3 4 5 6 7 8 9 10 11 12 229 230 1 = Full string up to and including # 231 2 = action (activation/interrogation/registration/erasure) 232 3 = service code 233 5 = SIA 234 7 = SIB 235 9 = SIC 236 10 = dialing number 237 */ 238 239 private static final int MATCH_GROUP_POUND_STRING = 1; 240 241 private static final int MATCH_GROUP_ACTION = 2; 242 //(activation/interrogation/registration/erasure) 243 244 private static final int MATCH_GROUP_SERVICE_CODE = 3; 245 private static final int MATCH_GROUP_SIA = 5; 246 private static final int MATCH_GROUP_SIB = 7; 247 private static final int MATCH_GROUP_SIC = 9; 248 private static final int MATCH_GROUP_PWD_CONFIRM = 11; 249 private static final int MATCH_GROUP_DIALING_NUMBER = 12; 250 static private String[] sTwoDigitNumberPattern; 251 252 private final FeatureFlags mFeatureFlags; 253 254 //***** Public Class methods 255 256 /** 257 * Some dial strings in GSM are defined to do non-call setup 258 * things, such as modify or query supplementary service settings (eg, call 259 * forwarding). These are generally referred to as "MMI codes". 260 * We look to see if the dial string contains a valid MMI code (potentially 261 * with a dial string at the end as well) and return info here. 262 * 263 * If the dial string contains no MMI code, we return an instance with 264 * only "dialingNumber" set 265 * 266 * Please see flow chart in TS 22.030 6.5.3.2 267 */ 268 269 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 270 @VisibleForTesting newFromDialString(String dialString, ImsPhone phone, FeatureFlags featureFlags)271 public static ImsPhoneMmiCode newFromDialString(String dialString, ImsPhone phone, 272 FeatureFlags featureFlags) { 273 return newFromDialString(dialString, phone, null, featureFlags); 274 } 275 newFromDialString(String dialString, ImsPhone phone, ResultReceiver wrappedCallback, FeatureFlags featureFlags)276 static ImsPhoneMmiCode newFromDialString(String dialString, ImsPhone phone, 277 ResultReceiver wrappedCallback, FeatureFlags featureFlags) { 278 Matcher m; 279 ImsPhoneMmiCode ret = null; 280 281 if ((phone.getDefaultPhone().getServiceState().getVoiceRoaming() 282 && phone.getDefaultPhone().supportsConversionOfCdmaCallerIdMmiCodesWhileRoaming()) 283 || (isEmergencyNumber(phone, dialString) 284 && isCarrierSupportCallerIdVerticalServiceCodes(phone))) { 285 /* The CDMA MMI coded dialString will be converted to a 3GPP MMI Coded dialString 286 so that it can be processed by the matcher and code below. This can be triggered if 287 the dialing string is an emergency number and carrier supports caller ID vertical 288 service codes *67, *82. 289 */ 290 dialString = convertCdmaMmiCodesTo3gppMmiCodes(dialString); 291 } 292 293 m = sPatternSuppService.matcher(dialString); 294 295 // Is this formatted like a standard supplementary service code? 296 if (m.matches()) { 297 ret = new ImsPhoneMmiCode(phone, featureFlags); 298 ret.mPoundString = makeEmptyNull(m.group(MATCH_GROUP_POUND_STRING)); 299 ret.mAction = makeEmptyNull(m.group(MATCH_GROUP_ACTION)); 300 ret.mSc = makeEmptyNull(m.group(MATCH_GROUP_SERVICE_CODE)); 301 ret.mSia = makeEmptyNull(m.group(MATCH_GROUP_SIA)); 302 ret.mSib = makeEmptyNull(m.group(MATCH_GROUP_SIB)); 303 ret.mSic = makeEmptyNull(m.group(MATCH_GROUP_SIC)); 304 ret.mPwd = makeEmptyNull(m.group(MATCH_GROUP_PWD_CONFIRM)); 305 ret.mDialingNumber = makeEmptyNull(m.group(MATCH_GROUP_DIALING_NUMBER)); 306 ret.mCallbackReceiver = wrappedCallback; 307 // According to TS 22.030 6.5.2 "Structure of the MMI", 308 // the dialing number should not ending with #. 309 // The dialing number ending # is treated as unique USSD, 310 // eg, *400#16 digit number# to recharge the prepaid card 311 // in India operator(Mumbai MTNL) 312 if (ret.mDialingNumber != null && 313 ret.mDialingNumber.endsWith("#") && 314 dialString.endsWith("#")){ 315 ret = new ImsPhoneMmiCode(phone, featureFlags); 316 ret.mPoundString = dialString; 317 } 318 } else if (dialString.endsWith("#")) { 319 // TS 22.030 sec 6.5.3.2 320 // "Entry of any characters defined in the 3GPP TS 23.038 [8] Default Alphabet 321 // (up to the maximum defined in 3GPP TS 24.080 [10]), followed by #SEND". 322 323 ret = new ImsPhoneMmiCode(phone, featureFlags); 324 ret.mPoundString = dialString; 325 } else if (GsmMmiCode.isTwoDigitShortCode(phone.getContext(), phone.getSubId(), 326 dialString)) { 327 //Is a country-specific exception to short codes as defined in TS 22.030, 6.5.3.2 328 ret = null; 329 } else if (isShortCode(dialString, phone)) { 330 // this may be a short code, as defined in TS 22.030, 6.5.3.2 331 ret = new ImsPhoneMmiCode(phone, featureFlags); 332 ret.mDialingNumber = dialString; 333 } 334 return ret; 335 } 336 convertCdmaMmiCodesTo3gppMmiCodes(String dialString)337 private static String convertCdmaMmiCodesTo3gppMmiCodes(String dialString) { 338 Matcher m; 339 m = sPatternCdmaMmiCodeWhileRoaming.matcher(dialString); 340 341 if (m.matches()) { 342 String serviceCode = makeEmptyNull(m.group(MATCH_GROUP_CDMA_MMI_CODE_SERVICE_CODE)); 343 String prefix = m.group(MATCH_GROUP_CDMA_MMI_CODE_NUMBER_PREFIX); 344 String number = makeEmptyNull(m.group(MATCH_GROUP_CDMA_MMI_CODE_NUMBER)); 345 346 if (serviceCode.equals("67") && number != null) { 347 // "#31#number" to invoke CLIR 348 dialString = ACTION_DEACTIVATE + SC_CLIR + ACTION_DEACTIVATE + prefix + number; 349 } else if (serviceCode.equals("82") && number != null) { 350 // "*31#number" to suppress CLIR 351 dialString = ACTION_ACTIVATE + SC_CLIR + ACTION_DEACTIVATE + prefix + number; 352 } 353 } 354 return dialString; 355 } 356 newNetworkInitiatedUssd(String ussdMessage, boolean isUssdRequest, ImsPhone phone, FeatureFlags featureFlags)357 public static ImsPhoneMmiCode newNetworkInitiatedUssd(String ussdMessage, 358 boolean isUssdRequest, ImsPhone phone, FeatureFlags featureFlags) { 359 ImsPhoneMmiCode ret; 360 361 ret = new ImsPhoneMmiCode(phone, featureFlags); 362 363 ret.mMessage = ussdMessage; 364 ret.mIsUssdRequest = isUssdRequest; 365 ret.mIsNetworkInitiatedUSSD = true; 366 367 // If it's a request, set to PENDING so that it's cancelable. 368 if (isUssdRequest) { 369 ret.mIsPendingUSSD = true; 370 ret.mState = State.PENDING; 371 } else { 372 ret.mState = State.COMPLETE; 373 } 374 375 return ret; 376 } 377 newFromUssdUserInput(String ussdMessage, ImsPhone phone, FeatureFlags featureFlags)378 static ImsPhoneMmiCode newFromUssdUserInput(String ussdMessage, ImsPhone phone, 379 FeatureFlags featureFlags) { 380 ImsPhoneMmiCode ret = new ImsPhoneMmiCode(phone, featureFlags); 381 382 ret.mMessage = ussdMessage; 383 ret.mState = State.PENDING; 384 ret.mIsPendingUSSD = true; 385 386 return ret; 387 } 388 389 //***** Private Class methods 390 391 /** make empty strings be null. 392 * Regexp returns empty strings for empty groups 393 */ 394 private static String makeEmptyNull(String s)395 makeEmptyNull (String s) { 396 if (s != null && s.length() == 0) return null; 397 398 return s; 399 } 400 isScMatchesSuppServType(String dialString)401 static boolean isScMatchesSuppServType(String dialString) { 402 boolean isMatch = false; 403 Matcher m = sPatternSuppService.matcher(dialString); 404 if (m.matches()) { 405 String sc = makeEmptyNull(m.group(MATCH_GROUP_SERVICE_CODE)); 406 if (sc.equals(SC_CFUT)) { 407 isMatch = true; 408 } else if(sc.equals(SC_BS_MT)) { 409 isMatch = true; 410 } 411 } 412 return isMatch; 413 } 414 415 /** returns true of the string is empty or null */ 416 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 417 private static boolean isEmptyOrNull(CharSequence s)418 isEmptyOrNull(CharSequence s) { 419 return s == null || (s.length() == 0); 420 } 421 422 private static int scToCallForwardReason(String sc)423 scToCallForwardReason(String sc) { 424 if (sc == null) { 425 throw new RuntimeException ("invalid call forward sc"); 426 } 427 428 if (sc.equals(SC_CF_All)) { 429 return CommandsInterface.CF_REASON_ALL; 430 } else if (sc.equals(SC_CFU)) { 431 return CommandsInterface.CF_REASON_UNCONDITIONAL; 432 } else if (sc.equals(SC_CFB)) { 433 return CommandsInterface.CF_REASON_BUSY; 434 } else if (sc.equals(SC_CFNR)) { 435 return CommandsInterface.CF_REASON_NOT_REACHABLE; 436 } else if (sc.equals(SC_CFNRy)) { 437 return CommandsInterface.CF_REASON_NO_REPLY; 438 } else if (sc.equals(SC_CF_All_Conditional)) { 439 return CommandsInterface.CF_REASON_ALL_CONDITIONAL; 440 } else { 441 throw new RuntimeException ("invalid call forward sc"); 442 } 443 } 444 445 private static int siToServiceClass(String si)446 siToServiceClass(String si) { 447 if (si == null || si.length() == 0) { 448 return SERVICE_CLASS_NONE; 449 } else { 450 // NumberFormatException should cause MMI fail 451 int serviceCode = Integer.parseInt(si, 10); 452 453 switch (serviceCode) { 454 case 10: return SERVICE_CLASS_SMS + SERVICE_CLASS_FAX + SERVICE_CLASS_VOICE; 455 case 11: return SERVICE_CLASS_VOICE; 456 case 12: return SERVICE_CLASS_SMS + SERVICE_CLASS_FAX; 457 case 13: return SERVICE_CLASS_FAX; 458 459 case 16: return SERVICE_CLASS_SMS; 460 461 case 19: return SERVICE_CLASS_FAX + SERVICE_CLASS_VOICE; 462 463 case 20: return SERVICE_CLASS_DATA_ASYNC + SERVICE_CLASS_DATA_SYNC; 464 465 case 21: return SERVICE_CLASS_PAD + SERVICE_CLASS_DATA_ASYNC; 466 case 22: return SERVICE_CLASS_PACKET + SERVICE_CLASS_DATA_SYNC; 467 case 24: return SERVICE_CLASS_DATA_SYNC; 468 case 25: return SERVICE_CLASS_DATA_ASYNC; 469 case 26: return SERVICE_CLASS_DATA_SYNC + SERVICE_CLASS_VOICE; 470 case 99: return SERVICE_CLASS_PACKET; 471 472 default: 473 throw new RuntimeException("unsupported MMI service code " + si); 474 } 475 } 476 } 477 478 private static int siToTime(String si)479 siToTime (String si) { 480 if (si == null || si.length() == 0) { 481 return 0; 482 } else { 483 // NumberFormatException should cause MMI fail 484 return Integer.parseInt(si, 10); 485 } 486 } 487 488 static boolean isServiceCodeCallForwarding(String sc)489 isServiceCodeCallForwarding(String sc) { 490 return sc != null && 491 (sc.equals(SC_CFU) 492 || sc.equals(SC_CFB) || sc.equals(SC_CFNRy) 493 || sc.equals(SC_CFNR) || sc.equals(SC_CF_All) 494 || sc.equals(SC_CF_All_Conditional)); 495 } 496 497 static boolean isServiceCodeCallBarring(String sc)498 isServiceCodeCallBarring(String sc) { 499 Resources resource = Resources.getSystem(); 500 if (sc != null) { 501 String[] barringMMI = resource.getStringArray( 502 com.android.internal.R.array.config_callBarringMMI_for_ims); 503 if (barringMMI != null) { 504 for (String match : barringMMI) { 505 if (sc.equals(match)) return true; 506 } 507 } 508 } 509 return false; 510 } 511 isPinPukCommand(String sc)512 static boolean isPinPukCommand(String sc) { 513 return sc != null && (sc.equals(SC_PIN) || sc.equals(SC_PIN2) 514 || sc.equals(SC_PUK) || sc.equals(SC_PUK2)); 515 } 516 517 /** 518 * Whether the dial string is supplementary service code. 519 * 520 * @param dialString The dial string. 521 * @return true if the dial string is supplementary service code, and {@code false} otherwise. 522 */ isSuppServiceCodes(String dialString, Phone phone)523 public static boolean isSuppServiceCodes(String dialString, Phone phone) { 524 if (phone != null && phone.getServiceState().getVoiceRoaming() 525 && phone.getDefaultPhone().supportsConversionOfCdmaCallerIdMmiCodesWhileRoaming()) { 526 /* The CDMA MMI coded dialString will be converted to a 3GPP MMI Coded dialString 527 so that it can be processed by the matcher and code below 528 */ 529 dialString = convertCdmaMmiCodesTo3gppMmiCodes(dialString); 530 } 531 532 Matcher m = sPatternSuppService.matcher(dialString); 533 if (m.matches()) { 534 String sc = makeEmptyNull(m.group(MATCH_GROUP_SERVICE_CODE)); 535 if (isServiceCodeCallForwarding(sc)) { 536 return true; 537 } else if (isServiceCodeCallBarring(sc)) { 538 return true; 539 } else if (sc != null && sc.equals(SC_CFUT)) { 540 return true; 541 } else if (sc != null && sc.equals(SC_CLIP)) { 542 return true; 543 } else if (sc != null && sc.equals(SC_CLIR)) { 544 return true; 545 } else if (sc != null && sc.equals(SC_COLP)) { 546 return true; 547 } else if (sc != null && sc.equals(SC_COLR)) { 548 return true; 549 } else if (sc != null && sc.equals(SC_CNAP)) { 550 return true; 551 } else if (sc != null && sc.equals(SC_BS_MT)) { 552 return true; 553 } else if (sc != null && sc.equals(SC_BAICa)) { 554 return true; 555 } else if (sc != null && sc.equals(SC_PWD)) { 556 return true; 557 } else if (sc != null && sc.equals(SC_WAIT)) { 558 return true; 559 } else if (isPinPukCommand(sc)) { 560 return true; 561 } 562 } 563 return false; 564 } 565 566 static String scToBarringFacility(String sc)567 scToBarringFacility(String sc) { 568 if (sc == null) { 569 throw new RuntimeException ("invalid call barring sc"); 570 } 571 572 if (sc.equals(SC_BAOC)) { 573 return CommandsInterface.CB_FACILITY_BAOC; 574 } else if (sc.equals(SC_BAOIC)) { 575 return CommandsInterface.CB_FACILITY_BAOIC; 576 } else if (sc.equals(SC_BAOICxH)) { 577 return CommandsInterface.CB_FACILITY_BAOICxH; 578 } else if (sc.equals(SC_BAIC)) { 579 return CommandsInterface.CB_FACILITY_BAIC; 580 } else if (sc.equals(SC_BAICr)) { 581 return CommandsInterface.CB_FACILITY_BAICr; 582 } else if (sc.equals(SC_BA_ALL)) { 583 return CommandsInterface.CB_FACILITY_BA_ALL; 584 } else if (sc.equals(SC_BA_MO)) { 585 return CommandsInterface.CB_FACILITY_BA_MO; 586 } else if (sc.equals(SC_BA_MT)) { 587 return CommandsInterface.CB_FACILITY_BA_MT; 588 } else { 589 throw new RuntimeException ("invalid call barring sc"); 590 } 591 } 592 593 //***** Constructor 594 ImsPhoneMmiCode(ImsPhone phone, FeatureFlags featureFlags)595 public ImsPhoneMmiCode(ImsPhone phone, FeatureFlags featureFlags) { 596 // The telephony unit-test cases may create ImsPhoneMmiCode's 597 // in secondary threads 598 super(phone.getHandler().getLooper()); 599 mPhone = phone; 600 mContext = phone.getContext(); 601 mIccRecords = mPhone.mDefaultPhone.getIccRecords(); 602 603 mFeatureFlags = featureFlags; 604 } 605 606 //***** MmiCode implementation 607 608 @Override 609 public State getState()610 getState() { 611 return mState; 612 } 613 614 @Override 615 public CharSequence getMessage()616 getMessage() { 617 return mMessage; 618 } 619 620 @Override getPhone()621 public Phone getPhone() { return mPhone; } 622 623 // inherited javadoc suffices 624 @Override 625 public void cancel()626 cancel() { 627 // Complete or failed cannot be cancelled 628 if (mState == State.COMPLETE || mState == State.FAILED) { 629 return; 630 } 631 632 mState = State.CANCELLED; 633 634 if (mIsPendingUSSD) { 635 mPhone.cancelUSSD(obtainMessage(EVENT_USSD_CANCEL_COMPLETE, this)); 636 } else { 637 mPhone.onMMIDone (this); 638 } 639 640 } 641 642 @Override isCancelable()643 public boolean isCancelable() { 644 /* Can only cancel pending USSD sessions. */ 645 return mIsPendingUSSD; 646 } 647 648 //***** Instance Methods 649 650 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) getDialingNumber()651 String getDialingNumber() { 652 return mDialingNumber; 653 } 654 655 /** Does this dial string contain a structured or unstructured MMI code? */ 656 boolean isMMI()657 isMMI() { 658 return mPoundString != null; 659 } 660 661 /* Is this a 1 or 2 digit "short code" as defined in TS 22.030 sec 6.5.3.2? */ 662 boolean isShortCode()663 isShortCode() { 664 return mPoundString == null 665 && mDialingNumber != null && mDialingNumber.length() <= 2; 666 667 } 668 669 @Override getDialString()670 public String getDialString() { 671 return mPoundString; 672 } 673 674 /** 675 * Helper function for newFromDialString. Returns true if dialString appears 676 * to be a short code AND conditions are correct for it to be treated as 677 * such. 678 */ isShortCode(String dialString, ImsPhone phone)679 static private boolean isShortCode(String dialString, ImsPhone phone) { 680 // Refer to TS 22.030 Figure 3.5.3.2: 681 if (dialString == null) { 682 return false; 683 } 684 685 // Illegal dial string characters will give a ZERO length. 686 // At this point we do not want to crash as any application with 687 // call privileges may send a non dial string. 688 // It return false as when the dialString is equal to NULL. 689 if (dialString.length() == 0) { 690 return false; 691 } 692 if (isEmergencyNumber(phone, dialString)) { 693 return false; 694 } else { 695 return isShortCodeUSSD(dialString, phone); 696 } 697 } 698 699 /** 700 * Helper function for isShortCode. Returns true if dialString appears to be 701 * a short code and it is a USSD structure 702 * 703 * According to the 3PGG TS 22.030 specification Figure 3.5.3.2: A 1 or 2 704 * digit "short code" is treated as USSD if it is entered while on a call or 705 * does not satisfy the condition (exactly 2 digits && starts with '1'), there 706 * are however exceptions to this rule (see below) 707 * 708 * Exception (1) to Call initiation is: If the user of the device is already in a call 709 * and enters a Short String without any #-key at the end and the length of the Short String is 710 * equal or less then the MAX_LENGTH_SHORT_CODE [constant that is equal to 2] 711 * 712 * The phone shall initiate a USSD/SS commands. 713 */ isShortCodeUSSD(String dialString, ImsPhone phone)714 static private boolean isShortCodeUSSD(String dialString, ImsPhone phone) { 715 if (dialString != null && dialString.length() <= MAX_LENGTH_SHORT_CODE) { 716 if (phone.isInCall()) { 717 return true; 718 } 719 720 if (dialString.length() != MAX_LENGTH_SHORT_CODE || 721 dialString.charAt(0) != '1') { 722 return true; 723 } 724 } 725 return false; 726 } 727 728 /** 729 * @return true if the Service Code is PIN/PIN2/PUK/PUK2-related 730 */ isPinPukCommand()731 public boolean isPinPukCommand() { 732 return isPinPukCommand(mSc); 733 } 734 735 /** 736 * See TS 22.030 Annex B. 737 * In temporary mode, to suppress CLIR for a single call, enter: 738 * " * 31 # [called number] SEND " 739 * In temporary mode, to invoke CLIR for a single call enter: 740 * " # 31 # [called number] SEND " 741 */ 742 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 743 @VisibleForTesting isTemporaryModeCLIR()744 public boolean isTemporaryModeCLIR() { 745 return mSc != null && mSc.equals(SC_CLIR) 746 && mDialingNumber != null && (isActivate() || isDeactivate()); 747 } 748 749 /** 750 * Checks if the dialing string is an emergency number. 751 */ 752 @VisibleForTesting isEmergencyNumber(Phone phone, String dialString)753 public static boolean isEmergencyNumber(Phone phone, String dialString) { 754 try { 755 TelephonyManager tm = phone.getContext().getSystemService(TelephonyManager.class); 756 return tm.isEmergencyNumber(dialString); 757 } catch (RuntimeException ex) { 758 return false; 759 } 760 } 761 762 /** 763 * Checks if carrier supports caller id vertical service codes by checking with 764 * {@link CarrierConfigManager#KEY_CARRIER_SUPPORTS_CALLER_ID_VERTICAL_SERVICE_CODES_BOOL}. 765 */ 766 @VisibleForTesting isCarrierSupportCallerIdVerticalServiceCodes(Phone phone)767 public static boolean isCarrierSupportCallerIdVerticalServiceCodes(Phone phone) { 768 CarrierConfigManager configManager = phone.getContext().getSystemService( 769 CarrierConfigManager.class); 770 PersistableBundle b = null; 771 if (configManager != null) { 772 // If an invalid subId is used, this bundle will contain default values. 773 b = configManager.getConfigForSubId(phone.getSubId()); 774 } 775 if (b != null) { 776 return b == null ? false : b.getBoolean(CarrierConfigManager 777 .KEY_CARRIER_SUPPORTS_CALLER_ID_VERTICAL_SERVICE_CODES_BOOL); 778 } 779 return false; 780 } 781 782 /** 783 * returns CommandsInterface.CLIR_* 784 * See also isTemporaryModeCLIR() 785 */ 786 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 787 int getCLIRMode()788 getCLIRMode() { 789 if (mSc != null && mSc.equals(SC_CLIR)) { 790 if (isActivate()) { 791 return CommandsInterface.CLIR_SUPPRESSION; 792 } else if (isDeactivate()) { 793 return CommandsInterface.CLIR_INVOCATION; 794 } 795 } 796 797 return CommandsInterface.CLIR_DEFAULT; 798 } 799 800 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) isActivate()801 boolean isActivate() { 802 return mAction != null && mAction.equals(ACTION_ACTIVATE); 803 } 804 805 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) isDeactivate()806 boolean isDeactivate() { 807 return mAction != null && mAction.equals(ACTION_DEACTIVATE); 808 } 809 isInterrogate()810 boolean isInterrogate() { 811 return mAction != null && mAction.equals(ACTION_INTERROGATE); 812 } 813 814 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) isRegister()815 boolean isRegister() { 816 return mAction != null && mAction.equals(ACTION_REGISTER); 817 } 818 819 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) isErasure()820 boolean isErasure() { 821 return mAction != null && mAction.equals(ACTION_ERASURE); 822 } 823 824 /** 825 * Returns true if this is a USSD code that's been submitted to the 826 * network...eg, after processCode() is called 827 */ isPendingUSSD()828 public boolean isPendingUSSD() { 829 return mIsPendingUSSD; 830 } 831 832 @Override isUssdRequest()833 public boolean isUssdRequest() { 834 return mIsUssdRequest; 835 } 836 837 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 838 boolean isSupportedOverImsPhone()839 isSupportedOverImsPhone() { 840 if (isShortCode()) return true; 841 else if (isServiceCodeCallForwarding(mSc) 842 || isServiceCodeCallBarring(mSc) 843 || (mSc != null && mSc.equals(SC_WAIT)) 844 || (mSc != null && mSc.equals(SC_CLIR)) 845 || (mSc != null && mSc.equals(SC_CLIP)) 846 || (mSc != null && mSc.equals(SC_COLR)) 847 || (mSc != null && mSc.equals(SC_COLP)) 848 || (mSc != null && mSc.equals(SC_BS_MT)) 849 || (mSc != null && mSc.equals(SC_BAICa))) { 850 851 try { 852 int serviceClass = siToServiceClass(mSib); 853 if (serviceClass != SERVICE_CLASS_NONE 854 && serviceClass != SERVICE_CLASS_VOICE 855 && serviceClass != (SERVICE_CLASS_PACKET 856 + SERVICE_CLASS_DATA_SYNC)) { 857 return false; 858 } 859 return true; 860 } catch (RuntimeException exc) { 861 Rlog.d(LOG_TAG, "Invalid service class " + exc); 862 } 863 } else if (isPinPukCommand() 864 || (mSc != null 865 && (mSc.equals(SC_PWD) || mSc.equals(SC_CLIP) || mSc.equals(SC_CLIR)))) { 866 return false; 867 } else if (mPoundString != null) return true; 868 869 return false; 870 } 871 872 /* 873 * The below actions are IMS/Volte CallBarring actions.We have not defined 874 * these actions in ImscommandInterface.However we have reused existing 875 * actions of CallForwarding as, both CF and CB actions are used for same 876 * purpose. 877 */ callBarAction(String dialingNumber)878 public int callBarAction(String dialingNumber) { 879 if (isActivate()) { 880 return CommandsInterface.CF_ACTION_ENABLE; 881 } else if (isDeactivate()) { 882 return CommandsInterface.CF_ACTION_DISABLE; 883 } else if (isRegister()) { 884 if (!isEmptyOrNull(dialingNumber)) { 885 return CommandsInterface.CF_ACTION_REGISTRATION; 886 } else { 887 throw new RuntimeException ("invalid action"); 888 } 889 } else if (isErasure()) { 890 return CommandsInterface.CF_ACTION_ERASURE; 891 } else { 892 throw new RuntimeException ("invalid action"); 893 } 894 } 895 896 /** Process a MMI code or short code...anything that isn't a dialing number */ 897 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 898 public void processCode()899 processCode () throws CallStateException { 900 try { 901 if (isShortCode()) { 902 Rlog.d(LOG_TAG, "processCode: isShortCode"); 903 904 // These just get treated as USSD. 905 if (isUssdOverImsAllowed()) { 906 Rlog.d(LOG_TAG, "processCode: Sending short code '" 907 + mDialingNumber + "' over IMS pipe."); 908 sendUssd(mDialingNumber); 909 } else { 910 Rlog.d(LOG_TAG, "processCode: Sending short code '" 911 + mDialingNumber + "' over CS pipe."); 912 throw new CallStateException(Phone.CS_FALLBACK); 913 } 914 } else if (isServiceCodeCallForwarding(mSc)) { 915 Rlog.d(LOG_TAG, "processCode: is CF"); 916 917 String dialingNumber = mSia; 918 int reason = scToCallForwardReason(mSc); 919 int serviceClass = siToServiceClass(mSib); 920 int time; 921 922 if (mFeatureFlags.useCarrierConfigForCfnryTimeViaMmi()) { 923 // If the code is CFNRy and time is null(empty) 924 // use the default time value from CarrierConfig 925 if (mSc.equals(SC_CFNRy) && isEmptyOrNull(mSic)) { 926 time = getCfnryTime(); 927 } else { 928 time = siToTime(mSic); 929 } 930 } else { 931 time = siToTime(mSic); 932 } 933 934 if (isInterrogate()) { 935 mPhone.getCallForwardingOption(reason, serviceClass, 936 obtainMessage(EVENT_QUERY_CF_COMPLETE, this)); 937 } else { 938 int cfAction; 939 940 if (isActivate()) { 941 // 3GPP TS 22.030 6.5.2 942 // a call forwarding request with a single * would be 943 // interpreted as registration if containing a forwarded-to 944 // number, or an activation if not 945 if (isEmptyOrNull(dialingNumber)) { 946 cfAction = CommandsInterface.CF_ACTION_ENABLE; 947 mIsCallFwdReg = false; 948 } else { 949 cfAction = CommandsInterface.CF_ACTION_REGISTRATION; 950 mIsCallFwdReg = true; 951 } 952 } else if (isDeactivate()) { 953 cfAction = CommandsInterface.CF_ACTION_DISABLE; 954 } else if (isRegister()) { 955 cfAction = CommandsInterface.CF_ACTION_REGISTRATION; 956 } else if (isErasure()) { 957 cfAction = CommandsInterface.CF_ACTION_ERASURE; 958 } else { 959 throw new RuntimeException ("invalid action"); 960 } 961 962 int isSettingUnconditional = 963 ((reason == CommandsInterface.CF_REASON_UNCONDITIONAL) || 964 (reason == CommandsInterface.CF_REASON_ALL)) ? 1 : 0; 965 966 int isEnableDesired = 967 ((cfAction == CommandsInterface.CF_ACTION_ENABLE) || 968 (cfAction == CommandsInterface.CF_ACTION_REGISTRATION)) ? 1 : 0; 969 970 Rlog.d(LOG_TAG, "processCode: is CF setCallForward"); 971 mPhone.setCallForwardingOption(cfAction, reason, 972 dialingNumber, serviceClass, time, obtainMessage( 973 EVENT_SET_CFF_COMPLETE, 974 isSettingUnconditional, 975 isEnableDesired, this)); 976 } 977 } else if (isServiceCodeCallBarring(mSc)) { 978 // sia = password 979 // sib = basic service group 980 // service group is not supported 981 982 String password = mSia; 983 String facility = scToBarringFacility(mSc); 984 int serviceClass = siToServiceClass(mSib); 985 986 if (isInterrogate()) { 987 mPhone.getCallBarring(facility, 988 obtainMessage(EVENT_SUPP_SVC_QUERY_COMPLETE, this), serviceClass); 989 } else if (isActivate() || isDeactivate()) { 990 mPhone.setCallBarring(facility, isActivate(), password, 991 obtainMessage(EVENT_SET_COMPLETE, this), serviceClass); 992 } else { 993 throw new RuntimeException ("Invalid or Unsupported MMI Code"); 994 } 995 } else if (mSc != null && mSc.equals(SC_CLIR)) { 996 // NOTE: Since these supplementary services are accessed only 997 // via MMI codes, methods have not been added to ImsPhone. 998 // Only the UT interface handle is used. 999 if (isActivate() 1000 && !mPhone.getDefaultPhone().isClirActivationAndDeactivationPrevented()) { 1001 try { 1002 mPhone.setOutgoingCallerIdDisplay(CommandsInterface.CLIR_INVOCATION, 1003 obtainMessage(EVENT_SET_COMPLETE, this)); 1004 } catch (Exception e) { 1005 Rlog.d(LOG_TAG, "processCode: Could not get UT handle for updateCLIR."); 1006 } 1007 } else if (isDeactivate() 1008 && !mPhone.getDefaultPhone().isClirActivationAndDeactivationPrevented()) { 1009 try { 1010 mPhone.setOutgoingCallerIdDisplay(CommandsInterface.CLIR_SUPPRESSION, 1011 obtainMessage(EVENT_SET_COMPLETE, this)); 1012 } catch (Exception e) { 1013 Rlog.d(LOG_TAG, "processCode: Could not get UT handle for updateCLIR."); 1014 } 1015 } else if (isInterrogate()) { 1016 try { 1017 mPhone.getOutgoingCallerIdDisplay(obtainMessage(EVENT_GET_CLIR_COMPLETE, this)); 1018 } catch (Exception e) { 1019 Rlog.d(LOG_TAG, "processCode: Could not get UT handle for queryCLIR."); 1020 } 1021 } else { 1022 throw new RuntimeException ("Invalid or Unsupported MMI Code"); 1023 } 1024 } else if (mSc != null && mSc.equals(SC_CLIP)) { 1025 // NOTE: Refer to the note above. 1026 if (isInterrogate()) { 1027 try { 1028 mPhone.queryCLIP(obtainMessage(EVENT_SUPP_SVC_QUERY_COMPLETE, this)); 1029 } catch (Exception e) { 1030 Rlog.d(LOG_TAG, "processCode: Could not get UT handle for queryCLIP."); 1031 } 1032 } else if (isActivate() || isDeactivate()) { 1033 try { 1034 mPhone.mCT.getUtInterface().updateCLIP(isActivate(), 1035 obtainMessage(EVENT_SET_COMPLETE, this)); 1036 } catch (ImsException e) { 1037 Rlog.d(LOG_TAG, "processCode: Could not get UT handle for updateCLIP."); 1038 } 1039 } else { 1040 throw new RuntimeException ("Invalid or Unsupported MMI Code"); 1041 } 1042 } else if (mSc != null && mSc.equals(SC_COLP)) { 1043 // NOTE: Refer to the note above. 1044 if (isInterrogate()) { 1045 try { 1046 mPhone.mCT.getUtInterface() 1047 .queryCOLP(obtainMessage(EVENT_SUPP_SVC_QUERY_COMPLETE, this)); 1048 } catch (ImsException e) { 1049 Rlog.d(LOG_TAG, "processCode: Could not get UT handle for queryCOLP."); 1050 } 1051 } else if (isActivate() || isDeactivate()) { 1052 try { 1053 mPhone.mCT.getUtInterface().updateCOLP(isActivate(), 1054 obtainMessage(EVENT_SET_COMPLETE, this)); 1055 } catch (ImsException e) { 1056 Rlog.d(LOG_TAG, "processCode: Could not get UT handle for updateCOLP."); 1057 } 1058 } else { 1059 throw new RuntimeException ("Invalid or Unsupported MMI Code"); 1060 } 1061 } else if (mSc != null && mSc.equals(SC_COLR)) { 1062 // NOTE: Refer to the note above. 1063 if (isActivate()) { 1064 try { 1065 mPhone.mCT.getUtInterface().updateCOLR(NUM_PRESENTATION_RESTRICTED, 1066 obtainMessage(EVENT_SET_COMPLETE, this)); 1067 } catch (ImsException e) { 1068 Rlog.d(LOG_TAG, "processCode: Could not get UT handle for updateCOLR."); 1069 } 1070 } else if (isDeactivate()) { 1071 try { 1072 mPhone.mCT.getUtInterface().updateCOLR(NUM_PRESENTATION_ALLOWED, 1073 obtainMessage(EVENT_SET_COMPLETE, this)); 1074 } catch (ImsException e) { 1075 Rlog.d(LOG_TAG, "processCode: Could not get UT handle for updateCOLR."); 1076 } 1077 } else if (isInterrogate()) { 1078 try { 1079 mPhone.mCT.getUtInterface() 1080 .queryCOLR(obtainMessage(EVENT_SUPP_SVC_QUERY_COMPLETE, this)); 1081 } catch (ImsException e) { 1082 Rlog.d(LOG_TAG, "processCode: Could not get UT handle for queryCOLR."); 1083 } 1084 } else { 1085 throw new RuntimeException ("Invalid or Unsupported MMI Code"); 1086 } 1087 } else if (mSc != null && (mSc.equals(SC_BS_MT))) { 1088 try { 1089 if (isInterrogate()) { 1090 mPhone.mCT.getUtInterface().queryCallBarring( 1091 ImsUtImplBase.CALL_BARRING_SPECIFIC_INCOMING_CALLS, 1092 obtainMessage(EVENT_QUERY_ICB_COMPLETE, this)); 1093 } else { 1094 processIcbMmiCodeForUpdate(); 1095 } 1096 // TODO: isRegister() case needs to be handled. 1097 } catch (ImsException e) { 1098 Rlog.d(LOG_TAG, "processCode: Could not get UT handle for ICB."); 1099 } 1100 } else if (mSc != null && mSc.equals(SC_BAICa)) { 1101 int callAction =0; 1102 // TODO: Should we route through queryCallBarring() here? 1103 try { 1104 if (isInterrogate()) { 1105 mPhone.mCT.getUtInterface().queryCallBarring( 1106 ImsUtImplBase.CALL_BARRING_ANONYMOUS_INCOMING, 1107 obtainMessage(EVENT_QUERY_ICB_COMPLETE, this)); 1108 } else { 1109 if (isActivate()) { 1110 callAction = CommandsInterface.CF_ACTION_ENABLE; 1111 } else if (isDeactivate()) { 1112 callAction = CommandsInterface.CF_ACTION_DISABLE; 1113 } 1114 mPhone.mCT.getUtInterface() 1115 .updateCallBarring(ImsUtImplBase.CALL_BARRING_ANONYMOUS_INCOMING, 1116 callAction, 1117 obtainMessage(EVENT_SET_COMPLETE,this), 1118 null); 1119 } 1120 } catch (ImsException e) { 1121 Rlog.d(LOG_TAG, "processCode: Could not get UT handle for ICBa."); 1122 } 1123 } else if (mSc != null && mSc.equals(SC_WAIT)) { 1124 // sia = basic service group 1125 int serviceClass = siToServiceClass(mSia); 1126 1127 if (isActivate() || isDeactivate()) { 1128 if (serviceClass == SERVICE_CLASS_NONE 1129 || (serviceClass & SERVICE_CLASS_VOICE) == SERVICE_CLASS_VOICE) { 1130 if (mPhone.getTerminalBasedCallWaitingState(false) 1131 != CallWaitingController.TERMINAL_BASED_NOT_SUPPORTED) { 1132 mPhone.getDefaultPhone().setCallWaiting(isActivate(), serviceClass, 1133 obtainMessage(EVENT_SET_COMPLETE, this)); 1134 return; 1135 } 1136 } 1137 mPhone.setCallWaiting(isActivate(), serviceClass, 1138 obtainMessage(EVENT_SET_COMPLETE, this)); 1139 } else if (isInterrogate()) { 1140 if (mPhone.getTerminalBasedCallWaitingState(false) 1141 != CallWaitingController.TERMINAL_BASED_NOT_SUPPORTED) { 1142 mPhone.getDefaultPhone() 1143 .getCallWaiting(obtainMessage(EVENT_QUERY_COMPLETE, this)); 1144 } else { 1145 mPhone.getCallWaiting(obtainMessage(EVENT_QUERY_COMPLETE, this)); 1146 } 1147 } else { 1148 throw new RuntimeException ("Invalid or Unsupported MMI Code"); 1149 } 1150 } else if (mPoundString != null) { 1151 if (isUssdOverImsAllowed()) { 1152 Rlog.i(LOG_TAG, "processCode: Sending ussd string '" 1153 + Rlog.pii(LOG_TAG, mPoundString) + "' over IMS pipe."); 1154 sendUssd(mPoundString); 1155 } else { 1156 Rlog.i(LOG_TAG, "processCode: Sending ussd string '" 1157 + Rlog.pii(LOG_TAG, mPoundString) + "' over CS pipe."); 1158 throw new CallStateException(Phone.CS_FALLBACK); 1159 } 1160 } else { 1161 Rlog.d(LOG_TAG, "processCode: invalid or unsupported MMI"); 1162 throw new RuntimeException ("Invalid or Unsupported MMI Code"); 1163 } 1164 } catch (RuntimeException exc) { 1165 mState = State.FAILED; 1166 mMessage = mContext.getText(com.android.internal.R.string.mmiError); 1167 Rlog.d(LOG_TAG, "processCode: RuntimeException = " + exc); 1168 mPhone.onMMIDone(this); 1169 } 1170 } 1171 getCfnryTime()1172 private int getCfnryTime() { 1173 CarrierConfigManager configManager = mContext.getSystemService(CarrierConfigManager.class); 1174 if (configManager == null) { 1175 return DEFAULT_NO_REPLY_TIMER_FOR_CFNRY; 1176 } 1177 1178 PersistableBundle carrierConfig = configManager.getConfigForSubId(mPhone.getSubId()); 1179 if (carrierConfig == null 1180 || !carrierConfig.getBoolean( 1181 CarrierConfigManager.KEY_SUPPORT_NO_REPLY_TIMER_FOR_CFNRY_BOOL, true)) { 1182 return DEFAULT_NO_REPLY_TIMER_FOR_CFNRY; 1183 } 1184 1185 int time = carrierConfig.getInt( 1186 CarrierConfigManager.KEY_NO_REPLY_TIMER_FOR_CFNRY_SEC_INT, 1187 DEFAULT_NO_REPLY_TIMER_FOR_CFNRY); 1188 1189 Rlog.d(LOG_TAG, "getCfnryTime: " + time); 1190 return time; 1191 } 1192 isUssdOverImsAllowed()1193 private boolean isUssdOverImsAllowed() { 1194 if (mContext.getResources().getBoolean( 1195 com.android.internal.R.bool.config_allow_ussd_over_ims)) { 1196 int ussd_method = getIntCarrierConfig( 1197 CarrierConfigManager.KEY_CARRIER_USSD_METHOD_INT); 1198 1199 switch (ussd_method) { 1200 case USSD_OVER_CS_PREFERRED: 1201 // We'll normally send USSD over the CS pipe, but if it happens that 1202 // the CS phone is out of service, we'll just try over IMS instead. 1203 if (mPhone.getDefaultPhone().getServiceStateTracker().mSS.getState() 1204 == STATE_IN_SERVICE) { 1205 return false; 1206 } else { 1207 Rlog.i(LOG_TAG, "isUssdOverImsAllowed: CS is out of service"); 1208 return true; 1209 } 1210 case USSD_OVER_IMS_PREFERRED: 1211 case USSD_OVER_IMS_ONLY: 1212 return true; 1213 case USSD_OVER_CS_ONLY: 1214 return false; 1215 default: 1216 Rlog.i(LOG_TAG, "isUssdOverImsAllowed: Unsupported method"); 1217 return false; 1218 } 1219 } else { 1220 // USSD codes are not supported over IMS due to modem limitations; send over 1221 // the CS pipe instead. This should be fixed in the future. 1222 Rlog.i(LOG_TAG, "isUssdOverImsAllowed: USSD over IMS pipe is not supported."); 1223 return false; 1224 } 1225 } 1226 1227 /** 1228 * Called from ImsPhone 1229 * 1230 * An unsolicited USSD NOTIFY or REQUEST has come in matching 1231 * up with this pending USSD request 1232 * 1233 * Note: If REQUEST, this exchange is complete, but the session remains 1234 * active (ie, the network expects user input). 1235 */ 1236 void onUssdFinished(String ussdMessage, boolean isUssdRequest)1237 onUssdFinished(String ussdMessage, boolean isUssdRequest) { 1238 if (mState == State.PENDING) { 1239 if (TextUtils.isEmpty(ussdMessage)) { 1240 mMessage = mContext.getText(com.android.internal.R.string.mmiComplete); 1241 Rlog.v(LOG_TAG, "onUssdFinished: no message; using: " + mMessage); 1242 } else { 1243 Rlog.v(LOG_TAG, "onUssdFinished: message: " + ussdMessage); 1244 mMessage = ussdMessage; 1245 } 1246 mIsUssdRequest = isUssdRequest; 1247 // If it's a request, leave it PENDING so that it's cancelable. 1248 if (!isUssdRequest) { 1249 mState = State.COMPLETE; 1250 } 1251 mPhone.onMMIDone(this); 1252 } 1253 } 1254 1255 /** 1256 * Called from ImsPhone 1257 * 1258 * The radio has reset, and this is still pending 1259 */ onUssdFinishedError()1260 public void onUssdFinishedError() { 1261 if (mState == State.PENDING) { 1262 mState = State.FAILED; 1263 if (TextUtils.isEmpty(mMessage)) { 1264 mMessage = mContext.getText(com.android.internal.R.string.mmiError); 1265 } 1266 Rlog.d(LOG_TAG, "onUssdFinishedError: mmi=" + this); 1267 mPhone.onMMIDone(this); 1268 } 1269 } 1270 sendUssd(String ussdMessage)1271 void sendUssd(String ussdMessage) { 1272 // Treat this as a USSD string 1273 mIsPendingUSSD = true; 1274 1275 // Note that unlike most everything else, the USSD complete 1276 // response does not complete this MMI code...we wait for 1277 // an unsolicited USSD "Notify" or "Request". 1278 // The matching up of this is done in ImsPhone. 1279 1280 mPhone.sendUSSD(ussdMessage, 1281 obtainMessage(EVENT_USSD_COMPLETE, this)); 1282 } 1283 1284 /** Called from ImsPhone.handleMessage; not a Handler subclass */ 1285 @Override 1286 public void handleMessage(Message msg)1287 handleMessage (Message msg) { 1288 AsyncResult ar; 1289 1290 switch (msg.what) { 1291 case EVENT_SET_COMPLETE: 1292 ar = (AsyncResult) (msg.obj); 1293 1294 onSetComplete(msg, ar); 1295 break; 1296 1297 case EVENT_SET_CFF_COMPLETE: 1298 ar = (AsyncResult) (msg.obj); 1299 1300 /* 1301 * msg.arg1 = 1 means to set unconditional voice call forwarding 1302 * msg.arg2 = 1 means to enable voice call forwarding 1303 */ 1304 if ((ar.exception == null) && (msg.arg1 == 1)) { 1305 boolean cffEnabled = (msg.arg2 == 1); 1306 if (mIccRecords != null) { 1307 mPhone.setVoiceCallForwardingFlag(mIccRecords, 1308 1, cffEnabled, mDialingNumber); 1309 } 1310 } 1311 1312 onSetComplete(msg, ar); 1313 break; 1314 1315 case EVENT_QUERY_CF_COMPLETE: 1316 ar = (AsyncResult) (msg.obj); 1317 onQueryCfComplete(ar); 1318 break; 1319 1320 case EVENT_QUERY_COMPLETE: 1321 ar = (AsyncResult) (msg.obj); 1322 onQueryComplete(ar); 1323 break; 1324 1325 case EVENT_USSD_COMPLETE: 1326 ar = (AsyncResult) (msg.obj); 1327 1328 if (ar.exception != null) { 1329 mState = State.FAILED; 1330 mMessage = getErrorMessage(ar); 1331 1332 mPhone.onMMIDone(this); 1333 } 1334 1335 // Note that unlike most everything else, the USSD complete 1336 // response does not complete this MMI code...we wait for 1337 // an unsolicited USSD "Notify" or "Request". 1338 // The matching up of this is done in ImsPhone. 1339 1340 break; 1341 1342 case EVENT_USSD_CANCEL_COMPLETE: 1343 mPhone.onMMIDone(this); 1344 break; 1345 1346 case EVENT_SUPP_SVC_QUERY_COMPLETE: 1347 ar = (AsyncResult) (msg.obj); 1348 onSuppSvcQueryComplete(ar); 1349 break; 1350 1351 case EVENT_QUERY_ICB_COMPLETE: 1352 ar = (AsyncResult) (msg.obj); 1353 onIcbQueryComplete(ar); 1354 break; 1355 1356 case EVENT_GET_CLIR_COMPLETE: 1357 ar = (AsyncResult) (msg.obj); 1358 onQueryClirComplete(ar); 1359 break; 1360 1361 default: 1362 break; 1363 } 1364 } 1365 1366 //***** Private instance methods 1367 1368 private void processIcbMmiCodeForUpdate()1369 processIcbMmiCodeForUpdate () { 1370 String dialingNumber = mSia; 1371 String[] icbNum = null; 1372 int callAction; 1373 if (dialingNumber != null) { 1374 icbNum = dialingNumber.split("\\$"); 1375 } 1376 callAction = callBarAction(dialingNumber); 1377 1378 try { 1379 mPhone.mCT.getUtInterface().updateCallBarring( 1380 ImsUtImplBase.CALL_BARRING_SPECIFIC_INCOMING_CALLS, 1381 callAction, 1382 obtainMessage(EVENT_SET_COMPLETE, this), 1383 icbNum); 1384 } catch (ImsException e) { 1385 Rlog.d(LOG_TAG, "processIcbMmiCodeForUpdate:Could not get UT handle for updating ICB."); 1386 } 1387 } 1388 1389 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) getErrorMessage(AsyncResult ar)1390 private CharSequence getErrorMessage(AsyncResult ar) { 1391 CharSequence errorMessage; 1392 return ((errorMessage = getMmiErrorMessage(ar)) != null) ? errorMessage : 1393 mContext.getText(com.android.internal.R.string.mmiError); 1394 } 1395 1396 @VisibleForTesting getMmiErrorMessage(AsyncResult ar)1397 public CharSequence getMmiErrorMessage(AsyncResult ar) { 1398 if (ar.exception instanceof ImsException) { 1399 switch (((ImsException) ar.exception).getCode()) { 1400 case ImsReasonInfo.CODE_FDN_BLOCKED: 1401 return mContext.getText(com.android.internal.R.string.mmiFdnError); 1402 case ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_DIAL: 1403 return mContext.getText(com.android.internal.R.string.stk_cc_ss_to_dial); 1404 case ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_USSD: 1405 return mContext.getText(com.android.internal.R.string.stk_cc_ss_to_ussd); 1406 case ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_SS: 1407 return mContext.getText(com.android.internal.R.string.stk_cc_ss_to_ss); 1408 case ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_DIAL_VIDEO: 1409 return mContext.getText(com.android.internal.R.string.stk_cc_ss_to_dial_video); 1410 default: 1411 return null; 1412 } 1413 } else if (ar.exception instanceof CommandException) { 1414 CommandException err = (CommandException) ar.exception; 1415 if (err.getCommandError() == CommandException.Error.FDN_CHECK_FAILURE) { 1416 return mContext.getText(com.android.internal.R.string.mmiFdnError); 1417 } else if (err.getCommandError() == CommandException.Error.SS_MODIFIED_TO_DIAL) { 1418 return mContext.getText(com.android.internal.R.string.stk_cc_ss_to_dial); 1419 } else if (err.getCommandError() == CommandException.Error.SS_MODIFIED_TO_USSD) { 1420 return mContext.getText(com.android.internal.R.string.stk_cc_ss_to_ussd); 1421 } else if (err.getCommandError() == CommandException.Error.SS_MODIFIED_TO_SS) { 1422 return mContext.getText(com.android.internal.R.string.stk_cc_ss_to_ss); 1423 } else if (err.getCommandError() == CommandException.Error.SS_MODIFIED_TO_DIAL_VIDEO) { 1424 return mContext.getText(com.android.internal.R.string.stk_cc_ss_to_dial_video); 1425 } else if (err.getCommandError() == CommandException.Error.INTERNAL_ERR) { 1426 return mContext.getText(com.android.internal.R.string.mmiError); 1427 } else if (err.getCommandError() == CommandException.Error.REQUEST_NOT_SUPPORTED 1428 || err.getCommandError() == CommandException.Error.OPERATION_NOT_ALLOWED) { 1429 // getResources().getText() is the same as getText(), however getText() is final and 1430 // cannot be mocked in tests. 1431 return mContext.getResources().getText( 1432 com.android.internal.R.string.mmiErrorNotSupported); 1433 } 1434 } 1435 return null; 1436 } 1437 1438 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) getScString()1439 private CharSequence getScString() { 1440 if (mSc != null) { 1441 if (isServiceCodeCallBarring(mSc)) { 1442 return mContext.getText(com.android.internal.R.string.BaMmi); 1443 } else if (isServiceCodeCallForwarding(mSc)) { 1444 return mContext.getText(com.android.internal.R.string.CfMmi); 1445 } else if (mSc.equals(SC_PWD)) { 1446 return mContext.getText(com.android.internal.R.string.PwdMmi); 1447 } else if (mSc.equals(SC_WAIT)) { 1448 return mContext.getText(com.android.internal.R.string.CwMmi); 1449 } else if (mSc.equals(SC_CLIP)) { 1450 return mContext.getText(com.android.internal.R.string.ClipMmi); 1451 } else if (mSc.equals(SC_CLIR)) { 1452 return mContext.getText(com.android.internal.R.string.ClirMmi); 1453 } else if (mSc.equals(SC_COLP)) { 1454 return mContext.getText(com.android.internal.R.string.ColpMmi); 1455 } else if (mSc.equals(SC_COLR)) { 1456 return mContext.getText(com.android.internal.R.string.ColrMmi); 1457 } else if (mSc.equals(SC_BS_MT)) { 1458 return IcbDnMmi; 1459 } else if (mSc.equals(SC_BAICa)) { 1460 return IcbAnonymousMmi; 1461 } 1462 } 1463 1464 return ""; 1465 } 1466 1467 private void onSetComplete(Message msg, AsyncResult ar)1468 onSetComplete(Message msg, AsyncResult ar){ 1469 StringBuilder sb = new StringBuilder(getScString()); 1470 sb.append("\n"); 1471 1472 if (ar.exception != null) { 1473 mState = State.FAILED; 1474 1475 if (ar.exception instanceof CommandException) { 1476 CommandException err = (CommandException) ar.exception; 1477 CharSequence errorMessage; 1478 if (err.getCommandError() == CommandException.Error.PASSWORD_INCORRECT) { 1479 sb.append(mContext.getText( 1480 com.android.internal.R.string.passwordIncorrect)); 1481 } else if ((errorMessage = getMmiErrorMessage(ar)) != null) { 1482 sb.append(errorMessage); 1483 } else if (err.getMessage() != null) { 1484 sb.append(err.getMessage()); 1485 } else { 1486 sb.append(mContext.getText(com.android.internal.R.string.mmiError)); 1487 } 1488 } else if (ar.exception instanceof ImsException) { 1489 sb.append(getImsErrorMessage(ar)); 1490 } 1491 } else if (ar.result != null && ar.result instanceof Integer 1492 && (int) ar.result == CommandsInterface.SS_STATUS_UNKNOWN) { 1493 mState = State.FAILED; 1494 sb = null; 1495 } else if (isActivate()) { 1496 mState = State.COMPLETE; 1497 if (mIsCallFwdReg) { 1498 sb.append(mContext.getText( 1499 com.android.internal.R.string.serviceRegistered)); 1500 } else { 1501 sb.append(mContext.getText( 1502 com.android.internal.R.string.serviceEnabled)); 1503 } 1504 // Record CLIR setting 1505 if (mSc.equals(SC_CLIR)) { 1506 mPhone.saveClirSetting(CommandsInterface.CLIR_INVOCATION); 1507 } 1508 } else if (isDeactivate()) { 1509 mState = State.COMPLETE; 1510 sb.append(mContext.getText( 1511 com.android.internal.R.string.serviceDisabled)); 1512 // Record CLIR setting 1513 if (mSc.equals(SC_CLIR)) { 1514 mPhone.saveClirSetting(CommandsInterface.CLIR_SUPPRESSION); 1515 } 1516 } else if (isRegister()) { 1517 mState = State.COMPLETE; 1518 sb.append(mContext.getText( 1519 com.android.internal.R.string.serviceRegistered)); 1520 } else if (isErasure()) { 1521 mState = State.COMPLETE; 1522 sb.append(mContext.getText( 1523 com.android.internal.R.string.serviceErased)); 1524 } else { 1525 mState = State.FAILED; 1526 sb.append(mContext.getText( 1527 com.android.internal.R.string.mmiError)); 1528 } 1529 1530 mMessage = sb; 1531 Rlog.d(LOG_TAG, "onSetComplete: mmi=" + this); 1532 mPhone.onMMIDone(this); 1533 } 1534 1535 /** 1536 * @param serviceClass 1 bit of the service class bit vectory 1537 * @return String to be used for call forward query MMI response text. 1538 * Returns null if unrecognized 1539 */ 1540 1541 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 1542 private CharSequence serviceClassToCFString(int serviceClass)1543 serviceClassToCFString (int serviceClass) { 1544 switch (serviceClass) { 1545 case SERVICE_CLASS_VOICE: 1546 return mContext.getText(com.android.internal.R.string.serviceClassVoice); 1547 case SERVICE_CLASS_DATA: 1548 return mContext.getText(com.android.internal.R.string.serviceClassData); 1549 case SERVICE_CLASS_FAX: 1550 return mContext.getText(com.android.internal.R.string.serviceClassFAX); 1551 case SERVICE_CLASS_SMS: 1552 return mContext.getText(com.android.internal.R.string.serviceClassSMS); 1553 case SERVICE_CLASS_DATA_SYNC: 1554 return mContext.getText(com.android.internal.R.string.serviceClassDataSync); 1555 case SERVICE_CLASS_DATA_ASYNC: 1556 return mContext.getText(com.android.internal.R.string.serviceClassDataAsync); 1557 case SERVICE_CLASS_PACKET: 1558 return mContext.getText(com.android.internal.R.string.serviceClassPacket); 1559 case SERVICE_CLASS_PAD: 1560 return mContext.getText(com.android.internal.R.string.serviceClassPAD); 1561 default: 1562 return null; 1563 } 1564 } 1565 1566 /** one CallForwardInfo + serviceClassMask -> one line of text */ 1567 private CharSequence makeCFQueryResultMessage(CallForwardInfo info, int serviceClassMask)1568 makeCFQueryResultMessage(CallForwardInfo info, int serviceClassMask) { 1569 CharSequence template; 1570 String sources[] = {"{0}", "{1}", "{2}"}; 1571 CharSequence destinations[] = new CharSequence[3]; 1572 boolean needTimeTemplate; 1573 1574 // CF_REASON_NO_REPLY also has a time value associated with 1575 // it. All others don't. 1576 1577 needTimeTemplate = 1578 (info.reason == CommandsInterface.CF_REASON_NO_REPLY); 1579 1580 if (info.status == 1) { 1581 if (needTimeTemplate) { 1582 template = mContext.getText( 1583 com.android.internal.R.string.cfTemplateForwardedTime); 1584 } else { 1585 template = mContext.getText( 1586 com.android.internal.R.string.cfTemplateForwarded); 1587 } 1588 } else if (info.status == 0 && isEmptyOrNull(info.number)) { 1589 template = mContext.getText( 1590 com.android.internal.R.string.cfTemplateNotForwarded); 1591 } else { /* (info.status == 0) && !isEmptyOrNull(info.number) */ 1592 // A call forward record that is not active but contains 1593 // a phone number is considered "registered" 1594 1595 if (needTimeTemplate) { 1596 template = mContext.getText( 1597 com.android.internal.R.string.cfTemplateRegisteredTime); 1598 } else { 1599 template = mContext.getText( 1600 com.android.internal.R.string.cfTemplateRegistered); 1601 } 1602 } 1603 1604 // In the template (from strings.xmls) 1605 // {0} is one of "bearerServiceCode*" 1606 // {1} is dialing number 1607 // {2} is time in seconds 1608 1609 destinations[0] = serviceClassToCFString(info.serviceClass & serviceClassMask); 1610 destinations[1] = PhoneNumberUtils.stringFromStringAndTOA(info.number, info.toa); 1611 destinations[2] = Integer.toString(info.timeSeconds); 1612 1613 if (info.reason == CommandsInterface.CF_REASON_UNCONDITIONAL && 1614 (info.serviceClass & serviceClassMask) 1615 == CommandsInterface.SERVICE_CLASS_VOICE) { 1616 boolean cffEnabled = (info.status == 1); 1617 mPhone.setVoiceCallForwardingFlag(mIccRecords, 1, cffEnabled, info.number); 1618 } 1619 1620 return TextUtils.replace(template, sources, destinations); 1621 } 1622 1623 1624 private void onQueryCfComplete(AsyncResult ar)1625 onQueryCfComplete(AsyncResult ar) { 1626 StringBuilder sb = new StringBuilder(getScString()); 1627 sb.append("\n"); 1628 1629 if (ar.exception != null) { 1630 mState = State.FAILED; 1631 1632 if (ar.exception instanceof ImsException) { 1633 sb.append(getImsErrorMessage(ar)); 1634 } 1635 else { 1636 sb.append(getErrorMessage(ar)); 1637 } 1638 } else if (ar.result instanceof CallForwardInfo[] && 1639 ((CallForwardInfo[]) ar.result)[0].status 1640 == CommandsInterface.SS_STATUS_UNKNOWN) { 1641 sb = null; 1642 mState = State.FAILED; 1643 } else { 1644 CallForwardInfo infos[]; 1645 1646 infos = (CallForwardInfo[]) ar.result; 1647 1648 if (infos == null || infos.length == 0) { 1649 // Assume the default is not active 1650 sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled)); 1651 1652 // Set unconditional CFF in SIM to false 1653 mPhone.setVoiceCallForwardingFlag(mIccRecords, 1, false, null); 1654 } else { 1655 1656 SpannableStringBuilder tb = new SpannableStringBuilder(); 1657 1658 // Each bit in the service class gets its own result line 1659 // The service classes may be split up over multiple 1660 // CallForwardInfos. So, for each service class, find out 1661 // which CallForwardInfo represents it and then build 1662 // the response text based on that 1663 1664 for (int serviceClassMask = 1 1665 ; serviceClassMask <= SERVICE_CLASS_MAX 1666 ; serviceClassMask <<= 1 1667 ) { 1668 for (int i = 0, s = infos.length; i < s ; i++) { 1669 if ((serviceClassMask & infos[i].serviceClass) != 0) { 1670 tb.append(makeCFQueryResultMessage(infos[i], 1671 serviceClassMask)); 1672 tb.append("\n"); 1673 } 1674 } 1675 } 1676 sb.append(tb); 1677 } 1678 1679 mState = State.COMPLETE; 1680 } 1681 1682 mMessage = sb; 1683 Rlog.d(LOG_TAG, "onQueryCfComplete: mmi=" + this); 1684 mPhone.onMMIDone(this); 1685 1686 } 1687 onSuppSvcQueryComplete(AsyncResult ar)1688 private void onSuppSvcQueryComplete(AsyncResult ar) { 1689 StringBuilder sb = new StringBuilder(getScString()); 1690 sb.append("\n"); 1691 1692 mState = State.FAILED; 1693 if (ar.exception != null) { 1694 if (ar.exception instanceof ImsException) { 1695 sb.append(getImsErrorMessage(ar)); 1696 } else { 1697 sb.append(getErrorMessage(ar)); 1698 } 1699 } else { 1700 if (ar.result instanceof ImsSsInfo) { 1701 Rlog.d(LOG_TAG, "onSuppSvcQueryComplete: Received CLIP/COLP/COLR Response."); 1702 // Response for CLIP, COLP and COLR queries. 1703 ImsSsInfo ssInfo = (ImsSsInfo) ar.result; 1704 if (ssInfo != null) { 1705 Rlog.d(LOG_TAG, 1706 "onSuppSvcQueryComplete: ImsSsInfo mStatus = " + ssInfo.getStatus()); 1707 if (ssInfo.getProvisionStatus() == ImsSsInfo.SERVICE_NOT_PROVISIONED) { 1708 sb.append(mContext.getText( 1709 com.android.internal.R.string.serviceNotProvisioned)); 1710 mState = State.COMPLETE; 1711 } else if (ssInfo.getStatus() == ImsSsInfo.DISABLED) { 1712 sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled)); 1713 mState = State.COMPLETE; 1714 } else if (ssInfo.getStatus() == ImsSsInfo.ENABLED) { 1715 sb.append(mContext.getText(com.android.internal.R.string.serviceEnabled)); 1716 mState = State.COMPLETE; 1717 } else { 1718 sb.append(mContext.getText(com.android.internal.R.string.mmiError)); 1719 } 1720 } else { 1721 sb.append(mContext.getText(com.android.internal.R.string.mmiError)); 1722 } 1723 1724 } else { 1725 Rlog.d(LOG_TAG, 1726 "onSuppSvcQueryComplete: Received Call Barring/CSFB CLIP Response."); 1727 // Response for Call Barring and CSFB CLIP queries. 1728 int[] infos = (int[]) ar.result; 1729 if (infos == null || infos.length == 0) { 1730 sb.append(mContext.getText(com.android.internal.R.string.mmiError)); 1731 } else { 1732 if (infos[0] != 0) { 1733 sb.append(mContext.getText(com.android.internal.R.string.serviceEnabled)); 1734 mState = State.COMPLETE; 1735 } else { 1736 sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled)); 1737 mState = State.COMPLETE; 1738 } 1739 } 1740 } 1741 } 1742 1743 mMessage = sb; 1744 Rlog.d(LOG_TAG, "onSuppSvcQueryComplete mmi=" + this); 1745 mPhone.onMMIDone(this); 1746 } 1747 onIcbQueryComplete(AsyncResult ar)1748 private void onIcbQueryComplete(AsyncResult ar) { 1749 Rlog.d(LOG_TAG, "onIcbQueryComplete mmi=" + this); 1750 StringBuilder sb = new StringBuilder(getScString()); 1751 sb.append("\n"); 1752 1753 if (ar.exception != null) { 1754 mState = State.FAILED; 1755 1756 if (ar.exception instanceof ImsException) { 1757 sb.append(getImsErrorMessage(ar)); 1758 } else { 1759 sb.append(getErrorMessage(ar)); 1760 } 1761 } else { 1762 List<ImsSsInfo> infos = null; 1763 try { 1764 infos = (List<ImsSsInfo>) ar.result; 1765 } catch (ClassCastException cce) { 1766 // TODO in R: #157# still has ImsSsInfo[] type, fix the type in IImsUtListener.aidl. 1767 infos = Arrays.asList((ImsSsInfo[]) ar.result); 1768 } 1769 if (infos == null || infos.size() == 0) { 1770 sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled)); 1771 } else { 1772 ImsSsInfo info; 1773 for (int i = 0, s = infos.size(); i < s; i++) { 1774 info = infos.get(i); 1775 if (info.getIncomingCommunicationBarringNumber() != null) { 1776 sb.append("Num: " + info.getIncomingCommunicationBarringNumber() 1777 + " status: " + info.getStatus() + "\n"); 1778 } else if (info.getStatus() == 1) { 1779 sb.append(mContext.getText(com.android.internal 1780 .R.string.serviceEnabled)); 1781 } else { 1782 sb.append(mContext.getText(com.android.internal 1783 .R.string.serviceDisabled)); 1784 } 1785 } 1786 } 1787 mState = State.COMPLETE; 1788 } 1789 mMessage = sb; 1790 mPhone.onMMIDone(this); 1791 } 1792 onQueryClirComplete(AsyncResult ar)1793 private void onQueryClirComplete(AsyncResult ar) { 1794 StringBuilder sb = new StringBuilder(getScString()); 1795 sb.append("\n"); 1796 mState = State.FAILED; 1797 1798 if (ar.exception != null) { 1799 if (ar.exception instanceof ImsException) { 1800 sb.append(getImsErrorMessage(ar)); 1801 } else { 1802 sb.append(getErrorMessage(ar)); 1803 } 1804 } else { 1805 int[] clirInfo = (int[]) ar.result; 1806 // ssInfo.getClirOutgoingState() = The 'n' parameter from TS 27.007 7.7 1807 // ssInfo.getClirInterrogationStatus() = The 'm' parameter from TS 27.007 7.7 1808 Rlog.d(LOG_TAG, "onQueryClirComplete: CLIR param n=" + clirInfo[0] 1809 + " m=" + clirInfo[1]); 1810 1811 // 'm' parameter. 1812 switch (clirInfo[1]) { 1813 case ImsSsInfo.CLIR_STATUS_NOT_PROVISIONED: 1814 sb.append(mContext.getText( 1815 com.android.internal.R.string.serviceNotProvisioned)); 1816 mState = State.COMPLETE; 1817 break; 1818 case ImsSsInfo.CLIR_STATUS_PROVISIONED_PERMANENT: 1819 sb.append(mContext.getText( 1820 com.android.internal.R.string.CLIRPermanent)); 1821 mState = State.COMPLETE; 1822 break; 1823 case ImsSsInfo.CLIR_STATUS_TEMPORARILY_RESTRICTED: 1824 // 'n' parameter. 1825 switch (clirInfo[0]) { 1826 case ImsSsInfo.CLIR_OUTGOING_DEFAULT: 1827 sb.append(mContext.getText( 1828 com.android.internal.R.string.CLIRDefaultOnNextCallOn)); 1829 mState = State.COMPLETE; 1830 break; 1831 case ImsSsInfo.CLIR_OUTGOING_INVOCATION: 1832 sb.append(mContext.getText( 1833 com.android.internal.R.string.CLIRDefaultOnNextCallOn)); 1834 mState = State.COMPLETE; 1835 break; 1836 case ImsSsInfo.CLIR_OUTGOING_SUPPRESSION: 1837 sb.append(mContext.getText( 1838 com.android.internal.R.string.CLIRDefaultOnNextCallOff)); 1839 mState = State.COMPLETE; 1840 break; 1841 default: 1842 sb.append(mContext.getText( 1843 com.android.internal.R.string.mmiError)); 1844 mState = State.FAILED; 1845 } 1846 break; 1847 case ImsSsInfo.CLIR_STATUS_TEMPORARILY_ALLOWED: 1848 // 'n' parameter. 1849 switch (clirInfo[0]) { 1850 case ImsSsInfo.CLIR_OUTGOING_DEFAULT: 1851 sb.append(mContext.getText( 1852 com.android.internal.R.string.CLIRDefaultOffNextCallOff)); 1853 mState = State.COMPLETE; 1854 break; 1855 case ImsSsInfo.CLIR_OUTGOING_INVOCATION: 1856 sb.append(mContext.getText( 1857 com.android.internal.R.string.CLIRDefaultOffNextCallOn)); 1858 mState = State.COMPLETE; 1859 break; 1860 case ImsSsInfo.CLIR_OUTGOING_SUPPRESSION: 1861 sb.append(mContext.getText( 1862 com.android.internal.R.string.CLIRDefaultOffNextCallOff)); 1863 mState = State.COMPLETE; 1864 break; 1865 default: 1866 sb.append(mContext.getText( 1867 com.android.internal.R.string.mmiError)); 1868 mState = State.FAILED; 1869 } 1870 break; 1871 default: 1872 sb.append(mContext.getText( 1873 com.android.internal.R.string.mmiError)); 1874 mState = State.FAILED; 1875 } 1876 } 1877 1878 mMessage = sb; 1879 Rlog.d(LOG_TAG, "onQueryClirComplete mmi=" + this); 1880 mPhone.onMMIDone(this); 1881 } 1882 1883 private void onQueryComplete(AsyncResult ar)1884 onQueryComplete(AsyncResult ar) { 1885 StringBuilder sb = new StringBuilder(getScString()); 1886 sb.append("\n"); 1887 1888 mState = State.FAILED; 1889 if (ar.exception != null) { 1890 if (ar.exception instanceof ImsException) { 1891 sb.append(getImsErrorMessage(ar)); 1892 } else { 1893 sb.append(getErrorMessage(ar)); 1894 } 1895 } else if ((ar.result instanceof int[]) && 1896 ((int[])ar.result)[0] == CommandsInterface.SS_STATUS_UNKNOWN) { 1897 sb = null; 1898 } else { 1899 int[] ints = (int[])ar.result; 1900 1901 if (ints != null && ints.length != 0) { 1902 if (ints[0] == 0) { 1903 sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled)); 1904 mState = State.COMPLETE; 1905 } else if (mSc.equals(SC_WAIT)) { 1906 // Call Waiting includes additional data in the response. 1907 sb.append(createQueryCallWaitingResultMessage(ints[1])); 1908 mState = State.COMPLETE; 1909 } else if (ints[0] == 1) { 1910 // for all other services, treat it as a boolean 1911 sb.append(mContext.getText(com.android.internal.R.string.serviceEnabled)); 1912 mState = State.COMPLETE; 1913 } else { 1914 sb.append(mContext.getText(com.android.internal.R.string.mmiError)); 1915 } 1916 } else { 1917 sb.append(mContext.getText(com.android.internal.R.string.mmiError)); 1918 } 1919 } 1920 1921 mMessage = sb; 1922 Rlog.d(LOG_TAG, "onQueryComplete mmi=" + this); 1923 mPhone.onMMIDone(this); 1924 } 1925 1926 private CharSequence createQueryCallWaitingResultMessage(int serviceClass)1927 createQueryCallWaitingResultMessage(int serviceClass) { 1928 StringBuilder sb = new StringBuilder( 1929 mContext.getText(com.android.internal.R.string.serviceEnabledFor)); 1930 1931 for (int classMask = 1 1932 ; classMask <= SERVICE_CLASS_MAX 1933 ; classMask <<= 1 1934 ) { 1935 if ((classMask & serviceClass) != 0) { 1936 sb.append("\n"); 1937 sb.append(serviceClassToCFString(classMask & serviceClass)); 1938 } 1939 } 1940 return sb; 1941 } 1942 getImsErrorMessage(AsyncResult ar)1943 private CharSequence getImsErrorMessage(AsyncResult ar) { 1944 ImsException error = (ImsException) ar.exception; 1945 CharSequence errorMessage; 1946 if ((errorMessage = getMmiErrorMessage(ar)) != null) { 1947 return errorMessage; 1948 } else if (error.getMessage() != null) { 1949 return error.getMessage(); 1950 } else { 1951 return getErrorMessage(ar); 1952 } 1953 } 1954 1955 /** 1956 * Get the int config from carrier config manager. 1957 * 1958 * @param key config key defined in CarrierConfigManager 1959 * @return integer value of corresponding key. 1960 */ getIntCarrierConfig(String key)1961 private int getIntCarrierConfig(String key) { 1962 CarrierConfigManager ConfigManager = 1963 (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE); 1964 PersistableBundle b = null; 1965 if (ConfigManager != null) { 1966 // If an invalid subId is used, this bundle will contain default values. 1967 b = ConfigManager.getConfigForSubId(mPhone.getSubId()); 1968 } 1969 if (b != null) { 1970 return b.getInt(key); 1971 } else { 1972 // Return static default defined in CarrierConfigManager. 1973 return CarrierConfigManager.getDefaultConfig().getInt(key); 1974 } 1975 } 1976 1977 @Override getUssdCallbackReceiver()1978 public ResultReceiver getUssdCallbackReceiver() { 1979 return this.mCallbackReceiver; 1980 } 1981 1982 /** 1983 * Process IMS SS Data received. 1984 */ processImsSsData(AsyncResult data)1985 public void processImsSsData(AsyncResult data) throws ImsException { 1986 try { 1987 ImsSsData ssData = (ImsSsData) data.result; 1988 parseSsData(ssData); 1989 } catch (ClassCastException | NullPointerException ex) { 1990 throw new ImsException("Exception in parsing SS Data", 0); 1991 } 1992 } 1993 parseSsData(ImsSsData ssData)1994 void parseSsData(ImsSsData ssData) { 1995 ImsException ex = (ssData.getResult() != ImsSsData.RESULT_SUCCESS) 1996 ? new ImsException(null, ssData.getResult()) : null; 1997 mSc = getScStringFromScType(ssData.getServiceType()); 1998 mAction = getActionStringFromReqType(ssData.getRequestType()); 1999 Rlog.d(LOG_TAG, "parseSsData msc = " + mSc + ", action = " + mAction + ", ex = " + ex); 2000 2001 switch (ssData.getRequestType()) { 2002 case ImsSsData.SS_ACTIVATION: 2003 case ImsSsData.SS_DEACTIVATION: 2004 case ImsSsData.SS_REGISTRATION: 2005 case ImsSsData.SS_ERASURE: 2006 if ((ssData.getResult() == ImsSsData.RESULT_SUCCESS) 2007 && ssData.isTypeUnConditional()) { 2008 /* 2009 * When ssData.serviceType is unconditional (SS_CFU or SS_CF_ALL) and 2010 * ssData.requestType is activate/register and 2011 * ServiceClass is Voice/Video/None, turn on voice call forwarding. 2012 */ 2013 boolean cffEnabled = ((ssData.getRequestType() == ImsSsData.SS_ACTIVATION 2014 || ssData.getRequestType() == ImsSsData.SS_REGISTRATION) 2015 && isServiceClassVoiceVideoOrNone(ssData.getServiceClass())); 2016 2017 Rlog.d(LOG_TAG, "setCallForwardingFlag cffEnabled: " + cffEnabled); 2018 if (mIccRecords != null) { 2019 Rlog.d(LOG_TAG, "setVoiceCallForwardingFlag done from SS Info."); 2020 //Only CF status is set here as part of activation/registration, 2021 //number is not available until interrogation. 2022 mPhone.setVoiceCallForwardingFlag(1, cffEnabled, null); 2023 } else { 2024 Rlog.e(LOG_TAG, "setCallForwardingFlag aborted. sim records is null."); 2025 } 2026 } 2027 onSetComplete(null, new AsyncResult(null, ssData.getCallForwardInfo(), ex)); 2028 break; 2029 case ImsSsData.SS_INTERROGATION: 2030 if (ssData.isTypeClir()) { 2031 Rlog.d(LOG_TAG, "CLIR INTERROGATION"); 2032 onQueryClirComplete(new AsyncResult(null, ssData.getSuppServiceInfoCompat(), ex)); 2033 } else if (ssData.isTypeCF()) { 2034 Rlog.d(LOG_TAG, "CALL FORWARD INTERROGATION"); 2035 // Have to translate to an array, since the modem still returns it in the 2036 // ImsCallForwardInfo[] format. 2037 List<ImsCallForwardInfo> mCfInfos = ssData.getCallForwardInfo(); 2038 ImsCallForwardInfo[] mCfInfosCompat = null; 2039 if (mCfInfos != null) { 2040 mCfInfosCompat = new ImsCallForwardInfo[mCfInfos.size()]; 2041 mCfInfosCompat = mCfInfos.toArray(mCfInfosCompat); 2042 } 2043 onQueryCfComplete(new AsyncResult(null, mPhone.handleCfQueryResult( 2044 mCfInfosCompat), ex)); 2045 } else if (ssData.isTypeBarring()) { 2046 onSuppSvcQueryComplete(new AsyncResult(null, ssData.getSuppServiceInfoCompat(), 2047 ex)); 2048 } else if (ssData.isTypeColr() || ssData.isTypeClip() || ssData.isTypeColp()) { 2049 onSuppSvcQueryComplete(new AsyncResult(null, ssData.getSuppServiceInfo().get(0), 2050 ex)); 2051 } else if (ssData.isTypeIcb()) { 2052 onIcbQueryComplete(new AsyncResult(null, ssData.getSuppServiceInfo(), ex)); 2053 } else { 2054 onQueryComplete(new AsyncResult(null, ssData.getSuppServiceInfoCompat(), ex)); 2055 } 2056 break; 2057 default: 2058 Rlog.e(LOG_TAG, "Invaid requestType in SSData : " + ssData.getRequestType()); 2059 break; 2060 } 2061 } 2062 getScStringFromScType(int serviceType)2063 private String getScStringFromScType(int serviceType) { 2064 switch (serviceType) { 2065 case ImsSsData.SS_CFU: 2066 return SC_CFU; 2067 case ImsSsData.SS_CF_BUSY: 2068 return SC_CFB; 2069 case ImsSsData.SS_CF_NO_REPLY: 2070 return SC_CFNRy; 2071 case ImsSsData.SS_CF_NOT_REACHABLE: 2072 return SC_CFNR; 2073 case ImsSsData.SS_CF_ALL: 2074 return SC_CF_All; 2075 case ImsSsData.SS_CF_ALL_CONDITIONAL: 2076 return SC_CF_All_Conditional; 2077 case ImsSsData.SS_CLIP: 2078 return SC_CLIP; 2079 case ImsSsData.SS_CLIR: 2080 return SC_CLIR; 2081 case ImsSsData.SS_COLP: 2082 return SC_COLP; 2083 case ImsSsData.SS_COLR: 2084 return SC_COLR; 2085 case ImsSsData.SS_CNAP: 2086 return SC_CNAP; 2087 case ImsSsData.SS_WAIT: 2088 return SC_WAIT; 2089 case ImsSsData.SS_BAOC: 2090 return SC_BAOC; 2091 case ImsSsData.SS_BAOIC: 2092 return SC_BAOIC; 2093 case ImsSsData.SS_BAOIC_EXC_HOME: 2094 return SC_BAOICxH; 2095 case ImsSsData.SS_BAIC: 2096 return SC_BAIC; 2097 case ImsSsData.SS_BAIC_ROAMING: 2098 return SC_BAICr; 2099 case ImsSsData.SS_ALL_BARRING: 2100 return SC_BA_ALL; 2101 case ImsSsData.SS_OUTGOING_BARRING: 2102 return SC_BA_MO; 2103 case ImsSsData.SS_INCOMING_BARRING: 2104 return SC_BA_MT; 2105 case ImsSsData.SS_INCOMING_BARRING_DN: 2106 return SC_BS_MT; 2107 case ImsSsData.SS_INCOMING_BARRING_ANONYMOUS: 2108 return SC_BAICa; 2109 default: 2110 return null; 2111 } 2112 } 2113 getActionStringFromReqType(int requestType)2114 private String getActionStringFromReqType(int requestType) { 2115 switch (requestType) { 2116 case ImsSsData.SS_ACTIVATION: 2117 return ACTION_ACTIVATE; 2118 case ImsSsData.SS_DEACTIVATION: 2119 return ACTION_DEACTIVATE; 2120 case ImsSsData.SS_INTERROGATION: 2121 return ACTION_INTERROGATE; 2122 case ImsSsData.SS_REGISTRATION: 2123 return ACTION_REGISTER; 2124 case ImsSsData.SS_ERASURE: 2125 return ACTION_ERASURE; 2126 default: 2127 return null; 2128 } 2129 } 2130 isServiceClassVoiceVideoOrNone(int serviceClass)2131 private boolean isServiceClassVoiceVideoOrNone(int serviceClass) { 2132 return ((serviceClass == SERVICE_CLASS_NONE) || (serviceClass == SERVICE_CLASS_VOICE) 2133 || (serviceClass == (SERVICE_CLASS_PACKET + SERVICE_CLASS_DATA_SYNC))); 2134 } 2135 isSsInfo()2136 public boolean isSsInfo() { 2137 return mIsSsInfo; 2138 } 2139 setIsSsInfo(boolean isSsInfo)2140 public void setIsSsInfo(boolean isSsInfo) { 2141 mIsSsInfo = isSsInfo; 2142 } 2143 2144 /*** 2145 * TODO: It would be nice to have a method here that can take in a dialstring and 2146 * figure out if there is an MMI code embedded within it. This code would replace 2147 * some of the string parsing functionality in the Phone App's 2148 * SpecialCharSequenceMgr class. 2149 */ 2150 2151 @Override toString()2152 public String toString() { 2153 StringBuilder sb = new StringBuilder("ImsPhoneMmiCode {"); 2154 2155 sb.append("State=" + getState()); 2156 if (mAction != null) sb.append(" action=" + mAction); 2157 if (mSc != null) sb.append(" sc=" + mSc); 2158 if (mSia != null) sb.append(" sia=" + mSia); 2159 if (mSib != null) sb.append(" sib=" + mSib); 2160 if (mSic != null) sb.append(" sic=" + mSic); 2161 if (mPoundString != null) sb.append(" poundString=" + Rlog.pii(LOG_TAG, mPoundString)); 2162 if (mDialingNumber != null) sb.append(" dialingNumber=" 2163 + Rlog.pii(LOG_TAG, mDialingNumber)); 2164 if (mPwd != null) sb.append(" pwd=" + Rlog.pii(LOG_TAG, mPwd)); 2165 if (mCallbackReceiver != null) sb.append(" hasReceiver"); 2166 sb.append("}"); 2167 return sb.toString(); 2168 } 2169 2170 @Override isNetworkInitiatedUssd()2171 public boolean isNetworkInitiatedUssd() { 2172 return mIsNetworkInitiatedUSSD; 2173 } 2174 } 2175