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