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