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 if (isUssdOverImsAllowed()) { 895 Rlog.d(LOG_TAG, "processCode: Sending short code '" 896 + mDialingNumber + "' over IMS pipe."); 897 sendUssd(mDialingNumber); 898 } else { 899 Rlog.d(LOG_TAG, "processCode: Sending short code '" 900 + mDialingNumber + "' over CS pipe."); 901 throw new CallStateException(Phone.CS_FALLBACK); 902 } 903 } else if (isServiceCodeCallForwarding(mSc)) { 904 Rlog.d(LOG_TAG, "processCode: is CF"); 905 906 String dialingNumber = mSia; 907 int reason = scToCallForwardReason(mSc); 908 int serviceClass = siToServiceClass(mSib); 909 int time = siToTime(mSic); 910 911 if (isInterrogate()) { 912 mPhone.getCallForwardingOption(reason, serviceClass, 913 obtainMessage(EVENT_QUERY_CF_COMPLETE, this)); 914 } else { 915 int cfAction; 916 917 if (isActivate()) { 918 // 3GPP TS 22.030 6.5.2 919 // a call forwarding request with a single * would be 920 // interpreted as registration if containing a forwarded-to 921 // number, or an activation if not 922 if (isEmptyOrNull(dialingNumber)) { 923 cfAction = CommandsInterface.CF_ACTION_ENABLE; 924 mIsCallFwdReg = false; 925 } else { 926 cfAction = CommandsInterface.CF_ACTION_REGISTRATION; 927 mIsCallFwdReg = true; 928 } 929 } else if (isDeactivate()) { 930 cfAction = CommandsInterface.CF_ACTION_DISABLE; 931 } else if (isRegister()) { 932 cfAction = CommandsInterface.CF_ACTION_REGISTRATION; 933 } else if (isErasure()) { 934 cfAction = CommandsInterface.CF_ACTION_ERASURE; 935 } else { 936 throw new RuntimeException ("invalid action"); 937 } 938 939 int isSettingUnconditional = 940 ((reason == CommandsInterface.CF_REASON_UNCONDITIONAL) || 941 (reason == CommandsInterface.CF_REASON_ALL)) ? 1 : 0; 942 943 int isEnableDesired = 944 ((cfAction == CommandsInterface.CF_ACTION_ENABLE) || 945 (cfAction == CommandsInterface.CF_ACTION_REGISTRATION)) ? 1 : 0; 946 947 Rlog.d(LOG_TAG, "processCode: is CF setCallForward"); 948 mPhone.setCallForwardingOption(cfAction, reason, 949 dialingNumber, serviceClass, time, obtainMessage( 950 EVENT_SET_CFF_COMPLETE, 951 isSettingUnconditional, 952 isEnableDesired, this)); 953 } 954 } else if (isServiceCodeCallBarring(mSc)) { 955 // sia = password 956 // sib = basic service group 957 // service group is not supported 958 959 String password = mSia; 960 String facility = scToBarringFacility(mSc); 961 int serviceClass = siToServiceClass(mSib); 962 963 if (isInterrogate()) { 964 mPhone.getCallBarring(facility, 965 obtainMessage(EVENT_SUPP_SVC_QUERY_COMPLETE, this), serviceClass); 966 } else if (isActivate() || isDeactivate()) { 967 mPhone.setCallBarring(facility, isActivate(), password, 968 obtainMessage(EVENT_SET_COMPLETE, this), serviceClass); 969 } else { 970 throw new RuntimeException ("Invalid or Unsupported MMI Code"); 971 } 972 } else if (mSc != null && mSc.equals(SC_CLIR)) { 973 // NOTE: Since these supplementary services are accessed only 974 // via MMI codes, methods have not been added to ImsPhone. 975 // Only the UT interface handle is used. 976 if (isActivate() 977 && !mPhone.getDefaultPhone().isClirActivationAndDeactivationPrevented()) { 978 try { 979 mPhone.setOutgoingCallerIdDisplay(CommandsInterface.CLIR_INVOCATION, 980 obtainMessage(EVENT_SET_COMPLETE, this)); 981 } catch (Exception e) { 982 Rlog.d(LOG_TAG, "processCode: Could not get UT handle for updateCLIR."); 983 } 984 } else if (isDeactivate() 985 && !mPhone.getDefaultPhone().isClirActivationAndDeactivationPrevented()) { 986 try { 987 mPhone.setOutgoingCallerIdDisplay(CommandsInterface.CLIR_SUPPRESSION, 988 obtainMessage(EVENT_SET_COMPLETE, this)); 989 } catch (Exception e) { 990 Rlog.d(LOG_TAG, "processCode: Could not get UT handle for updateCLIR."); 991 } 992 } else if (isInterrogate()) { 993 try { 994 mPhone.getOutgoingCallerIdDisplay(obtainMessage(EVENT_GET_CLIR_COMPLETE, this)); 995 } catch (Exception e) { 996 Rlog.d(LOG_TAG, "processCode: Could not get UT handle for queryCLIR."); 997 } 998 } else { 999 throw new RuntimeException ("Invalid or Unsupported MMI Code"); 1000 } 1001 } else if (mSc != null && mSc.equals(SC_CLIP)) { 1002 // NOTE: Refer to the note above. 1003 if (isInterrogate()) { 1004 try { 1005 mPhone.queryCLIP(obtainMessage(EVENT_SUPP_SVC_QUERY_COMPLETE, this)); 1006 } catch (Exception e) { 1007 Rlog.d(LOG_TAG, "processCode: Could not get UT handle for queryCLIP."); 1008 } 1009 } else if (isActivate() || isDeactivate()) { 1010 try { 1011 mPhone.mCT.getUtInterface().updateCLIP(isActivate(), 1012 obtainMessage(EVENT_SET_COMPLETE, this)); 1013 } catch (ImsException e) { 1014 Rlog.d(LOG_TAG, "processCode: Could not get UT handle for updateCLIP."); 1015 } 1016 } else { 1017 throw new RuntimeException ("Invalid or Unsupported MMI Code"); 1018 } 1019 } else if (mSc != null && mSc.equals(SC_COLP)) { 1020 // NOTE: Refer to the note above. 1021 if (isInterrogate()) { 1022 try { 1023 mPhone.mCT.getUtInterface() 1024 .queryCOLP(obtainMessage(EVENT_SUPP_SVC_QUERY_COMPLETE, this)); 1025 } catch (ImsException e) { 1026 Rlog.d(LOG_TAG, "processCode: Could not get UT handle for queryCOLP."); 1027 } 1028 } else if (isActivate() || isDeactivate()) { 1029 try { 1030 mPhone.mCT.getUtInterface().updateCOLP(isActivate(), 1031 obtainMessage(EVENT_SET_COMPLETE, this)); 1032 } catch (ImsException e) { 1033 Rlog.d(LOG_TAG, "processCode: Could not get UT handle for updateCOLP."); 1034 } 1035 } else { 1036 throw new RuntimeException ("Invalid or Unsupported MMI Code"); 1037 } 1038 } else if (mSc != null && mSc.equals(SC_COLR)) { 1039 // NOTE: Refer to the note above. 1040 if (isActivate()) { 1041 try { 1042 mPhone.mCT.getUtInterface().updateCOLR(NUM_PRESENTATION_RESTRICTED, 1043 obtainMessage(EVENT_SET_COMPLETE, this)); 1044 } catch (ImsException e) { 1045 Rlog.d(LOG_TAG, "processCode: Could not get UT handle for updateCOLR."); 1046 } 1047 } else if (isDeactivate()) { 1048 try { 1049 mPhone.mCT.getUtInterface().updateCOLR(NUM_PRESENTATION_ALLOWED, 1050 obtainMessage(EVENT_SET_COMPLETE, this)); 1051 } catch (ImsException e) { 1052 Rlog.d(LOG_TAG, "processCode: Could not get UT handle for updateCOLR."); 1053 } 1054 } else if (isInterrogate()) { 1055 try { 1056 mPhone.mCT.getUtInterface() 1057 .queryCOLR(obtainMessage(EVENT_SUPP_SVC_QUERY_COMPLETE, this)); 1058 } catch (ImsException e) { 1059 Rlog.d(LOG_TAG, "processCode: Could not get UT handle for queryCOLR."); 1060 } 1061 } else { 1062 throw new RuntimeException ("Invalid or Unsupported MMI Code"); 1063 } 1064 } else if (mSc != null && (mSc.equals(SC_BS_MT))) { 1065 try { 1066 if (isInterrogate()) { 1067 mPhone.mCT.getUtInterface().queryCallBarring( 1068 ImsUtImplBase.CALL_BARRING_SPECIFIC_INCOMING_CALLS, 1069 obtainMessage(EVENT_QUERY_ICB_COMPLETE, this)); 1070 } else { 1071 processIcbMmiCodeForUpdate(); 1072 } 1073 // TODO: isRegister() case needs to be handled. 1074 } catch (ImsException e) { 1075 Rlog.d(LOG_TAG, "processCode: Could not get UT handle for ICB."); 1076 } 1077 } else if (mSc != null && mSc.equals(SC_BAICa)) { 1078 int callAction =0; 1079 // TODO: Should we route through queryCallBarring() here? 1080 try { 1081 if (isInterrogate()) { 1082 mPhone.mCT.getUtInterface().queryCallBarring( 1083 ImsUtImplBase.CALL_BARRING_ANONYMOUS_INCOMING, 1084 obtainMessage(EVENT_QUERY_ICB_COMPLETE, this)); 1085 } else { 1086 if (isActivate()) { 1087 callAction = CommandsInterface.CF_ACTION_ENABLE; 1088 } else if (isDeactivate()) { 1089 callAction = CommandsInterface.CF_ACTION_DISABLE; 1090 } 1091 mPhone.mCT.getUtInterface() 1092 .updateCallBarring(ImsUtImplBase.CALL_BARRING_ANONYMOUS_INCOMING, 1093 callAction, 1094 obtainMessage(EVENT_SET_COMPLETE,this), 1095 null); 1096 } 1097 } catch (ImsException e) { 1098 Rlog.d(LOG_TAG, "processCode: Could not get UT handle for ICBa."); 1099 } 1100 } else if (mSc != null && mSc.equals(SC_WAIT)) { 1101 // sia = basic service group 1102 int serviceClass = siToServiceClass(mSia); 1103 1104 if (isActivate() || isDeactivate()) { 1105 mPhone.setCallWaiting(isActivate(), serviceClass, 1106 obtainMessage(EVENT_SET_COMPLETE, this)); 1107 } else if (isInterrogate()) { 1108 mPhone.getCallWaiting(obtainMessage(EVENT_QUERY_COMPLETE, this)); 1109 } else { 1110 throw new RuntimeException ("Invalid or Unsupported MMI Code"); 1111 } 1112 } else if (mPoundString != null) { 1113 if (isUssdOverImsAllowed()) { 1114 Rlog.i(LOG_TAG, "processCode: Sending ussd string '" 1115 + Rlog.pii(LOG_TAG, mPoundString) + "' over IMS pipe."); 1116 sendUssd(mPoundString); 1117 } else { 1118 Rlog.i(LOG_TAG, "processCode: Sending ussd string '" 1119 + Rlog.pii(LOG_TAG, mPoundString) + "' over CS pipe."); 1120 throw new CallStateException(Phone.CS_FALLBACK); 1121 } 1122 } else { 1123 Rlog.d(LOG_TAG, "processCode: invalid or unsupported MMI"); 1124 throw new RuntimeException ("Invalid or Unsupported MMI Code"); 1125 } 1126 } catch (RuntimeException exc) { 1127 mState = State.FAILED; 1128 mMessage = mContext.getText(com.android.internal.R.string.mmiError); 1129 Rlog.d(LOG_TAG, "processCode: RuntimeException = " + exc); 1130 mPhone.onMMIDone(this); 1131 } 1132 } 1133 isUssdOverImsAllowed()1134 private boolean isUssdOverImsAllowed() { 1135 if (mContext.getResources().getBoolean( 1136 com.android.internal.R.bool.config_allow_ussd_over_ims)) { 1137 int ussd_method = getIntCarrierConfig( 1138 CarrierConfigManager.KEY_CARRIER_USSD_METHOD_INT); 1139 1140 switch (ussd_method) { 1141 case USSD_OVER_CS_PREFERRED: 1142 // We'll normally send USSD over the CS pipe, but if it happens that 1143 // the CS phone is out of service, we'll just try over IMS instead. 1144 if (mPhone.getDefaultPhone().getServiceStateTracker().mSS.getState() 1145 == STATE_IN_SERVICE) { 1146 return false; 1147 } else { 1148 Rlog.i(LOG_TAG, "isUssdOverImsAllowed: CS is out of service"); 1149 return true; 1150 } 1151 case USSD_OVER_IMS_PREFERRED: 1152 case USSD_OVER_IMS_ONLY: 1153 return true; 1154 case USSD_OVER_CS_ONLY: 1155 return false; 1156 default: 1157 Rlog.i(LOG_TAG, "isUssdOverImsAllowed: Unsupported method"); 1158 return false; 1159 } 1160 } else { 1161 // USSD codes are not supported over IMS due to modem limitations; send over 1162 // the CS pipe instead. This should be fixed in the future. 1163 Rlog.i(LOG_TAG, "isUssdOverImsAllowed: USSD over IMS pipe is not supported."); 1164 return false; 1165 } 1166 } 1167 1168 /** 1169 * Called from ImsPhone 1170 * 1171 * An unsolicited USSD NOTIFY or REQUEST has come in matching 1172 * up with this pending USSD request 1173 * 1174 * Note: If REQUEST, this exchange is complete, but the session remains 1175 * active (ie, the network expects user input). 1176 */ 1177 void onUssdFinished(String ussdMessage, boolean isUssdRequest)1178 onUssdFinished(String ussdMessage, boolean isUssdRequest) { 1179 if (mState == State.PENDING) { 1180 if (TextUtils.isEmpty(ussdMessage)) { 1181 mMessage = mContext.getText(com.android.internal.R.string.mmiComplete); 1182 Rlog.v(LOG_TAG, "onUssdFinished: no message; using: " + mMessage); 1183 } else { 1184 Rlog.v(LOG_TAG, "onUssdFinished: message: " + ussdMessage); 1185 mMessage = ussdMessage; 1186 } 1187 mIsUssdRequest = isUssdRequest; 1188 // If it's a request, leave it PENDING so that it's cancelable. 1189 if (!isUssdRequest) { 1190 mState = State.COMPLETE; 1191 } 1192 mPhone.onMMIDone(this); 1193 } 1194 } 1195 1196 /** 1197 * Called from ImsPhone 1198 * 1199 * The radio has reset, and this is still pending 1200 */ onUssdFinishedError()1201 public void onUssdFinishedError() { 1202 if (mState == State.PENDING) { 1203 mState = State.FAILED; 1204 if (TextUtils.isEmpty(mMessage)) { 1205 mMessage = mContext.getText(com.android.internal.R.string.mmiError); 1206 } 1207 Rlog.d(LOG_TAG, "onUssdFinishedError: mmi=" + this); 1208 mPhone.onMMIDone(this); 1209 } 1210 } 1211 sendUssd(String ussdMessage)1212 void sendUssd(String ussdMessage) { 1213 // Treat this as a USSD string 1214 mIsPendingUSSD = true; 1215 1216 // Note that unlike most everything else, the USSD complete 1217 // response does not complete this MMI code...we wait for 1218 // an unsolicited USSD "Notify" or "Request". 1219 // The matching up of this is done in ImsPhone. 1220 1221 mPhone.sendUSSD(ussdMessage, 1222 obtainMessage(EVENT_USSD_COMPLETE, this)); 1223 } 1224 1225 /** Called from ImsPhone.handleMessage; not a Handler subclass */ 1226 @Override 1227 public void handleMessage(Message msg)1228 handleMessage (Message msg) { 1229 AsyncResult ar; 1230 1231 switch (msg.what) { 1232 case EVENT_SET_COMPLETE: 1233 ar = (AsyncResult) (msg.obj); 1234 1235 onSetComplete(msg, ar); 1236 break; 1237 1238 case EVENT_SET_CFF_COMPLETE: 1239 ar = (AsyncResult) (msg.obj); 1240 1241 /* 1242 * msg.arg1 = 1 means to set unconditional voice call forwarding 1243 * msg.arg2 = 1 means to enable voice call forwarding 1244 */ 1245 if ((ar.exception == null) && (msg.arg1 == 1)) { 1246 boolean cffEnabled = (msg.arg2 == 1); 1247 if (mIccRecords != null) { 1248 mPhone.setVoiceCallForwardingFlag(mIccRecords, 1249 1, cffEnabled, mDialingNumber); 1250 } 1251 } 1252 1253 onSetComplete(msg, ar); 1254 break; 1255 1256 case EVENT_QUERY_CF_COMPLETE: 1257 ar = (AsyncResult) (msg.obj); 1258 onQueryCfComplete(ar); 1259 break; 1260 1261 case EVENT_QUERY_COMPLETE: 1262 ar = (AsyncResult) (msg.obj); 1263 onQueryComplete(ar); 1264 break; 1265 1266 case EVENT_USSD_COMPLETE: 1267 ar = (AsyncResult) (msg.obj); 1268 1269 if (ar.exception != null) { 1270 mState = State.FAILED; 1271 mMessage = getErrorMessage(ar); 1272 1273 mPhone.onMMIDone(this); 1274 } 1275 1276 // Note that unlike most everything else, the USSD complete 1277 // response does not complete this MMI code...we wait for 1278 // an unsolicited USSD "Notify" or "Request". 1279 // The matching up of this is done in ImsPhone. 1280 1281 break; 1282 1283 case EVENT_USSD_CANCEL_COMPLETE: 1284 mPhone.onMMIDone(this); 1285 break; 1286 1287 case EVENT_SUPP_SVC_QUERY_COMPLETE: 1288 ar = (AsyncResult) (msg.obj); 1289 onSuppSvcQueryComplete(ar); 1290 break; 1291 1292 case EVENT_QUERY_ICB_COMPLETE: 1293 ar = (AsyncResult) (msg.obj); 1294 onIcbQueryComplete(ar); 1295 break; 1296 1297 case EVENT_GET_CLIR_COMPLETE: 1298 ar = (AsyncResult) (msg.obj); 1299 onQueryClirComplete(ar); 1300 break; 1301 1302 default: 1303 break; 1304 } 1305 } 1306 1307 //***** Private instance methods 1308 1309 private void processIcbMmiCodeForUpdate()1310 processIcbMmiCodeForUpdate () { 1311 String dialingNumber = mSia; 1312 String[] icbNum = null; 1313 int callAction; 1314 if (dialingNumber != null) { 1315 icbNum = dialingNumber.split("\\$"); 1316 } 1317 callAction = callBarAction(dialingNumber); 1318 1319 try { 1320 mPhone.mCT.getUtInterface().updateCallBarring( 1321 ImsUtImplBase.CALL_BARRING_SPECIFIC_INCOMING_CALLS, 1322 callAction, 1323 obtainMessage(EVENT_SET_COMPLETE, this), 1324 icbNum); 1325 } catch (ImsException e) { 1326 Rlog.d(LOG_TAG, "processIcbMmiCodeForUpdate:Could not get UT handle for updating ICB."); 1327 } 1328 } 1329 1330 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) getErrorMessage(AsyncResult ar)1331 private CharSequence getErrorMessage(AsyncResult ar) { 1332 CharSequence errorMessage; 1333 return ((errorMessage = getMmiErrorMessage(ar)) != null) ? errorMessage : 1334 mContext.getText(com.android.internal.R.string.mmiError); 1335 } 1336 1337 @VisibleForTesting getMmiErrorMessage(AsyncResult ar)1338 public CharSequence getMmiErrorMessage(AsyncResult ar) { 1339 if (ar.exception instanceof ImsException) { 1340 switch (((ImsException) ar.exception).getCode()) { 1341 case ImsReasonInfo.CODE_FDN_BLOCKED: 1342 return mContext.getText(com.android.internal.R.string.mmiFdnError); 1343 case ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_DIAL: 1344 return mContext.getText(com.android.internal.R.string.stk_cc_ss_to_dial); 1345 case ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_USSD: 1346 return mContext.getText(com.android.internal.R.string.stk_cc_ss_to_ussd); 1347 case ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_SS: 1348 return mContext.getText(com.android.internal.R.string.stk_cc_ss_to_ss); 1349 case ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_DIAL_VIDEO: 1350 return mContext.getText(com.android.internal.R.string.stk_cc_ss_to_dial_video); 1351 default: 1352 return null; 1353 } 1354 } else if (ar.exception instanceof CommandException) { 1355 CommandException err = (CommandException) ar.exception; 1356 if (err.getCommandError() == CommandException.Error.FDN_CHECK_FAILURE) { 1357 return mContext.getText(com.android.internal.R.string.mmiFdnError); 1358 } else if (err.getCommandError() == CommandException.Error.SS_MODIFIED_TO_DIAL) { 1359 return mContext.getText(com.android.internal.R.string.stk_cc_ss_to_dial); 1360 } else if (err.getCommandError() == CommandException.Error.SS_MODIFIED_TO_USSD) { 1361 return mContext.getText(com.android.internal.R.string.stk_cc_ss_to_ussd); 1362 } else if (err.getCommandError() == CommandException.Error.SS_MODIFIED_TO_SS) { 1363 return mContext.getText(com.android.internal.R.string.stk_cc_ss_to_ss); 1364 } else if (err.getCommandError() == CommandException.Error.SS_MODIFIED_TO_DIAL_VIDEO) { 1365 return mContext.getText(com.android.internal.R.string.stk_cc_ss_to_dial_video); 1366 } else if (err.getCommandError() == CommandException.Error.INTERNAL_ERR) { 1367 return mContext.getText(com.android.internal.R.string.mmiError); 1368 } else if (err.getCommandError() == CommandException.Error.REQUEST_NOT_SUPPORTED 1369 || err.getCommandError() == CommandException.Error.OPERATION_NOT_ALLOWED) { 1370 // getResources().getText() is the same as getText(), however getText() is final and 1371 // cannot be mocked in tests. 1372 return mContext.getResources().getText( 1373 com.android.internal.R.string.mmiErrorNotSupported); 1374 } 1375 } 1376 return null; 1377 } 1378 1379 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) getScString()1380 private CharSequence getScString() { 1381 if (mSc != null) { 1382 if (isServiceCodeCallBarring(mSc)) { 1383 return mContext.getText(com.android.internal.R.string.BaMmi); 1384 } else if (isServiceCodeCallForwarding(mSc)) { 1385 return mContext.getText(com.android.internal.R.string.CfMmi); 1386 } else if (mSc.equals(SC_PWD)) { 1387 return mContext.getText(com.android.internal.R.string.PwdMmi); 1388 } else if (mSc.equals(SC_WAIT)) { 1389 return mContext.getText(com.android.internal.R.string.CwMmi); 1390 } else if (mSc.equals(SC_CLIP)) { 1391 return mContext.getText(com.android.internal.R.string.ClipMmi); 1392 } else if (mSc.equals(SC_CLIR)) { 1393 return mContext.getText(com.android.internal.R.string.ClirMmi); 1394 } else if (mSc.equals(SC_COLP)) { 1395 return mContext.getText(com.android.internal.R.string.ColpMmi); 1396 } else if (mSc.equals(SC_COLR)) { 1397 return mContext.getText(com.android.internal.R.string.ColrMmi); 1398 } else if (mSc.equals(SC_BS_MT)) { 1399 return IcbDnMmi; 1400 } else if (mSc.equals(SC_BAICa)) { 1401 return IcbAnonymousMmi; 1402 } 1403 } 1404 1405 return ""; 1406 } 1407 1408 private void onSetComplete(Message msg, AsyncResult ar)1409 onSetComplete(Message msg, AsyncResult ar){ 1410 StringBuilder sb = new StringBuilder(getScString()); 1411 sb.append("\n"); 1412 1413 if (ar.exception != null) { 1414 mState = State.FAILED; 1415 1416 if (ar.exception instanceof CommandException) { 1417 CommandException err = (CommandException) ar.exception; 1418 CharSequence errorMessage; 1419 if (err.getCommandError() == CommandException.Error.PASSWORD_INCORRECT) { 1420 sb.append(mContext.getText( 1421 com.android.internal.R.string.passwordIncorrect)); 1422 } else if ((errorMessage = getMmiErrorMessage(ar)) != null) { 1423 sb.append(errorMessage); 1424 } else if (err.getMessage() != null) { 1425 sb.append(err.getMessage()); 1426 } else { 1427 sb.append(mContext.getText(com.android.internal.R.string.mmiError)); 1428 } 1429 } else if (ar.exception instanceof ImsException) { 1430 sb.append(getImsErrorMessage(ar)); 1431 } 1432 } else if (ar.result != null && ar.result instanceof Integer 1433 && (int) ar.result == CommandsInterface.SS_STATUS_UNKNOWN) { 1434 mState = State.FAILED; 1435 sb = null; 1436 } else if (isActivate()) { 1437 mState = State.COMPLETE; 1438 if (mIsCallFwdReg) { 1439 sb.append(mContext.getText( 1440 com.android.internal.R.string.serviceRegistered)); 1441 } else { 1442 sb.append(mContext.getText( 1443 com.android.internal.R.string.serviceEnabled)); 1444 } 1445 // Record CLIR setting 1446 if (mSc.equals(SC_CLIR)) { 1447 mPhone.saveClirSetting(CommandsInterface.CLIR_INVOCATION); 1448 } 1449 } else if (isDeactivate()) { 1450 mState = State.COMPLETE; 1451 sb.append(mContext.getText( 1452 com.android.internal.R.string.serviceDisabled)); 1453 // Record CLIR setting 1454 if (mSc.equals(SC_CLIR)) { 1455 mPhone.saveClirSetting(CommandsInterface.CLIR_SUPPRESSION); 1456 } 1457 } else if (isRegister()) { 1458 mState = State.COMPLETE; 1459 sb.append(mContext.getText( 1460 com.android.internal.R.string.serviceRegistered)); 1461 } else if (isErasure()) { 1462 mState = State.COMPLETE; 1463 sb.append(mContext.getText( 1464 com.android.internal.R.string.serviceErased)); 1465 } else { 1466 mState = State.FAILED; 1467 sb.append(mContext.getText( 1468 com.android.internal.R.string.mmiError)); 1469 } 1470 1471 mMessage = sb; 1472 Rlog.d(LOG_TAG, "onSetComplete: mmi=" + this); 1473 mPhone.onMMIDone(this); 1474 } 1475 1476 /** 1477 * @param serviceClass 1 bit of the service class bit vectory 1478 * @return String to be used for call forward query MMI response text. 1479 * Returns null if unrecognized 1480 */ 1481 1482 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 1483 private CharSequence serviceClassToCFString(int serviceClass)1484 serviceClassToCFString (int serviceClass) { 1485 switch (serviceClass) { 1486 case SERVICE_CLASS_VOICE: 1487 return mContext.getText(com.android.internal.R.string.serviceClassVoice); 1488 case SERVICE_CLASS_DATA: 1489 return mContext.getText(com.android.internal.R.string.serviceClassData); 1490 case SERVICE_CLASS_FAX: 1491 return mContext.getText(com.android.internal.R.string.serviceClassFAX); 1492 case SERVICE_CLASS_SMS: 1493 return mContext.getText(com.android.internal.R.string.serviceClassSMS); 1494 case SERVICE_CLASS_DATA_SYNC: 1495 return mContext.getText(com.android.internal.R.string.serviceClassDataSync); 1496 case SERVICE_CLASS_DATA_ASYNC: 1497 return mContext.getText(com.android.internal.R.string.serviceClassDataAsync); 1498 case SERVICE_CLASS_PACKET: 1499 return mContext.getText(com.android.internal.R.string.serviceClassPacket); 1500 case SERVICE_CLASS_PAD: 1501 return mContext.getText(com.android.internal.R.string.serviceClassPAD); 1502 default: 1503 return null; 1504 } 1505 } 1506 1507 /** one CallForwardInfo + serviceClassMask -> one line of text */ 1508 private CharSequence makeCFQueryResultMessage(CallForwardInfo info, int serviceClassMask)1509 makeCFQueryResultMessage(CallForwardInfo info, int serviceClassMask) { 1510 CharSequence template; 1511 String sources[] = {"{0}", "{1}", "{2}"}; 1512 CharSequence destinations[] = new CharSequence[3]; 1513 boolean needTimeTemplate; 1514 1515 // CF_REASON_NO_REPLY also has a time value associated with 1516 // it. All others don't. 1517 1518 needTimeTemplate = 1519 (info.reason == CommandsInterface.CF_REASON_NO_REPLY); 1520 1521 if (info.status == 1) { 1522 if (needTimeTemplate) { 1523 template = mContext.getText( 1524 com.android.internal.R.string.cfTemplateForwardedTime); 1525 } else { 1526 template = mContext.getText( 1527 com.android.internal.R.string.cfTemplateForwarded); 1528 } 1529 } else if (info.status == 0 && isEmptyOrNull(info.number)) { 1530 template = mContext.getText( 1531 com.android.internal.R.string.cfTemplateNotForwarded); 1532 } else { /* (info.status == 0) && !isEmptyOrNull(info.number) */ 1533 // A call forward record that is not active but contains 1534 // a phone number is considered "registered" 1535 1536 if (needTimeTemplate) { 1537 template = mContext.getText( 1538 com.android.internal.R.string.cfTemplateRegisteredTime); 1539 } else { 1540 template = mContext.getText( 1541 com.android.internal.R.string.cfTemplateRegistered); 1542 } 1543 } 1544 1545 // In the template (from strings.xmls) 1546 // {0} is one of "bearerServiceCode*" 1547 // {1} is dialing number 1548 // {2} is time in seconds 1549 1550 destinations[0] = serviceClassToCFString(info.serviceClass & serviceClassMask); 1551 destinations[1] = PhoneNumberUtils.stringFromStringAndTOA(info.number, info.toa); 1552 destinations[2] = Integer.toString(info.timeSeconds); 1553 1554 if (info.reason == CommandsInterface.CF_REASON_UNCONDITIONAL && 1555 (info.serviceClass & serviceClassMask) 1556 == CommandsInterface.SERVICE_CLASS_VOICE) { 1557 boolean cffEnabled = (info.status == 1); 1558 mPhone.setVoiceCallForwardingFlag(mIccRecords, 1, cffEnabled, info.number); 1559 } 1560 1561 return TextUtils.replace(template, sources, destinations); 1562 } 1563 1564 1565 private void onQueryCfComplete(AsyncResult ar)1566 onQueryCfComplete(AsyncResult ar) { 1567 StringBuilder sb = new StringBuilder(getScString()); 1568 sb.append("\n"); 1569 1570 if (ar.exception != null) { 1571 mState = State.FAILED; 1572 1573 if (ar.exception instanceof ImsException) { 1574 sb.append(getImsErrorMessage(ar)); 1575 } 1576 else { 1577 sb.append(getErrorMessage(ar)); 1578 } 1579 } else if (ar.result instanceof CallForwardInfo[] && 1580 ((CallForwardInfo[]) ar.result)[0].status 1581 == CommandsInterface.SS_STATUS_UNKNOWN) { 1582 sb = null; 1583 mState = State.FAILED; 1584 } else { 1585 CallForwardInfo infos[]; 1586 1587 infos = (CallForwardInfo[]) ar.result; 1588 1589 if (infos == null || infos.length == 0) { 1590 // Assume the default is not active 1591 sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled)); 1592 1593 // Set unconditional CFF in SIM to false 1594 mPhone.setVoiceCallForwardingFlag(mIccRecords, 1, false, null); 1595 } else { 1596 1597 SpannableStringBuilder tb = new SpannableStringBuilder(); 1598 1599 // Each bit in the service class gets its own result line 1600 // The service classes may be split up over multiple 1601 // CallForwardInfos. So, for each service class, find out 1602 // which CallForwardInfo represents it and then build 1603 // the response text based on that 1604 1605 for (int serviceClassMask = 1 1606 ; serviceClassMask <= SERVICE_CLASS_MAX 1607 ; serviceClassMask <<= 1 1608 ) { 1609 for (int i = 0, s = infos.length; i < s ; i++) { 1610 if ((serviceClassMask & infos[i].serviceClass) != 0) { 1611 tb.append(makeCFQueryResultMessage(infos[i], 1612 serviceClassMask)); 1613 tb.append("\n"); 1614 } 1615 } 1616 } 1617 sb.append(tb); 1618 } 1619 1620 mState = State.COMPLETE; 1621 } 1622 1623 mMessage = sb; 1624 Rlog.d(LOG_TAG, "onQueryCfComplete: mmi=" + this); 1625 mPhone.onMMIDone(this); 1626 1627 } 1628 onSuppSvcQueryComplete(AsyncResult ar)1629 private void onSuppSvcQueryComplete(AsyncResult ar) { 1630 StringBuilder sb = new StringBuilder(getScString()); 1631 sb.append("\n"); 1632 1633 mState = State.FAILED; 1634 if (ar.exception != null) { 1635 if (ar.exception instanceof ImsException) { 1636 sb.append(getImsErrorMessage(ar)); 1637 } else { 1638 sb.append(getErrorMessage(ar)); 1639 } 1640 } else { 1641 if (ar.result instanceof ImsSsInfo) { 1642 Rlog.d(LOG_TAG, "onSuppSvcQueryComplete: Received CLIP/COLP/COLR Response."); 1643 // Response for CLIP, COLP and COLR queries. 1644 ImsSsInfo ssInfo = (ImsSsInfo) ar.result; 1645 if (ssInfo != null) { 1646 Rlog.d(LOG_TAG, 1647 "onSuppSvcQueryComplete: ImsSsInfo mStatus = " + ssInfo.getStatus()); 1648 if (ssInfo.getProvisionStatus() == ImsSsInfo.SERVICE_NOT_PROVISIONED) { 1649 sb.append(mContext.getText( 1650 com.android.internal.R.string.serviceNotProvisioned)); 1651 mState = State.COMPLETE; 1652 } else if (ssInfo.getStatus() == ImsSsInfo.DISABLED) { 1653 sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled)); 1654 mState = State.COMPLETE; 1655 } else if (ssInfo.getStatus() == ImsSsInfo.ENABLED) { 1656 sb.append(mContext.getText(com.android.internal.R.string.serviceEnabled)); 1657 mState = State.COMPLETE; 1658 } else { 1659 sb.append(mContext.getText(com.android.internal.R.string.mmiError)); 1660 } 1661 } else { 1662 sb.append(mContext.getText(com.android.internal.R.string.mmiError)); 1663 } 1664 1665 } else { 1666 Rlog.d(LOG_TAG, 1667 "onSuppSvcQueryComplete: Received Call Barring/CSFB CLIP Response."); 1668 // Response for Call Barring and CSFB CLIP queries. 1669 int[] infos = (int[]) ar.result; 1670 if (infos == null || infos.length == 0) { 1671 sb.append(mContext.getText(com.android.internal.R.string.mmiError)); 1672 } else { 1673 if (infos[0] != 0) { 1674 sb.append(mContext.getText(com.android.internal.R.string.serviceEnabled)); 1675 mState = State.COMPLETE; 1676 } else { 1677 sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled)); 1678 mState = State.COMPLETE; 1679 } 1680 } 1681 } 1682 } 1683 1684 mMessage = sb; 1685 Rlog.d(LOG_TAG, "onSuppSvcQueryComplete mmi=" + this); 1686 mPhone.onMMIDone(this); 1687 } 1688 onIcbQueryComplete(AsyncResult ar)1689 private void onIcbQueryComplete(AsyncResult ar) { 1690 Rlog.d(LOG_TAG, "onIcbQueryComplete mmi=" + this); 1691 StringBuilder sb = new StringBuilder(getScString()); 1692 sb.append("\n"); 1693 1694 if (ar.exception != null) { 1695 mState = State.FAILED; 1696 1697 if (ar.exception instanceof ImsException) { 1698 sb.append(getImsErrorMessage(ar)); 1699 } else { 1700 sb.append(getErrorMessage(ar)); 1701 } 1702 } else { 1703 List<ImsSsInfo> infos = null; 1704 try { 1705 infos = (List<ImsSsInfo>) ar.result; 1706 } catch (ClassCastException cce) { 1707 // TODO in R: #157# still has ImsSsInfo[] type, fix the type in IImsUtListener.aidl. 1708 infos = Arrays.asList((ImsSsInfo[]) ar.result); 1709 } 1710 if (infos == null || infos.size() == 0) { 1711 sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled)); 1712 } else { 1713 ImsSsInfo info; 1714 for (int i = 0, s = infos.size(); i < s; i++) { 1715 info = infos.get(i); 1716 if (info.getIncomingCommunicationBarringNumber() != null) { 1717 sb.append("Num: " + info.getIncomingCommunicationBarringNumber() 1718 + " status: " + info.getStatus() + "\n"); 1719 } else if (info.getStatus() == 1) { 1720 sb.append(mContext.getText(com.android.internal 1721 .R.string.serviceEnabled)); 1722 } else { 1723 sb.append(mContext.getText(com.android.internal 1724 .R.string.serviceDisabled)); 1725 } 1726 } 1727 } 1728 mState = State.COMPLETE; 1729 } 1730 mMessage = sb; 1731 mPhone.onMMIDone(this); 1732 } 1733 onQueryClirComplete(AsyncResult ar)1734 private void onQueryClirComplete(AsyncResult ar) { 1735 StringBuilder sb = new StringBuilder(getScString()); 1736 sb.append("\n"); 1737 mState = State.FAILED; 1738 1739 if (ar.exception != null) { 1740 if (ar.exception instanceof ImsException) { 1741 sb.append(getImsErrorMessage(ar)); 1742 } else { 1743 sb.append(getErrorMessage(ar)); 1744 } 1745 } else { 1746 int[] clirInfo = (int[]) ar.result; 1747 // ssInfo.getClirOutgoingState() = The 'n' parameter from TS 27.007 7.7 1748 // ssInfo.getClirInterrogationStatus() = The 'm' parameter from TS 27.007 7.7 1749 Rlog.d(LOG_TAG, "onQueryClirComplete: CLIR param n=" + clirInfo[0] 1750 + " m=" + clirInfo[1]); 1751 1752 // 'm' parameter. 1753 switch (clirInfo[1]) { 1754 case ImsSsInfo.CLIR_STATUS_NOT_PROVISIONED: 1755 sb.append(mContext.getText( 1756 com.android.internal.R.string.serviceNotProvisioned)); 1757 mState = State.COMPLETE; 1758 break; 1759 case ImsSsInfo.CLIR_STATUS_PROVISIONED_PERMANENT: 1760 sb.append(mContext.getText( 1761 com.android.internal.R.string.CLIRPermanent)); 1762 mState = State.COMPLETE; 1763 break; 1764 case ImsSsInfo.CLIR_STATUS_TEMPORARILY_RESTRICTED: 1765 // 'n' parameter. 1766 switch (clirInfo[0]) { 1767 case ImsSsInfo.CLIR_OUTGOING_DEFAULT: 1768 sb.append(mContext.getText( 1769 com.android.internal.R.string.CLIRDefaultOnNextCallOn)); 1770 mState = State.COMPLETE; 1771 break; 1772 case ImsSsInfo.CLIR_OUTGOING_INVOCATION: 1773 sb.append(mContext.getText( 1774 com.android.internal.R.string.CLIRDefaultOnNextCallOn)); 1775 mState = State.COMPLETE; 1776 break; 1777 case ImsSsInfo.CLIR_OUTGOING_SUPPRESSION: 1778 sb.append(mContext.getText( 1779 com.android.internal.R.string.CLIRDefaultOnNextCallOff)); 1780 mState = State.COMPLETE; 1781 break; 1782 default: 1783 sb.append(mContext.getText( 1784 com.android.internal.R.string.mmiError)); 1785 mState = State.FAILED; 1786 } 1787 break; 1788 case ImsSsInfo.CLIR_STATUS_TEMPORARILY_ALLOWED: 1789 // 'n' parameter. 1790 switch (clirInfo[0]) { 1791 case ImsSsInfo.CLIR_OUTGOING_DEFAULT: 1792 sb.append(mContext.getText( 1793 com.android.internal.R.string.CLIRDefaultOffNextCallOff)); 1794 mState = State.COMPLETE; 1795 break; 1796 case ImsSsInfo.CLIR_OUTGOING_INVOCATION: 1797 sb.append(mContext.getText( 1798 com.android.internal.R.string.CLIRDefaultOffNextCallOn)); 1799 mState = State.COMPLETE; 1800 break; 1801 case ImsSsInfo.CLIR_OUTGOING_SUPPRESSION: 1802 sb.append(mContext.getText( 1803 com.android.internal.R.string.CLIRDefaultOffNextCallOff)); 1804 mState = State.COMPLETE; 1805 break; 1806 default: 1807 sb.append(mContext.getText( 1808 com.android.internal.R.string.mmiError)); 1809 mState = State.FAILED; 1810 } 1811 break; 1812 default: 1813 sb.append(mContext.getText( 1814 com.android.internal.R.string.mmiError)); 1815 mState = State.FAILED; 1816 } 1817 } 1818 1819 mMessage = sb; 1820 Rlog.d(LOG_TAG, "onQueryClirComplete mmi=" + this); 1821 mPhone.onMMIDone(this); 1822 } 1823 1824 private void onQueryComplete(AsyncResult ar)1825 onQueryComplete(AsyncResult ar) { 1826 StringBuilder sb = new StringBuilder(getScString()); 1827 sb.append("\n"); 1828 1829 mState = State.FAILED; 1830 if (ar.exception != null) { 1831 if (ar.exception instanceof ImsException) { 1832 sb.append(getImsErrorMessage(ar)); 1833 } else { 1834 sb.append(getErrorMessage(ar)); 1835 } 1836 } else if ((ar.result instanceof int[]) && 1837 ((int[])ar.result)[0] == CommandsInterface.SS_STATUS_UNKNOWN) { 1838 sb = null; 1839 } else { 1840 int[] ints = (int[])ar.result; 1841 1842 if (ints != null && ints.length != 0) { 1843 if (ints[0] == 0) { 1844 sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled)); 1845 mState = State.COMPLETE; 1846 } else if (mSc.equals(SC_WAIT)) { 1847 // Call Waiting includes additional data in the response. 1848 sb.append(createQueryCallWaitingResultMessage(ints[1])); 1849 mState = State.COMPLETE; 1850 } else if (ints[0] == 1) { 1851 // for all other services, treat it as a boolean 1852 sb.append(mContext.getText(com.android.internal.R.string.serviceEnabled)); 1853 mState = State.COMPLETE; 1854 } else { 1855 sb.append(mContext.getText(com.android.internal.R.string.mmiError)); 1856 } 1857 } else { 1858 sb.append(mContext.getText(com.android.internal.R.string.mmiError)); 1859 } 1860 } 1861 1862 mMessage = sb; 1863 Rlog.d(LOG_TAG, "onQueryComplete mmi=" + this); 1864 mPhone.onMMIDone(this); 1865 } 1866 1867 private CharSequence createQueryCallWaitingResultMessage(int serviceClass)1868 createQueryCallWaitingResultMessage(int serviceClass) { 1869 StringBuilder sb = new StringBuilder( 1870 mContext.getText(com.android.internal.R.string.serviceEnabledFor)); 1871 1872 for (int classMask = 1 1873 ; classMask <= SERVICE_CLASS_MAX 1874 ; classMask <<= 1 1875 ) { 1876 if ((classMask & serviceClass) != 0) { 1877 sb.append("\n"); 1878 sb.append(serviceClassToCFString(classMask & serviceClass)); 1879 } 1880 } 1881 return sb; 1882 } 1883 getImsErrorMessage(AsyncResult ar)1884 private CharSequence getImsErrorMessage(AsyncResult ar) { 1885 ImsException error = (ImsException) ar.exception; 1886 CharSequence errorMessage; 1887 if ((errorMessage = getMmiErrorMessage(ar)) != null) { 1888 return errorMessage; 1889 } else if (error.getMessage() != null) { 1890 return error.getMessage(); 1891 } else { 1892 return getErrorMessage(ar); 1893 } 1894 } 1895 1896 /** 1897 * Get the int config from carrier config manager. 1898 * 1899 * @param key config key defined in CarrierConfigManager 1900 * @return integer value of corresponding key. 1901 */ getIntCarrierConfig(String key)1902 private int getIntCarrierConfig(String key) { 1903 CarrierConfigManager ConfigManager = 1904 (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE); 1905 PersistableBundle b = null; 1906 if (ConfigManager != null) { 1907 // If an invalid subId is used, this bundle will contain default values. 1908 b = ConfigManager.getConfigForSubId(mPhone.getSubId()); 1909 } 1910 if (b != null) { 1911 return b.getInt(key); 1912 } else { 1913 // Return static default defined in CarrierConfigManager. 1914 return CarrierConfigManager.getDefaultConfig().getInt(key); 1915 } 1916 } 1917 1918 @Override getUssdCallbackReceiver()1919 public ResultReceiver getUssdCallbackReceiver() { 1920 return this.mCallbackReceiver; 1921 } 1922 1923 /** 1924 * Process IMS SS Data received. 1925 */ processImsSsData(AsyncResult data)1926 public void processImsSsData(AsyncResult data) throws ImsException { 1927 try { 1928 ImsSsData ssData = (ImsSsData) data.result; 1929 parseSsData(ssData); 1930 } catch (ClassCastException | NullPointerException ex) { 1931 throw new ImsException("Exception in parsing SS Data", 0); 1932 } 1933 } 1934 parseSsData(ImsSsData ssData)1935 void parseSsData(ImsSsData ssData) { 1936 ImsException ex = (ssData.getResult() != ImsSsData.RESULT_SUCCESS) 1937 ? new ImsException(null, ssData.getResult()) : null; 1938 mSc = getScStringFromScType(ssData.getServiceType()); 1939 mAction = getActionStringFromReqType(ssData.getRequestType()); 1940 Rlog.d(LOG_TAG, "parseSsData msc = " + mSc + ", action = " + mAction + ", ex = " + ex); 1941 1942 switch (ssData.getRequestType()) { 1943 case ImsSsData.SS_ACTIVATION: 1944 case ImsSsData.SS_DEACTIVATION: 1945 case ImsSsData.SS_REGISTRATION: 1946 case ImsSsData.SS_ERASURE: 1947 if ((ssData.getResult() == ImsSsData.RESULT_SUCCESS) 1948 && ssData.isTypeUnConditional()) { 1949 /* 1950 * When ssData.serviceType is unconditional (SS_CFU or SS_CF_ALL) and 1951 * ssData.requestType is activate/register and 1952 * ServiceClass is Voice/Video/None, turn on voice call forwarding. 1953 */ 1954 boolean cffEnabled = ((ssData.getRequestType() == ImsSsData.SS_ACTIVATION 1955 || ssData.getRequestType() == ImsSsData.SS_REGISTRATION) 1956 && isServiceClassVoiceVideoOrNone(ssData.getServiceClass())); 1957 1958 Rlog.d(LOG_TAG, "setCallForwardingFlag cffEnabled: " + cffEnabled); 1959 if (mIccRecords != null) { 1960 Rlog.d(LOG_TAG, "setVoiceCallForwardingFlag done from SS Info."); 1961 //Only CF status is set here as part of activation/registration, 1962 //number is not available until interrogation. 1963 mPhone.setVoiceCallForwardingFlag(1, cffEnabled, null); 1964 } else { 1965 Rlog.e(LOG_TAG, "setCallForwardingFlag aborted. sim records is null."); 1966 } 1967 } 1968 onSetComplete(null, new AsyncResult(null, ssData.getCallForwardInfo(), ex)); 1969 break; 1970 case ImsSsData.SS_INTERROGATION: 1971 if (ssData.isTypeClir()) { 1972 Rlog.d(LOG_TAG, "CLIR INTERROGATION"); 1973 onQueryClirComplete(new AsyncResult(null, ssData.getSuppServiceInfoCompat(), ex)); 1974 } else if (ssData.isTypeCF()) { 1975 Rlog.d(LOG_TAG, "CALL FORWARD INTERROGATION"); 1976 // Have to translate to an array, since the modem still returns it in the 1977 // ImsCallForwardInfo[] format. 1978 List<ImsCallForwardInfo> mCfInfos = ssData.getCallForwardInfo(); 1979 ImsCallForwardInfo[] mCfInfosCompat = null; 1980 if (mCfInfos != null) { 1981 mCfInfosCompat = new ImsCallForwardInfo[mCfInfos.size()]; 1982 mCfInfosCompat = mCfInfos.toArray(mCfInfosCompat); 1983 } 1984 onQueryCfComplete(new AsyncResult(null, mPhone.handleCfQueryResult( 1985 mCfInfosCompat), ex)); 1986 } else if (ssData.isTypeBarring()) { 1987 onSuppSvcQueryComplete(new AsyncResult(null, ssData.getSuppServiceInfoCompat(), 1988 ex)); 1989 } else if (ssData.isTypeColr() || ssData.isTypeClip() || ssData.isTypeColp()) { 1990 onSuppSvcQueryComplete(new AsyncResult(null, ssData.getSuppServiceInfo().get(0), 1991 ex)); 1992 } else if (ssData.isTypeIcb()) { 1993 onIcbQueryComplete(new AsyncResult(null, ssData.getSuppServiceInfo(), ex)); 1994 } else { 1995 onQueryComplete(new AsyncResult(null, ssData.getSuppServiceInfoCompat(), ex)); 1996 } 1997 break; 1998 default: 1999 Rlog.e(LOG_TAG, "Invaid requestType in SSData : " + ssData.getRequestType()); 2000 break; 2001 } 2002 } 2003 getScStringFromScType(int serviceType)2004 private String getScStringFromScType(int serviceType) { 2005 switch (serviceType) { 2006 case ImsSsData.SS_CFU: 2007 return SC_CFU; 2008 case ImsSsData.SS_CF_BUSY: 2009 return SC_CFB; 2010 case ImsSsData.SS_CF_NO_REPLY: 2011 return SC_CFNRy; 2012 case ImsSsData.SS_CF_NOT_REACHABLE: 2013 return SC_CFNR; 2014 case ImsSsData.SS_CF_ALL: 2015 return SC_CF_All; 2016 case ImsSsData.SS_CF_ALL_CONDITIONAL: 2017 return SC_CF_All_Conditional; 2018 case ImsSsData.SS_CLIP: 2019 return SC_CLIP; 2020 case ImsSsData.SS_CLIR: 2021 return SC_CLIR; 2022 case ImsSsData.SS_COLP: 2023 return SC_COLP; 2024 case ImsSsData.SS_COLR: 2025 return SC_COLR; 2026 case ImsSsData.SS_CNAP: 2027 return SC_CNAP; 2028 case ImsSsData.SS_WAIT: 2029 return SC_WAIT; 2030 case ImsSsData.SS_BAOC: 2031 return SC_BAOC; 2032 case ImsSsData.SS_BAOIC: 2033 return SC_BAOIC; 2034 case ImsSsData.SS_BAOIC_EXC_HOME: 2035 return SC_BAOICxH; 2036 case ImsSsData.SS_BAIC: 2037 return SC_BAIC; 2038 case ImsSsData.SS_BAIC_ROAMING: 2039 return SC_BAICr; 2040 case ImsSsData.SS_ALL_BARRING: 2041 return SC_BA_ALL; 2042 case ImsSsData.SS_OUTGOING_BARRING: 2043 return SC_BA_MO; 2044 case ImsSsData.SS_INCOMING_BARRING: 2045 return SC_BA_MT; 2046 case ImsSsData.SS_INCOMING_BARRING_DN: 2047 return SC_BS_MT; 2048 case ImsSsData.SS_INCOMING_BARRING_ANONYMOUS: 2049 return SC_BAICa; 2050 default: 2051 return null; 2052 } 2053 } 2054 getActionStringFromReqType(int requestType)2055 private String getActionStringFromReqType(int requestType) { 2056 switch (requestType) { 2057 case ImsSsData.SS_ACTIVATION: 2058 return ACTION_ACTIVATE; 2059 case ImsSsData.SS_DEACTIVATION: 2060 return ACTION_DEACTIVATE; 2061 case ImsSsData.SS_INTERROGATION: 2062 return ACTION_INTERROGATE; 2063 case ImsSsData.SS_REGISTRATION: 2064 return ACTION_REGISTER; 2065 case ImsSsData.SS_ERASURE: 2066 return ACTION_ERASURE; 2067 default: 2068 return null; 2069 } 2070 } 2071 isServiceClassVoiceVideoOrNone(int serviceClass)2072 private boolean isServiceClassVoiceVideoOrNone(int serviceClass) { 2073 return ((serviceClass == SERVICE_CLASS_NONE) || (serviceClass == SERVICE_CLASS_VOICE) 2074 || (serviceClass == (SERVICE_CLASS_PACKET + SERVICE_CLASS_DATA_SYNC))); 2075 } 2076 isSsInfo()2077 public boolean isSsInfo() { 2078 return mIsSsInfo; 2079 } 2080 setIsSsInfo(boolean isSsInfo)2081 public void setIsSsInfo(boolean isSsInfo) { 2082 mIsSsInfo = isSsInfo; 2083 } 2084 2085 /*** 2086 * TODO: It would be nice to have a method here that can take in a dialstring and 2087 * figure out if there is an MMI code embedded within it. This code would replace 2088 * some of the string parsing functionality in the Phone App's 2089 * SpecialCharSequenceMgr class. 2090 */ 2091 2092 @Override toString()2093 public String toString() { 2094 StringBuilder sb = new StringBuilder("ImsPhoneMmiCode {"); 2095 2096 sb.append("State=" + getState()); 2097 if (mAction != null) sb.append(" action=" + mAction); 2098 if (mSc != null) sb.append(" sc=" + mSc); 2099 if (mSia != null) sb.append(" sia=" + mSia); 2100 if (mSib != null) sb.append(" sib=" + mSib); 2101 if (mSic != null) sb.append(" sic=" + mSic); 2102 if (mPoundString != null) sb.append(" poundString=" + Rlog.pii(LOG_TAG, mPoundString)); 2103 if (mDialingNumber != null) sb.append(" dialingNumber=" 2104 + Rlog.pii(LOG_TAG, mDialingNumber)); 2105 if (mPwd != null) sb.append(" pwd=" + Rlog.pii(LOG_TAG, mPwd)); 2106 if (mCallbackReceiver != null) sb.append(" hasReceiver"); 2107 sb.append("}"); 2108 return sb.toString(); 2109 } 2110 2111 @Override isNetworkInitiatedUssd()2112 public boolean isNetworkInitiatedUssd() { 2113 return mIsNetworkInitiatedUSSD; 2114 } 2115 } 2116