1 /* 2 * Copyright (C) 2006 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.gsm; 18 19 import static android.telephony.CarrierConfigManager.USSD_OVER_IMS_ONLY; 20 21 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_DATA; 22 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_DATA_ASYNC; 23 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_DATA_SYNC; 24 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_FAX; 25 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_MAX; 26 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_NONE; 27 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_PACKET; 28 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_PAD; 29 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_SMS; 30 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE; 31 32 import android.compat.annotation.UnsupportedAppUsage; 33 import android.content.Context; 34 import android.content.res.Resources; 35 import android.os.AsyncResult; 36 import android.os.Build; 37 import android.os.Handler; 38 import android.os.Message; 39 import android.os.PersistableBundle; 40 import android.os.ResultReceiver; 41 import android.telephony.CarrierConfigManager; 42 import android.telephony.PhoneNumberUtils; 43 import android.telephony.TelephonyManager; 44 import android.text.BidiFormatter; 45 import android.text.SpannableStringBuilder; 46 import android.text.TextDirectionHeuristics; 47 import android.text.TextUtils; 48 49 import com.android.internal.annotations.VisibleForTesting; 50 import com.android.internal.telephony.CallForwardInfo; 51 import com.android.internal.telephony.CallStateException; 52 import com.android.internal.telephony.CommandException; 53 import com.android.internal.telephony.CommandsInterface; 54 import com.android.internal.telephony.GsmCdmaPhone; 55 import com.android.internal.telephony.MmiCode; 56 import com.android.internal.telephony.Phone; 57 import com.android.internal.telephony.RILConstants; 58 import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState; 59 import com.android.internal.telephony.uicc.IccRecords; 60 import com.android.internal.telephony.uicc.UiccCardApplication; 61 import com.android.internal.telephony.util.ArrayUtils; 62 import com.android.telephony.Rlog; 63 64 import java.util.regex.Matcher; 65 import java.util.regex.Pattern; 66 67 /** 68 * The motto for this file is: 69 * 70 * "NOTE: By using the # as a separator, most cases are expected to be unambiguous." 71 * -- TS 22.030 6.5.2 72 * 73 * {@hide} 74 * 75 */ 76 public final class GsmMmiCode extends Handler implements MmiCode { 77 static final String LOG_TAG = "GsmMmiCode"; 78 79 //***** Constants 80 81 // Max Size of the Short Code (aka Short String from TS 22.030 6.5.2) 82 static final int MAX_LENGTH_SHORT_CODE = 2; 83 84 // TS 22.030 6.5.2 Every Short String USSD command will end with #-key 85 // (known as #-String) 86 static final char END_OF_USSD_COMMAND = '#'; 87 88 // From TS 22.030 6.5.2 89 static final String ACTION_ACTIVATE = "*"; 90 static final String ACTION_DEACTIVATE = "#"; 91 static final String ACTION_INTERROGATE = "*#"; 92 static final String ACTION_REGISTER = "**"; 93 static final String ACTION_ERASURE = "##"; 94 95 // Supp Service codes from TS 22.030 Annex B 96 97 //Called line presentation 98 static final String SC_CLIP = "30"; 99 static final String SC_CLIR = "31"; 100 101 // Call Forwarding 102 static final String SC_CFU = "21"; 103 static final String SC_CFB = "67"; 104 static final String SC_CFNRy = "61"; 105 static final String SC_CFNR = "62"; 106 107 static final String SC_CF_All = "002"; 108 static final String SC_CF_All_Conditional = "004"; 109 110 // Call Waiting 111 static final String SC_WAIT = "43"; 112 113 // Call Barring 114 static final String SC_BAOC = "33"; 115 static final String SC_BAOIC = "331"; 116 static final String SC_BAOICxH = "332"; 117 static final String SC_BAIC = "35"; 118 static final String SC_BAICr = "351"; 119 120 static final String SC_BA_ALL = "330"; 121 static final String SC_BA_MO = "333"; 122 static final String SC_BA_MT = "353"; 123 124 // Supp Service Password registration 125 static final String SC_PWD = "03"; 126 127 // PIN/PIN2/PUK/PUK2 128 static final String SC_PIN = "04"; 129 static final String SC_PIN2 = "042"; 130 static final String SC_PUK = "05"; 131 static final String SC_PUK2 = "052"; 132 133 //***** Event Constants 134 135 static final int EVENT_SET_COMPLETE = 1; 136 static final int EVENT_GET_CLIR_COMPLETE = 2; 137 static final int EVENT_QUERY_CF_COMPLETE = 3; 138 static final int EVENT_USSD_COMPLETE = 4; 139 static final int EVENT_QUERY_COMPLETE = 5; 140 static final int EVENT_SET_CFF_COMPLETE = 6; 141 static final int EVENT_USSD_CANCEL_COMPLETE = 7; 142 143 //***** Instance Variables 144 145 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 146 GsmCdmaPhone mPhone; 147 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 148 Context mContext; 149 UiccCardApplication mUiccApplication; 150 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 151 IccRecords mIccRecords; 152 153 String mAction; // One of ACTION_* 154 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 155 String mSc; // Service Code 156 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 157 String mSia; // Service Info a 158 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 159 String mSib; // Service Info b 160 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 161 String mSic; // Service Info c 162 String mPoundString; // Entire MMI string up to and including # 163 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 164 public String mDialingNumber; 165 String mPwd; // For password registration 166 167 /** Set to true in processCode, not at newFromDialString time */ 168 private boolean mIsPendingUSSD; 169 170 private boolean mIsUssdRequest; 171 172 private boolean mIsCallFwdReg; 173 174 private boolean mIsNetworkInitiatedUSSD; 175 176 State mState = State.PENDING; 177 CharSequence mMessage; 178 private boolean mIsSsInfo = false; 179 private ResultReceiver mCallbackReceiver; 180 181 182 //***** Class Variables 183 184 185 // See TS 22.030 6.5.2 "Structure of the MMI" 186 187 @UnsupportedAppUsage 188 static Pattern sPatternSuppService = Pattern.compile( 189 "((\\*|#|\\*#|\\*\\*|##)(\\d{2,3})(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*))?)?)?)?#)(.*)"); 190 /* 1 2 3 4 5 6 7 8 9 10 11 12 191 192 1 = Full string up to and including # 193 2 = action (activation/interrogation/registration/erasure) 194 3 = service code 195 5 = SIA 196 7 = SIB 197 9 = SIC 198 10 = dialing number 199 */ 200 201 static final int MATCH_GROUP_POUND_STRING = 1; 202 203 static final int MATCH_GROUP_ACTION = 2; 204 //(activation/interrogation/registration/erasure) 205 206 static final int MATCH_GROUP_SERVICE_CODE = 3; 207 static final int MATCH_GROUP_SIA = 5; 208 static final int MATCH_GROUP_SIB = 7; 209 static final int MATCH_GROUP_SIC = 9; 210 static final int MATCH_GROUP_PWD_CONFIRM = 11; 211 static final int MATCH_GROUP_DIALING_NUMBER = 12; 212 static private String[] sTwoDigitNumberPattern; 213 214 //***** Public Class methods 215 216 /** 217 * Some dial strings in GSM are defined to do non-call setup 218 * things, such as modify or query supplementary service settings (eg, call 219 * forwarding). These are generally referred to as "MMI codes". 220 * We look to see if the dial string contains a valid MMI code (potentially 221 * with a dial string at the end as well) and return info here. 222 * 223 * If the dial string contains no MMI code, we return an instance with 224 * only "dialingNumber" set 225 * 226 * Please see flow chart in TS 22.030 6.5.3.2 227 */ 228 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) newFromDialString(String dialString, GsmCdmaPhone phone, UiccCardApplication app)229 public static GsmMmiCode newFromDialString(String dialString, GsmCdmaPhone phone, 230 UiccCardApplication app) { 231 return newFromDialString(dialString, phone, app, null); 232 } 233 newFromDialString(String dialString, GsmCdmaPhone phone, UiccCardApplication app, ResultReceiver wrappedCallback)234 public static GsmMmiCode newFromDialString(String dialString, GsmCdmaPhone phone, 235 UiccCardApplication app, ResultReceiver wrappedCallback) { 236 Matcher m; 237 GsmMmiCode ret = null; 238 239 if ((phone.getServiceState().getVoiceRoaming() 240 && phone.supportsConversionOfCdmaCallerIdMmiCodesWhileRoaming()) 241 || (isEmergencyNumber(phone, dialString) 242 && isCarrierSupportCallerIdVerticalServiceCodes(phone))) { 243 /* The CDMA MMI coded dialString will be converted to a 3GPP MMI Coded dialString 244 so that it can be processed by the matcher and code below. This can be triggered if 245 the dialing string is an emergency number and carrier supports caller ID vertical 246 service codes *67, *82. 247 */ 248 dialString = convertCdmaMmiCodesTo3gppMmiCodes(dialString); 249 } 250 251 m = sPatternSuppService.matcher(dialString); 252 253 // Is this formatted like a standard supplementary service code? 254 if (m.matches()) { 255 ret = new GsmMmiCode(phone, app); 256 ret.mPoundString = makeEmptyNull(m.group(MATCH_GROUP_POUND_STRING)); 257 ret.mAction = makeEmptyNull(m.group(MATCH_GROUP_ACTION)); 258 ret.mSc = makeEmptyNull(m.group(MATCH_GROUP_SERVICE_CODE)); 259 ret.mSia = makeEmptyNull(m.group(MATCH_GROUP_SIA)); 260 ret.mSib = makeEmptyNull(m.group(MATCH_GROUP_SIB)); 261 ret.mSic = makeEmptyNull(m.group(MATCH_GROUP_SIC)); 262 ret.mPwd = makeEmptyNull(m.group(MATCH_GROUP_PWD_CONFIRM)); 263 ret.mDialingNumber = makeEmptyNull(m.group(MATCH_GROUP_DIALING_NUMBER)); 264 265 if(ret.mDialingNumber != null && 266 ret.mDialingNumber.endsWith("#") && 267 dialString.endsWith("#")){ 268 // According to TS 22.030 6.5.2 "Structure of the MMI", 269 // the dialing number should not ending with #. 270 // The dialing number ending # is treated as unique USSD, 271 // eg, *400#16 digit number# to recharge the prepaid card 272 // in India operator(Mumbai MTNL) 273 ret = new GsmMmiCode(phone, app); 274 ret.mPoundString = dialString; 275 } else if (ret.isFacToDial()) { 276 // This is a FAC (feature access code) to dial as a normal call. 277 ret = null; 278 } 279 } else if (dialString.endsWith("#")) { 280 // TS 22.030 sec 6.5.3.2 281 // "Entry of any characters defined in the 3GPP TS 23.038 [8] Default Alphabet 282 // (up to the maximum defined in 3GPP TS 24.080 [10]), followed by #SEND". 283 284 ret = new GsmMmiCode(phone, app); 285 ret.mPoundString = dialString; 286 } else if (isTwoDigitShortCode(phone.getContext(), phone.getSubId(), dialString)) { 287 //Is a country-specific exception to short codes as defined in TS 22.030, 6.5.3.2 288 ret = null; 289 } else if (isShortCode(dialString, phone)) { 290 // this may be a short code, as defined in TS 22.030, 6.5.3.2 291 ret = new GsmMmiCode(phone, app); 292 ret.mDialingNumber = dialString; 293 } 294 295 if (ret != null) { 296 ret.mCallbackReceiver = wrappedCallback; 297 } 298 299 return ret; 300 } 301 convertCdmaMmiCodesTo3gppMmiCodes(String dialString)302 private static String convertCdmaMmiCodesTo3gppMmiCodes(String dialString) { 303 Matcher m; 304 m = sPatternCdmaMmiCodeWhileRoaming.matcher(dialString); 305 if (m.matches()) { 306 String serviceCode = makeEmptyNull(m.group(MATCH_GROUP_CDMA_MMI_CODE_SERVICE_CODE)); 307 String prefix = m.group(MATCH_GROUP_CDMA_MMI_CODE_NUMBER_PREFIX); 308 String number = makeEmptyNull(m.group(MATCH_GROUP_CDMA_MMI_CODE_NUMBER)); 309 310 if (serviceCode.equals("67") && number != null) { 311 // "#31#number" to invoke CLIR 312 dialString = ACTION_DEACTIVATE + SC_CLIR + ACTION_DEACTIVATE + prefix + number; 313 } else if (serviceCode.equals("82") && number != null) { 314 // "*31#number" to suppress CLIR 315 dialString = ACTION_ACTIVATE + SC_CLIR + ACTION_DEACTIVATE + prefix + number; 316 } 317 } 318 return dialString; 319 } 320 321 public static GsmMmiCode newNetworkInitiatedUssd(String ussdMessage, boolean isUssdRequest, GsmCdmaPhone phone, UiccCardApplication app)322 newNetworkInitiatedUssd(String ussdMessage, 323 boolean isUssdRequest, GsmCdmaPhone phone, UiccCardApplication app) { 324 GsmMmiCode ret; 325 326 ret = new GsmMmiCode(phone, app); 327 328 ret.mMessage = ussdMessage; 329 ret.mIsUssdRequest = isUssdRequest; 330 ret.mIsNetworkInitiatedUSSD = true; 331 332 // If it's a request, set to PENDING so that it's cancelable. 333 if (isUssdRequest) { 334 ret.mIsPendingUSSD = true; 335 ret.mState = State.PENDING; 336 } else { 337 ret.mState = State.COMPLETE; 338 } 339 340 return ret; 341 } 342 newFromUssdUserInput(String ussdMessge, GsmCdmaPhone phone, UiccCardApplication app)343 public static GsmMmiCode newFromUssdUserInput(String ussdMessge, 344 GsmCdmaPhone phone, 345 UiccCardApplication app) { 346 GsmMmiCode ret = new GsmMmiCode(phone, app); 347 348 ret.mMessage = ussdMessge; 349 ret.mState = State.PENDING; 350 ret.mIsPendingUSSD = true; 351 352 return ret; 353 } 354 355 /** Process SS Data */ 356 public void processSsData(AsyncResult data)357 processSsData(AsyncResult data) { 358 Rlog.d(LOG_TAG, "In processSsData"); 359 360 mIsSsInfo = true; 361 try { 362 SsData ssData = (SsData)data.result; 363 parseSsData(ssData); 364 } catch (ClassCastException ex) { 365 Rlog.e(LOG_TAG, "Class Cast Exception in parsing SS Data : " + ex); 366 } catch (NullPointerException ex) { 367 Rlog.e(LOG_TAG, "Null Pointer Exception in parsing SS Data : " + ex); 368 } 369 } 370 parseSsData(SsData ssData)371 void parseSsData(SsData ssData) { 372 CommandException ex; 373 374 ex = CommandException.fromRilErrno(ssData.result); 375 mSc = getScStringFromScType(ssData.serviceType); 376 mAction = getActionStringFromReqType(ssData.requestType); 377 Rlog.d(LOG_TAG, "parseSsData msc = " + mSc + ", action = " + mAction + ", ex = " + ex); 378 379 switch (ssData.requestType) { 380 case SS_ACTIVATION: 381 case SS_DEACTIVATION: 382 case SS_REGISTRATION: 383 case SS_ERASURE: 384 if ((ssData.result == RILConstants.SUCCESS) && 385 ssData.serviceType.isTypeUnConditional()) { 386 /* 387 * When ServiceType is SS_CFU/SS_CF_ALL and RequestType is activate/register 388 * and ServiceClass is Voice/None, set IccRecords.setVoiceCallForwardingFlag. 389 * Only CF status can be set here since number is not available. 390 */ 391 boolean cffEnabled = ((ssData.requestType == SsData.RequestType.SS_ACTIVATION || 392 ssData.requestType == SsData.RequestType.SS_REGISTRATION) && 393 isServiceClassVoiceorNone(ssData.serviceClass)); 394 395 Rlog.d(LOG_TAG, "setVoiceCallForwardingFlag cffEnabled: " + cffEnabled); 396 mPhone.setVoiceCallForwardingFlag(1, cffEnabled, null); 397 } 398 onSetComplete(null, new AsyncResult(null, ssData.cfInfo, ex)); 399 break; 400 case SS_INTERROGATION: 401 if (ssData.serviceType.isTypeClir()) { 402 Rlog.d(LOG_TAG, "CLIR INTERROGATION"); 403 onGetClirComplete(new AsyncResult(null, ssData.ssInfo, ex)); 404 } else if (ssData.serviceType.isTypeCF()) { 405 Rlog.d(LOG_TAG, "CALL FORWARD INTERROGATION"); 406 onQueryCfComplete(new AsyncResult(null, ssData.cfInfo, ex)); 407 } else { 408 onQueryComplete(new AsyncResult(null, ssData.ssInfo, ex)); 409 } 410 break; 411 default: 412 Rlog.e(LOG_TAG, "Invaid requestType in SSData : " + ssData.requestType); 413 break; 414 } 415 } 416 getScStringFromScType(SsData.ServiceType sType)417 private String getScStringFromScType(SsData.ServiceType sType) { 418 switch (sType) { 419 case SS_CFU: 420 return SC_CFU; 421 case SS_CF_BUSY: 422 return SC_CFB; 423 case SS_CF_NO_REPLY: 424 return SC_CFNRy; 425 case SS_CF_NOT_REACHABLE: 426 return SC_CFNR; 427 case SS_CF_ALL: 428 return SC_CF_All; 429 case SS_CF_ALL_CONDITIONAL: 430 return SC_CF_All_Conditional; 431 case SS_CLIP: 432 return SC_CLIP; 433 case SS_CLIR: 434 return SC_CLIR; 435 case SS_WAIT: 436 return SC_WAIT; 437 case SS_BAOC: 438 return SC_BAOC; 439 case SS_BAOIC: 440 return SC_BAOIC; 441 case SS_BAOIC_EXC_HOME: 442 return SC_BAOICxH; 443 case SS_BAIC: 444 return SC_BAIC; 445 case SS_BAIC_ROAMING: 446 return SC_BAICr; 447 case SS_ALL_BARRING: 448 return SC_BA_ALL; 449 case SS_OUTGOING_BARRING: 450 return SC_BA_MO; 451 case SS_INCOMING_BARRING: 452 return SC_BA_MT; 453 } 454 455 return ""; 456 } 457 getActionStringFromReqType(SsData.RequestType rType)458 private String getActionStringFromReqType(SsData.RequestType rType) { 459 switch (rType) { 460 case SS_ACTIVATION: 461 return ACTION_ACTIVATE; 462 case SS_DEACTIVATION: 463 return ACTION_DEACTIVATE; 464 case SS_INTERROGATION: 465 return ACTION_INTERROGATE; 466 case SS_REGISTRATION: 467 return ACTION_REGISTER; 468 case SS_ERASURE: 469 return ACTION_ERASURE; 470 } 471 472 return ""; 473 } 474 isServiceClassVoiceorNone(int serviceClass)475 private boolean isServiceClassVoiceorNone(int serviceClass) { 476 return (((serviceClass & CommandsInterface.SERVICE_CLASS_VOICE) != 0) || 477 (serviceClass == CommandsInterface.SERVICE_CLASS_NONE)); 478 } 479 480 //***** Private Class methods 481 482 /** make empty strings be null. 483 * Regexp returns empty strings for empty groups 484 */ 485 @UnsupportedAppUsage 486 private static String makeEmptyNull(String s)487 makeEmptyNull (String s) { 488 if (s != null && s.length() == 0) return null; 489 490 return s; 491 } 492 493 /** returns true of the string is empty or null */ 494 private static boolean isEmptyOrNull(CharSequence s)495 isEmptyOrNull(CharSequence s) { 496 return s == null || (s.length() == 0); 497 } 498 499 500 private static int scToCallForwardReason(String sc)501 scToCallForwardReason(String sc) { 502 if (sc == null) { 503 throw new RuntimeException ("invalid call forward sc"); 504 } 505 506 if (sc.equals(SC_CF_All)) { 507 return CommandsInterface.CF_REASON_ALL; 508 } else if (sc.equals(SC_CFU)) { 509 return CommandsInterface.CF_REASON_UNCONDITIONAL; 510 } else if (sc.equals(SC_CFB)) { 511 return CommandsInterface.CF_REASON_BUSY; 512 } else if (sc.equals(SC_CFNR)) { 513 return CommandsInterface.CF_REASON_NOT_REACHABLE; 514 } else if (sc.equals(SC_CFNRy)) { 515 return CommandsInterface.CF_REASON_NO_REPLY; 516 } else if (sc.equals(SC_CF_All_Conditional)) { 517 return CommandsInterface.CF_REASON_ALL_CONDITIONAL; 518 } else { 519 throw new RuntimeException ("invalid call forward sc"); 520 } 521 } 522 523 @UnsupportedAppUsage 524 private static int siToServiceClass(String si)525 siToServiceClass(String si) { 526 if (si == null || si.length() == 0) { 527 return SERVICE_CLASS_NONE; 528 } else { 529 // NumberFormatException should cause MMI fail 530 int serviceCode = Integer.parseInt(si, 10); 531 532 switch (serviceCode) { 533 case 10: return SERVICE_CLASS_SMS + SERVICE_CLASS_FAX + SERVICE_CLASS_VOICE; 534 case 11: return SERVICE_CLASS_VOICE; 535 case 12: return SERVICE_CLASS_SMS + SERVICE_CLASS_FAX; 536 case 13: return SERVICE_CLASS_FAX; 537 538 case 16: return SERVICE_CLASS_SMS; 539 540 case 19: return SERVICE_CLASS_FAX + SERVICE_CLASS_VOICE; 541 /* 542 Note for code 20: 543 From TS 22.030 Annex C: 544 "All GPRS bearer services" are not included in "All tele and bearer services" 545 and "All bearer services"." 546 ....so SERVICE_CLASS_DATA, which (according to 27.007) includes GPRS 547 */ 548 case 20: return SERVICE_CLASS_DATA_ASYNC + SERVICE_CLASS_DATA_SYNC; 549 550 case 21: return SERVICE_CLASS_PAD + SERVICE_CLASS_DATA_ASYNC; 551 case 22: return SERVICE_CLASS_PACKET + SERVICE_CLASS_DATA_SYNC; 552 case 24: return SERVICE_CLASS_DATA_SYNC; 553 case 25: return SERVICE_CLASS_DATA_ASYNC; 554 case 26: return SERVICE_CLASS_DATA_SYNC + SERVICE_CLASS_VOICE; 555 case 99: return SERVICE_CLASS_PACKET; 556 557 default: 558 throw new RuntimeException("unsupported MMI service code " + si); 559 } 560 } 561 } 562 563 private static int siToTime(String si)564 siToTime (String si) { 565 if (si == null || si.length() == 0) { 566 return 0; 567 } else { 568 // NumberFormatException should cause MMI fail 569 return Integer.parseInt(si, 10); 570 } 571 } 572 573 @UnsupportedAppUsage 574 static boolean isServiceCodeCallForwarding(String sc)575 isServiceCodeCallForwarding(String sc) { 576 return sc != null && 577 (sc.equals(SC_CFU) 578 || sc.equals(SC_CFB) || sc.equals(SC_CFNRy) 579 || sc.equals(SC_CFNR) || sc.equals(SC_CF_All) 580 || sc.equals(SC_CF_All_Conditional)); 581 } 582 583 @UnsupportedAppUsage 584 static boolean isServiceCodeCallBarring(String sc)585 isServiceCodeCallBarring(String sc) { 586 Resources resource = Resources.getSystem(); 587 if (sc != null) { 588 String[] barringMMI = resource.getStringArray( 589 com.android.internal.R.array.config_callBarringMMI); 590 if (barringMMI != null) { 591 for (String match : barringMMI) { 592 if (sc.equals(match)) return true; 593 } 594 } 595 } 596 return false; 597 } 598 599 static String scToBarringFacility(String sc)600 scToBarringFacility(String sc) { 601 if (sc == null) { 602 throw new RuntimeException ("invalid call barring sc"); 603 } 604 605 if (sc.equals(SC_BAOC)) { 606 return CommandsInterface.CB_FACILITY_BAOC; 607 } else if (sc.equals(SC_BAOIC)) { 608 return CommandsInterface.CB_FACILITY_BAOIC; 609 } else if (sc.equals(SC_BAOICxH)) { 610 return CommandsInterface.CB_FACILITY_BAOICxH; 611 } else if (sc.equals(SC_BAIC)) { 612 return CommandsInterface.CB_FACILITY_BAIC; 613 } else if (sc.equals(SC_BAICr)) { 614 return CommandsInterface.CB_FACILITY_BAICr; 615 } else if (sc.equals(SC_BA_ALL)) { 616 return CommandsInterface.CB_FACILITY_BA_ALL; 617 } else if (sc.equals(SC_BA_MO)) { 618 return CommandsInterface.CB_FACILITY_BA_MO; 619 } else if (sc.equals(SC_BA_MT)) { 620 return CommandsInterface.CB_FACILITY_BA_MT; 621 } else { 622 throw new RuntimeException ("invalid call barring sc"); 623 } 624 } 625 626 //***** Constructor 627 628 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) GsmMmiCode(GsmCdmaPhone phone, UiccCardApplication app)629 public GsmMmiCode(GsmCdmaPhone phone, UiccCardApplication app) { 630 // The telephony unit-test cases may create GsmMmiCode's 631 // in secondary threads 632 super(phone.getHandler().getLooper()); 633 mPhone = phone; 634 mContext = phone.getContext(); 635 mUiccApplication = app; 636 if (app != null) { 637 mIccRecords = app.getIccRecords(); 638 } 639 } 640 641 //***** MmiCode implementation 642 643 @Override 644 public State getState()645 getState() { 646 return mState; 647 } 648 649 @Override 650 public CharSequence getMessage()651 getMessage() { 652 return mMessage; 653 } 654 655 public Phone getPhone()656 getPhone() { 657 return ((Phone) mPhone); 658 } 659 660 // inherited javadoc suffices 661 @Override 662 public void cancel()663 cancel() { 664 // Complete or failed cannot be cancelled 665 if (mState == State.COMPLETE || mState == State.FAILED) { 666 return; 667 } 668 669 mState = State.CANCELLED; 670 671 if (mIsPendingUSSD) { 672 /* 673 * There can only be one pending USSD session, so tell the radio to 674 * cancel it. 675 */ 676 mPhone.mCi.cancelPendingUssd(obtainMessage(EVENT_USSD_CANCEL_COMPLETE, this)); 677 678 /* 679 * Don't call phone.onMMIDone here; wait for CANCEL_COMPLETE notice 680 * from RIL. 681 */ 682 } else { 683 // TODO in cases other than USSD, it would be nice to cancel 684 // the pending radio operation. This requires RIL cancellation 685 // support, which does not presently exist. 686 687 mPhone.onMMIDone (this); 688 } 689 690 } 691 692 @Override isCancelable()693 public boolean isCancelable() { 694 /* Can only cancel pending USSD sessions. */ 695 return mIsPendingUSSD; 696 } 697 698 @Override isNetworkInitiatedUssd()699 public boolean isNetworkInitiatedUssd() { 700 return mIsNetworkInitiatedUSSD; 701 } 702 703 //***** Instance Methods 704 705 /** Does this dial string contain a structured or unstructured MMI code? */ 706 boolean isMMI()707 isMMI() { 708 return mPoundString != null; 709 } 710 711 /* Is this a 1 or 2 digit "short code" as defined in TS 22.030 sec 6.5.3.2? */ 712 boolean isShortCode()713 isShortCode() { 714 return mPoundString == null 715 && mDialingNumber != null && mDialingNumber.length() <= 2; 716 717 } 718 719 @Override getDialString()720 public String getDialString() { 721 return mPoundString; 722 } 723 724 /** 725 * Check if the dial string match the two digital number pattern which defined by Carrier. 726 */ isTwoDigitShortCode(Context context, int subId, String dialString)727 public static boolean isTwoDigitShortCode(Context context, int subId, String dialString) { 728 Rlog.d(LOG_TAG, "isTwoDigitShortCode"); 729 730 if (dialString == null || dialString.length() > 2) return false; 731 732 if (sTwoDigitNumberPattern == null) { 733 sTwoDigitNumberPattern = getTwoDigitNumberPattern(context, subId); 734 } 735 736 for (String dialnumber : sTwoDigitNumberPattern) { 737 Rlog.d(LOG_TAG, "Two Digit Number Pattern " + dialnumber); 738 if (dialString.equals(dialnumber)) { 739 Rlog.d(LOG_TAG, "Two Digit Number Pattern -true"); 740 return true; 741 } 742 } 743 Rlog.d(LOG_TAG, "Two Digit Number Pattern -false"); 744 return false; 745 } 746 getTwoDigitNumberPattern(Context context, int subId)747 private static String[] getTwoDigitNumberPattern(Context context, int subId) { 748 Rlog.d(LOG_TAG, "Get two digit number pattern: subId=" + subId); 749 String[] twoDigitNumberPattern = null; 750 CarrierConfigManager configManager = (CarrierConfigManager) 751 context.getSystemService(Context.CARRIER_CONFIG_SERVICE); 752 if (configManager != null) { 753 PersistableBundle bundle = configManager.getConfigForSubId(subId); 754 if (bundle != null) { 755 Rlog.d(LOG_TAG, "Two Digit Number Pattern from carrir config"); 756 twoDigitNumberPattern = bundle.getStringArray(CarrierConfigManager 757 .KEY_MMI_TWO_DIGIT_NUMBER_PATTERN_STRING_ARRAY); 758 } 759 } 760 761 // Do NOT return null array 762 if (twoDigitNumberPattern == null) { 763 twoDigitNumberPattern = new String[0]; 764 } 765 return twoDigitNumberPattern; 766 } 767 768 /** 769 * Helper function for newFromDialString. Returns true if dialString appears 770 * to be a short code AND conditions are correct for it to be treated as 771 * such. 772 */ isShortCode(String dialString, GsmCdmaPhone phone)773 static private boolean isShortCode(String dialString, GsmCdmaPhone phone) { 774 // Refer to TS 22.030 Figure 3.5.3.2: 775 if (dialString == null) { 776 return false; 777 } 778 779 // Illegal dial string characters will give a ZERO length. 780 // At this point we do not want to crash as any application with 781 // call privileges may send a non dial string. 782 // It return false as when the dialString is equal to NULL. 783 if (dialString.length() == 0) { 784 return false; 785 } 786 787 if (isEmergencyNumber(phone, dialString)) { 788 return false; 789 } else { 790 return isShortCodeUSSD(dialString, phone); 791 } 792 } 793 794 /** 795 * Helper function for isShortCode. Returns true if dialString appears to be 796 * a short code and it is a USSD structure 797 * 798 * According to the 3PGG TS 22.030 specification Figure 3.5.3.2: A 1 or 2 799 * digit "short code" is treated as USSD if it is entered while on a call or 800 * does not satisfy the condition (exactly 2 digits && starts with '1'), there 801 * are however exceptions to this rule (see below) 802 * 803 * Exception (1) to Call initiation is: If the user of the device is already in a call 804 * and enters a Short String without any #-key at the end and the length of the Short String is 805 * equal or less then the MAX_LENGTH_SHORT_CODE [constant that is equal to 2] 806 * 807 * The phone shall initiate a USSD/SS commands. 808 */ isShortCodeUSSD(String dialString, GsmCdmaPhone phone)809 static private boolean isShortCodeUSSD(String dialString, GsmCdmaPhone phone) { 810 if (dialString != null && dialString.length() <= MAX_LENGTH_SHORT_CODE) { 811 if (phone.isInCall()) { 812 return true; 813 } 814 815 if (dialString.length() != MAX_LENGTH_SHORT_CODE || 816 dialString.charAt(0) != '1') { 817 return true; 818 } 819 } 820 return false; 821 } 822 823 /** 824 * @return true if the Service Code is PIN/PIN2/PUK/PUK2-related 825 */ isPinPukCommand()826 public boolean isPinPukCommand() { 827 return mSc != null && (mSc.equals(SC_PIN) || mSc.equals(SC_PIN2) 828 || mSc.equals(SC_PUK) || mSc.equals(SC_PUK2)); 829 } 830 831 /** 832 * See TS 22.030 Annex B. 833 * In temporary mode, to suppress CLIR for a single call, enter: 834 * " * 31 # [called number] SEND " 835 * In temporary mode, to invoke CLIR for a single call enter: 836 * " # 31 # [called number] SEND " 837 */ 838 @UnsupportedAppUsage isTemporaryModeCLIR()839 public boolean isTemporaryModeCLIR() { 840 return mSc != null && mSc.equals(SC_CLIR) 841 && mDialingNumber != null && (isActivate() || isDeactivate()); 842 } 843 844 /** 845 * Checks if the dialing string is an emergency number. 846 */ 847 @VisibleForTesting isEmergencyNumber(Phone phone, String dialString)848 public static boolean isEmergencyNumber(Phone phone, String dialString) { 849 try { 850 TelephonyManager tm = phone.getContext().getSystemService(TelephonyManager.class); 851 return tm.isEmergencyNumber(dialString); 852 } catch (RuntimeException ex) { 853 return false; 854 } 855 } 856 857 /** 858 * Checks if carrier supports caller id vertical service codes by checking with 859 * {@link CarrierConfigManager#KEY_CARRIER_SUPPORTS_CALLER_ID_VERTICAL_SERVICE_CODES_BOOL}. 860 */ 861 @VisibleForTesting isCarrierSupportCallerIdVerticalServiceCodes(Phone phone)862 public static boolean isCarrierSupportCallerIdVerticalServiceCodes(Phone phone) { 863 CarrierConfigManager configManager = phone.getContext().getSystemService( 864 CarrierConfigManager.class); 865 PersistableBundle b = null; 866 if (configManager != null) { 867 // If an invalid subId is used, this bundle will contain default values. 868 b = configManager.getConfigForSubId(phone.getSubId()); 869 } 870 if (b != null) { 871 return b == null ? false : b.getBoolean(CarrierConfigManager 872 .KEY_CARRIER_SUPPORTS_CALLER_ID_VERTICAL_SERVICE_CODES_BOOL); 873 } 874 return false; 875 } 876 877 /** 878 * returns CommandsInterface.CLIR_* 879 * See also isTemporaryModeCLIR() 880 */ 881 @UnsupportedAppUsage 882 public int getCLIRMode()883 getCLIRMode() { 884 if (mSc != null && mSc.equals(SC_CLIR)) { 885 if (isActivate()) { 886 return CommandsInterface.CLIR_SUPPRESSION; 887 } else if (isDeactivate()) { 888 return CommandsInterface.CLIR_INVOCATION; 889 } 890 } 891 892 return CommandsInterface.CLIR_DEFAULT; 893 } 894 895 /** 896 * Returns true if the Service Code is FAC to dial as a normal call. 897 * 898 * FAC stands for feature access code and it is special patterns of characters 899 * to invoke certain features. 900 */ isFacToDial()901 private boolean isFacToDial() { 902 CarrierConfigManager configManager = (CarrierConfigManager) 903 mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); 904 PersistableBundle b = configManager.getConfigForSubId(mPhone.getSubId()); 905 if (b != null) { 906 String[] dialFacList = b.getStringArray(CarrierConfigManager 907 .KEY_FEATURE_ACCESS_CODES_STRING_ARRAY); 908 if (!ArrayUtils.isEmpty(dialFacList)) { 909 for (String fac : dialFacList) { 910 if (fac.equals(mSc)) { 911 return true; 912 } 913 } 914 } 915 } 916 return false; 917 } 918 919 @UnsupportedAppUsage isActivate()920 boolean isActivate() { 921 return mAction != null && mAction.equals(ACTION_ACTIVATE); 922 } 923 924 @UnsupportedAppUsage isDeactivate()925 boolean isDeactivate() { 926 return mAction != null && mAction.equals(ACTION_DEACTIVATE); 927 } 928 929 @UnsupportedAppUsage isInterrogate()930 boolean isInterrogate() { 931 return mAction != null && mAction.equals(ACTION_INTERROGATE); 932 } 933 934 @UnsupportedAppUsage isRegister()935 boolean isRegister() { 936 return mAction != null && mAction.equals(ACTION_REGISTER); 937 } 938 939 @UnsupportedAppUsage isErasure()940 boolean isErasure() { 941 return mAction != null && mAction.equals(ACTION_ERASURE); 942 } 943 944 /** 945 * Returns true if this is a USSD code that's been submitted to the 946 * network...eg, after processCode() is called 947 */ isPendingUSSD()948 public boolean isPendingUSSD() { 949 return mIsPendingUSSD; 950 } 951 952 @Override isUssdRequest()953 public boolean isUssdRequest() { 954 return mIsUssdRequest; 955 } 956 isSsInfo()957 public boolean isSsInfo() { 958 return mIsSsInfo; 959 } 960 isVoiceUnconditionalForwarding(int reason, int serviceClass)961 public static boolean isVoiceUnconditionalForwarding(int reason, int serviceClass) { 962 return (((reason == CommandsInterface.CF_REASON_UNCONDITIONAL) 963 || (reason == CommandsInterface.CF_REASON_ALL)) 964 && (((serviceClass & CommandsInterface.SERVICE_CLASS_VOICE) != 0) 965 || (serviceClass == CommandsInterface.SERVICE_CLASS_NONE))); 966 } 967 968 /** Process a MMI code or short code...anything that isn't a dialing number */ 969 @UnsupportedAppUsage 970 public void processCode()971 processCode() throws CallStateException { 972 try { 973 if (isShortCode()) { 974 Rlog.d(LOG_TAG, "processCode: isShortCode"); 975 // These just get treated as USSD. 976 sendUssd(mDialingNumber); 977 } else if (mDialingNumber != null) { 978 // We should have no dialing numbers here 979 throw new RuntimeException ("Invalid or Unsupported MMI Code"); 980 } else if (mSc != null && mSc.equals(SC_CLIP)) { 981 Rlog.d(LOG_TAG, "processCode: is CLIP"); 982 if (isInterrogate()) { 983 mPhone.mCi.queryCLIP( 984 obtainMessage(EVENT_QUERY_COMPLETE, this)); 985 } else { 986 throw new RuntimeException ("Invalid or Unsupported MMI Code"); 987 } 988 } else if (mSc != null && mSc.equals(SC_CLIR)) { 989 Rlog.d(LOG_TAG, "processCode: is CLIR"); 990 if (isActivate() && !mPhone.isClirActivationAndDeactivationPrevented()) { 991 mPhone.mCi.setCLIR(CommandsInterface.CLIR_INVOCATION, 992 obtainMessage(EVENT_SET_COMPLETE, this)); 993 } else if (isDeactivate() && !mPhone.isClirActivationAndDeactivationPrevented()) { 994 mPhone.mCi.setCLIR(CommandsInterface.CLIR_SUPPRESSION, 995 obtainMessage(EVENT_SET_COMPLETE, this)); 996 } else if (isInterrogate()) { 997 mPhone.mCi.getCLIR( 998 obtainMessage(EVENT_GET_CLIR_COMPLETE, this)); 999 } else { 1000 throw new RuntimeException ("Invalid or Unsupported MMI Code"); 1001 } 1002 } else if (isServiceCodeCallForwarding(mSc)) { 1003 Rlog.d(LOG_TAG, "processCode: is CF"); 1004 1005 String dialingNumber = mSia; 1006 int serviceClass = siToServiceClass(mSib); 1007 int reason = scToCallForwardReason(mSc); 1008 int time = siToTime(mSic); 1009 1010 if (isInterrogate()) { 1011 mPhone.mCi.queryCallForwardStatus( 1012 reason, serviceClass, dialingNumber, 1013 obtainMessage(EVENT_QUERY_CF_COMPLETE, this)); 1014 } else { 1015 int cfAction; 1016 1017 if (isActivate()) { 1018 // 3GPP TS 22.030 6.5.2 1019 // a call forwarding request with a single * would be 1020 // interpreted as registration if containing a forwarded-to 1021 // number, or an activation if not 1022 if (isEmptyOrNull(dialingNumber)) { 1023 cfAction = CommandsInterface.CF_ACTION_ENABLE; 1024 mIsCallFwdReg = false; 1025 } else { 1026 cfAction = CommandsInterface.CF_ACTION_REGISTRATION; 1027 mIsCallFwdReg = true; 1028 } 1029 } else if (isDeactivate()) { 1030 cfAction = CommandsInterface.CF_ACTION_DISABLE; 1031 } else if (isRegister()) { 1032 cfAction = CommandsInterface.CF_ACTION_REGISTRATION; 1033 } else if (isErasure()) { 1034 cfAction = CommandsInterface.CF_ACTION_ERASURE; 1035 } else { 1036 throw new RuntimeException ("invalid action"); 1037 } 1038 1039 int isEnableDesired = 1040 ((cfAction == CommandsInterface.CF_ACTION_ENABLE) || 1041 (cfAction == CommandsInterface.CF_ACTION_REGISTRATION)) ? 1 : 0; 1042 1043 Rlog.d(LOG_TAG, "processCode: is CF setCallForward"); 1044 mPhone.mCi.setCallForward(cfAction, reason, serviceClass, 1045 dialingNumber, time, obtainMessage( 1046 EVENT_SET_CFF_COMPLETE, 1047 isVoiceUnconditionalForwarding(reason, serviceClass) ? 1 : 0, 1048 isEnableDesired, this)); 1049 } 1050 } else if (isServiceCodeCallBarring(mSc)) { 1051 // sia = password 1052 // sib = basic service group 1053 1054 String password = mSia; 1055 int serviceClass = siToServiceClass(mSib); 1056 String facility = scToBarringFacility(mSc); 1057 1058 if (isInterrogate()) { 1059 mPhone.mCi.queryFacilityLock(facility, password, 1060 serviceClass, obtainMessage(EVENT_QUERY_COMPLETE, this)); 1061 } else if (isActivate() || isDeactivate()) { 1062 mPhone.mCi.setFacilityLock(facility, isActivate(), password, 1063 serviceClass, obtainMessage(EVENT_SET_COMPLETE, this)); 1064 } else { 1065 throw new RuntimeException ("Invalid or Unsupported MMI Code"); 1066 } 1067 1068 } else if (mSc != null && mSc.equals(SC_PWD)) { 1069 // sia = fac 1070 // sib = old pwd 1071 // sic = new pwd 1072 // pwd = new pwd 1073 String facility; 1074 String oldPwd = mSib; 1075 String newPwd = mSic; 1076 if (isActivate() || isRegister()) { 1077 // Even though ACTIVATE is acceptable, this is really termed a REGISTER 1078 mAction = ACTION_REGISTER; 1079 1080 if (mSia == null) { 1081 // If sc was not specified, treat it as BA_ALL. 1082 facility = CommandsInterface.CB_FACILITY_BA_ALL; 1083 } else { 1084 facility = scToBarringFacility(mSia); 1085 } 1086 if (newPwd.equals(mPwd)) { 1087 mPhone.mCi.changeBarringPassword(facility, oldPwd, 1088 newPwd, obtainMessage(EVENT_SET_COMPLETE, this)); 1089 } else { 1090 // password mismatch; return error 1091 handlePasswordError(com.android.internal.R.string.passwordIncorrect); 1092 } 1093 } else { 1094 throw new RuntimeException ("Invalid or Unsupported MMI Code"); 1095 } 1096 1097 } else if (mSc != null && mSc.equals(SC_WAIT)) { 1098 // sia = basic service group 1099 int serviceClass = siToServiceClass(mSia); 1100 1101 if (isActivate() || isDeactivate()) { 1102 mPhone.mCi.setCallWaiting(isActivate(), serviceClass, 1103 obtainMessage(EVENT_SET_COMPLETE, this)); 1104 } else if (isInterrogate()) { 1105 mPhone.mCi.queryCallWaiting(serviceClass, 1106 obtainMessage(EVENT_QUERY_COMPLETE, this)); 1107 } else { 1108 throw new RuntimeException ("Invalid or Unsupported MMI Code"); 1109 } 1110 } else if (isPinPukCommand()) { 1111 // TODO: This is the same as the code in CmdaMmiCode.java, 1112 // MmiCode should be an abstract or base class and this and 1113 // other common variables and code should be promoted. 1114 1115 // sia = old PIN or PUK 1116 // sib = new PIN 1117 // sic = new PIN 1118 String oldPinOrPuk = mSia; 1119 String newPinOrPuk = mSib; 1120 int pinLen = newPinOrPuk.length(); 1121 if (isRegister()) { 1122 if (!newPinOrPuk.equals(mSic)) { 1123 // password mismatch; return error 1124 handlePasswordError(com.android.internal.R.string.mismatchPin); 1125 } else if (pinLen < 4 || pinLen > 8 ) { 1126 // invalid length 1127 handlePasswordError(com.android.internal.R.string.invalidPin); 1128 } else if (mSc.equals(SC_PIN) 1129 && mUiccApplication != null 1130 && mUiccApplication.getState() == AppState.APPSTATE_PUK) { 1131 // Sim is puk-locked 1132 handlePasswordError(com.android.internal.R.string.needPuk); 1133 } else if (mUiccApplication != null) { 1134 Rlog.d(LOG_TAG, 1135 "processCode: process mmi service code using UiccApp sc=" + mSc); 1136 1137 // We have an app and the pre-checks are OK 1138 if (mSc.equals(SC_PIN)) { 1139 mUiccApplication.changeIccLockPassword(oldPinOrPuk, newPinOrPuk, 1140 obtainMessage(EVENT_SET_COMPLETE, this)); 1141 } else if (mSc.equals(SC_PIN2)) { 1142 mUiccApplication.changeIccFdnPassword(oldPinOrPuk, newPinOrPuk, 1143 obtainMessage(EVENT_SET_COMPLETE, this)); 1144 } else if (mSc.equals(SC_PUK)) { 1145 mUiccApplication.supplyPuk(oldPinOrPuk, newPinOrPuk, 1146 obtainMessage(EVENT_SET_COMPLETE, this)); 1147 } else if (mSc.equals(SC_PUK2)) { 1148 mUiccApplication.supplyPuk2(oldPinOrPuk, newPinOrPuk, 1149 obtainMessage(EVENT_SET_COMPLETE, this)); 1150 } else { 1151 throw new RuntimeException("uicc unsupported service code=" + mSc); 1152 } 1153 } else { 1154 throw new RuntimeException("No application mUiccApplicaiton is null"); 1155 } 1156 } else { 1157 throw new RuntimeException ("Ivalid register/action=" + mAction); 1158 } 1159 } else if (mPoundString != null) { 1160 if (mContext.getResources().getBoolean( 1161 com.android.internal.R.bool.config_allow_ussd_over_ims)) { 1162 int ussd_method = getIntCarrierConfig( 1163 CarrierConfigManager.KEY_CARRIER_USSD_METHOD_INT); 1164 if (ussd_method != USSD_OVER_IMS_ONLY) { 1165 sendUssd(mPoundString); 1166 } else { 1167 throw new RuntimeException("The USSD request is not allowed over CS"); 1168 } 1169 } else { 1170 sendUssd(mPoundString); 1171 } 1172 } else { 1173 Rlog.d(LOG_TAG, "processCode: Invalid or Unsupported MMI Code"); 1174 throw new RuntimeException ("Invalid or Unsupported MMI Code"); 1175 } 1176 } catch (RuntimeException exc) { 1177 mState = State.FAILED; 1178 mMessage = mContext.getText(com.android.internal.R.string.mmiError); 1179 Rlog.d(LOG_TAG, "processCode: RuntimeException=" + exc); 1180 mPhone.onMMIDone(this); 1181 } 1182 } 1183 handlePasswordError(int res)1184 private void handlePasswordError(int res) { 1185 mState = State.FAILED; 1186 StringBuilder sb = new StringBuilder(getScString()); 1187 sb.append("\n"); 1188 sb.append(mContext.getText(res)); 1189 mMessage = sb; 1190 mPhone.onMMIDone(this); 1191 } 1192 1193 /** 1194 * Called from GsmCdmaPhone 1195 * 1196 * An unsolicited USSD NOTIFY or REQUEST has come in matching 1197 * up with this pending USSD request 1198 * 1199 * Note: If REQUEST, this exchange is complete, but the session remains 1200 * active (ie, the network expects user input). 1201 */ 1202 public void onUssdFinished(String ussdMessage, boolean isUssdRequest)1203 onUssdFinished(String ussdMessage, boolean isUssdRequest) { 1204 if (mState == State.PENDING) { 1205 if (TextUtils.isEmpty(ussdMessage)) { 1206 Rlog.d(LOG_TAG, "onUssdFinished: no network provided message; using default."); 1207 mMessage = mContext.getText(com.android.internal.R.string.mmiComplete); 1208 } else { 1209 mMessage = ussdMessage; 1210 } 1211 mIsUssdRequest = isUssdRequest; 1212 // If it's a request, leave it PENDING so that it's cancelable. 1213 if (!isUssdRequest) { 1214 mState = State.COMPLETE; 1215 } 1216 Rlog.d(LOG_TAG, "onUssdFinished: ussdMessage=" + ussdMessage); 1217 mPhone.onMMIDone(this); 1218 } 1219 } 1220 1221 /** 1222 * Called from GsmCdmaPhone 1223 * 1224 * The radio has reset, and this is still pending 1225 */ 1226 1227 public void onUssdFinishedError()1228 onUssdFinishedError() { 1229 if (mState == State.PENDING) { 1230 mState = State.FAILED; 1231 if (TextUtils.isEmpty(mMessage)) { 1232 mMessage = mContext.getText(com.android.internal.R.string.mmiError); 1233 } 1234 Rlog.d(LOG_TAG, "onUssdFinishedError"); 1235 mPhone.onMMIDone(this); 1236 } 1237 } 1238 1239 /** 1240 * Called from GsmCdmaPhone 1241 * 1242 * An unsolicited USSD NOTIFY or REQUEST has come in matching 1243 * up with this pending USSD request 1244 * 1245 * Note: If REQUEST, this exchange is complete, but the session remains 1246 * active (ie, the network expects user input). 1247 */ 1248 public void onUssdRelease()1249 onUssdRelease() { 1250 if (mState == State.PENDING) { 1251 mState = State.COMPLETE; 1252 mMessage = null; 1253 Rlog.d(LOG_TAG, "onUssdRelease"); 1254 mPhone.onMMIDone(this); 1255 } 1256 } 1257 sendUssd(String ussdMessage)1258 public void sendUssd(String ussdMessage) { 1259 // Treat this as a USSD string 1260 mIsPendingUSSD = true; 1261 1262 // Note that unlike most everything else, the USSD complete 1263 // response does not complete this MMI code...we wait for 1264 // an unsolicited USSD "Notify" or "Request". 1265 // The matching up of this is done in GsmCdmaPhone. 1266 mPhone.mCi.sendUSSD(ussdMessage, 1267 obtainMessage(EVENT_USSD_COMPLETE, this)); 1268 } 1269 1270 /** Called from GsmCdmaPhone.handleMessage; not a Handler subclass */ 1271 @Override 1272 public void handleMessage(Message msg)1273 handleMessage (Message msg) { 1274 AsyncResult ar; 1275 1276 switch (msg.what) { 1277 case EVENT_SET_COMPLETE: 1278 ar = (AsyncResult) (msg.obj); 1279 1280 onSetComplete(msg, ar); 1281 break; 1282 1283 case EVENT_SET_CFF_COMPLETE: 1284 ar = (AsyncResult) (msg.obj); 1285 1286 /* 1287 * msg.arg1 = 1 means to set unconditional voice call forwarding 1288 * msg.arg2 = 1 means to enable voice call forwarding 1289 */ 1290 if ((ar.exception == null) && (msg.arg1 == 1)) { 1291 boolean cffEnabled = (msg.arg2 == 1); 1292 mPhone.setVoiceCallForwardingFlag(1, cffEnabled, mDialingNumber); 1293 } 1294 1295 onSetComplete(msg, ar); 1296 break; 1297 1298 case EVENT_GET_CLIR_COMPLETE: 1299 ar = (AsyncResult) (msg.obj); 1300 onGetClirComplete(ar); 1301 break; 1302 1303 case EVENT_QUERY_CF_COMPLETE: 1304 ar = (AsyncResult) (msg.obj); 1305 onQueryCfComplete(ar); 1306 break; 1307 1308 case EVENT_QUERY_COMPLETE: 1309 ar = (AsyncResult) (msg.obj); 1310 onQueryComplete(ar); 1311 break; 1312 1313 case EVENT_USSD_COMPLETE: 1314 ar = (AsyncResult) (msg.obj); 1315 1316 if (ar.exception != null) { 1317 mState = State.FAILED; 1318 mMessage = getErrorMessage(ar); 1319 1320 mPhone.onMMIDone(this); 1321 } 1322 1323 // Note that unlike most everything else, the USSD complete 1324 // response does not complete this MMI code...we wait for 1325 // an unsolicited USSD "Notify" or "Request". 1326 // The matching up of this is done in GsmCdmaPhone. 1327 1328 break; 1329 1330 case EVENT_USSD_CANCEL_COMPLETE: 1331 mPhone.onMMIDone(this); 1332 break; 1333 } 1334 } 1335 //***** Private instance methods 1336 getErrorMessage(AsyncResult ar)1337 private CharSequence getErrorMessage(AsyncResult ar) { 1338 1339 if (ar.exception instanceof CommandException) { 1340 CommandException.Error err = ((CommandException)(ar.exception)).getCommandError(); 1341 if (err == CommandException.Error.FDN_CHECK_FAILURE) { 1342 Rlog.i(LOG_TAG, "FDN_CHECK_FAILURE"); 1343 return mContext.getText(com.android.internal.R.string.mmiFdnError); 1344 } else if (err == CommandException.Error.USSD_MODIFIED_TO_DIAL) { 1345 Rlog.i(LOG_TAG, "USSD_MODIFIED_TO_DIAL"); 1346 return mContext.getText(com.android.internal.R.string.stk_cc_ussd_to_dial); 1347 } else if (err == CommandException.Error.USSD_MODIFIED_TO_SS) { 1348 Rlog.i(LOG_TAG, "USSD_MODIFIED_TO_SS"); 1349 return mContext.getText(com.android.internal.R.string.stk_cc_ussd_to_ss); 1350 } else if (err == CommandException.Error.USSD_MODIFIED_TO_USSD) { 1351 Rlog.i(LOG_TAG, "USSD_MODIFIED_TO_USSD"); 1352 return mContext.getText(com.android.internal.R.string.stk_cc_ussd_to_ussd); 1353 } else if (err == CommandException.Error.SS_MODIFIED_TO_DIAL) { 1354 Rlog.i(LOG_TAG, "SS_MODIFIED_TO_DIAL"); 1355 return mContext.getText(com.android.internal.R.string.stk_cc_ss_to_dial); 1356 } else if (err == CommandException.Error.SS_MODIFIED_TO_USSD) { 1357 Rlog.i(LOG_TAG, "SS_MODIFIED_TO_USSD"); 1358 return mContext.getText(com.android.internal.R.string.stk_cc_ss_to_ussd); 1359 } else if (err == CommandException.Error.SS_MODIFIED_TO_SS) { 1360 Rlog.i(LOG_TAG, "SS_MODIFIED_TO_SS"); 1361 return mContext.getText(com.android.internal.R.string.stk_cc_ss_to_ss); 1362 } else if (err == CommandException.Error.OEM_ERROR_1) { 1363 Rlog.i(LOG_TAG, "OEM_ERROR_1 USSD_MODIFIED_TO_DIAL_VIDEO"); 1364 return mContext.getText(com.android.internal.R.string.stk_cc_ussd_to_dial_video); 1365 } 1366 } 1367 1368 return mContext.getText(com.android.internal.R.string.mmiError); 1369 } 1370 1371 @UnsupportedAppUsage getScString()1372 private CharSequence getScString() { 1373 if (mSc != null) { 1374 if (isServiceCodeCallBarring(mSc)) { 1375 return mContext.getText(com.android.internal.R.string.BaMmi); 1376 } else if (isServiceCodeCallForwarding(mSc)) { 1377 return mContext.getText(com.android.internal.R.string.CfMmi); 1378 } else if (mSc.equals(SC_CLIP)) { 1379 return mContext.getText(com.android.internal.R.string.ClipMmi); 1380 } else if (mSc.equals(SC_CLIR)) { 1381 return mContext.getText(com.android.internal.R.string.ClirMmi); 1382 } else if (mSc.equals(SC_PWD)) { 1383 return mContext.getText(com.android.internal.R.string.PwdMmi); 1384 } else if (mSc.equals(SC_WAIT)) { 1385 return mContext.getText(com.android.internal.R.string.CwMmi); 1386 } else if (isPinPukCommand()) { 1387 return mContext.getText(com.android.internal.R.string.PinMmi); 1388 } 1389 } 1390 1391 return ""; 1392 } 1393 1394 private void onSetComplete(Message msg, AsyncResult ar)1395 onSetComplete(Message msg, AsyncResult ar){ 1396 StringBuilder sb = new StringBuilder(getScString()); 1397 sb.append("\n"); 1398 1399 if (ar.exception != null) { 1400 mState = State.FAILED; 1401 if (ar.exception instanceof CommandException) { 1402 CommandException.Error err = ((CommandException)(ar.exception)).getCommandError(); 1403 if (err == CommandException.Error.PASSWORD_INCORRECT) { 1404 if (isPinPukCommand()) { 1405 // look specifically for the PUK commands and adjust 1406 // the message accordingly. 1407 if (mSc.equals(SC_PUK) || mSc.equals(SC_PUK2)) { 1408 sb.append(mContext.getText( 1409 com.android.internal.R.string.badPuk)); 1410 } else { 1411 sb.append(mContext.getText( 1412 com.android.internal.R.string.badPin)); 1413 } 1414 // Get the No. of retries remaining to unlock PUK/PUK2 1415 int attemptsRemaining = msg.arg1; 1416 if (attemptsRemaining <= 0) { 1417 Rlog.d(LOG_TAG, "onSetComplete: PUK locked," 1418 + " cancel as lock screen will handle this"); 1419 mState = State.CANCELLED; 1420 } else if (attemptsRemaining > 0) { 1421 Rlog.d(LOG_TAG, "onSetComplete: attemptsRemaining="+attemptsRemaining); 1422 sb.append(mContext.getResources().getQuantityString( 1423 com.android.internal.R.plurals.pinpuk_attempts, 1424 attemptsRemaining, attemptsRemaining)); 1425 } 1426 } else { 1427 sb.append(mContext.getText( 1428 com.android.internal.R.string.passwordIncorrect)); 1429 } 1430 } else if (err == CommandException.Error.SIM_PUK2) { 1431 sb.append(mContext.getText( 1432 com.android.internal.R.string.badPin)); 1433 sb.append("\n"); 1434 sb.append(mContext.getText( 1435 com.android.internal.R.string.needPuk2)); 1436 } else if (err == CommandException.Error.REQUEST_NOT_SUPPORTED) { 1437 if (mSc.equals(SC_PIN)) { 1438 sb.append(mContext.getText(com.android.internal.R.string.enablePin)); 1439 } 1440 } else if (err == CommandException.Error.FDN_CHECK_FAILURE) { 1441 Rlog.i(LOG_TAG, "FDN_CHECK_FAILURE"); 1442 sb.append(mContext.getText(com.android.internal.R.string.mmiFdnError)); 1443 } else if (err == CommandException.Error.MODEM_ERR) { 1444 // Some carriers do not allow changing call forwarding settings while roaming 1445 // and will return an error from the modem. 1446 if (isServiceCodeCallForwarding(mSc) 1447 && mPhone.getServiceState().getVoiceRoaming() 1448 && !mPhone.supports3gppCallForwardingWhileRoaming()) { 1449 sb.append(mContext.getText( 1450 com.android.internal.R.string.mmiErrorWhileRoaming)); 1451 } else { 1452 sb.append(getErrorMessage(ar)); 1453 } 1454 } else { 1455 sb.append(getErrorMessage(ar)); 1456 } 1457 } else { 1458 sb.append(mContext.getText( 1459 com.android.internal.R.string.mmiError)); 1460 } 1461 } else if (isActivate()) { 1462 mState = State.COMPLETE; 1463 if (mIsCallFwdReg) { 1464 sb.append(mContext.getText( 1465 com.android.internal.R.string.serviceRegistered)); 1466 } else { 1467 sb.append(mContext.getText( 1468 com.android.internal.R.string.serviceEnabled)); 1469 } 1470 // Record CLIR setting 1471 if (mSc.equals(SC_CLIR)) { 1472 mPhone.saveClirSetting(CommandsInterface.CLIR_INVOCATION); 1473 } 1474 } else if (isDeactivate()) { 1475 mState = State.COMPLETE; 1476 sb.append(mContext.getText( 1477 com.android.internal.R.string.serviceDisabled)); 1478 // Record CLIR setting 1479 if (mSc.equals(SC_CLIR)) { 1480 mPhone.saveClirSetting(CommandsInterface.CLIR_SUPPRESSION); 1481 } 1482 } else if (isRegister()) { 1483 mState = State.COMPLETE; 1484 sb.append(mContext.getText( 1485 com.android.internal.R.string.serviceRegistered)); 1486 } else if (isErasure()) { 1487 mState = State.COMPLETE; 1488 sb.append(mContext.getText( 1489 com.android.internal.R.string.serviceErased)); 1490 } else { 1491 mState = State.FAILED; 1492 sb.append(mContext.getText( 1493 com.android.internal.R.string.mmiError)); 1494 } 1495 1496 mMessage = sb; 1497 Rlog.d(LOG_TAG, "onSetComplete mmi=" + this); 1498 mPhone.onMMIDone(this); 1499 } 1500 1501 private void onGetClirComplete(AsyncResult ar)1502 onGetClirComplete(AsyncResult ar) { 1503 StringBuilder sb = new StringBuilder(getScString()); 1504 sb.append("\n"); 1505 1506 if (ar.exception != null) { 1507 mState = State.FAILED; 1508 sb.append(getErrorMessage(ar)); 1509 } else { 1510 int clirArgs[]; 1511 1512 clirArgs = (int[])ar.result; 1513 1514 // the 'm' parameter from TS 27.007 7.7 1515 switch (clirArgs[1]) { 1516 case 0: // CLIR not provisioned 1517 sb.append(mContext.getText( 1518 com.android.internal.R.string.serviceNotProvisioned)); 1519 mState = State.COMPLETE; 1520 break; 1521 1522 case 1: // CLIR provisioned in permanent mode 1523 sb.append(mContext.getText( 1524 com.android.internal.R.string.CLIRPermanent)); 1525 mState = State.COMPLETE; 1526 break; 1527 1528 case 2: // unknown (e.g. no network, etc.) 1529 sb.append(mContext.getText( 1530 com.android.internal.R.string.mmiError)); 1531 mState = State.FAILED; 1532 break; 1533 1534 case 3: // CLIR temporary mode presentation restricted 1535 1536 // the 'n' parameter from TS 27.007 7.7 1537 switch (clirArgs[0]) { 1538 default: 1539 case 0: // Default 1540 sb.append(mContext.getText( 1541 com.android.internal.R.string.CLIRDefaultOnNextCallOn)); 1542 break; 1543 case 1: // CLIR invocation 1544 sb.append(mContext.getText( 1545 com.android.internal.R.string.CLIRDefaultOnNextCallOn)); 1546 break; 1547 case 2: // CLIR suppression 1548 sb.append(mContext.getText( 1549 com.android.internal.R.string.CLIRDefaultOnNextCallOff)); 1550 break; 1551 } 1552 mState = State.COMPLETE; 1553 break; 1554 1555 case 4: // CLIR temporary mode presentation allowed 1556 // the 'n' parameter from TS 27.007 7.7 1557 switch (clirArgs[0]) { 1558 default: 1559 case 0: // Default 1560 sb.append(mContext.getText( 1561 com.android.internal.R.string.CLIRDefaultOffNextCallOff)); 1562 break; 1563 case 1: // CLIR invocation 1564 sb.append(mContext.getText( 1565 com.android.internal.R.string.CLIRDefaultOffNextCallOn)); 1566 break; 1567 case 2: // CLIR suppression 1568 sb.append(mContext.getText( 1569 com.android.internal.R.string.CLIRDefaultOffNextCallOff)); 1570 break; 1571 } 1572 1573 mState = State.COMPLETE; 1574 break; 1575 } 1576 } 1577 1578 mMessage = sb; 1579 Rlog.d(LOG_TAG, "onGetClirComplete: mmi=" + this); 1580 mPhone.onMMIDone(this); 1581 } 1582 1583 /** 1584 * @param serviceClass 1 bit of the service class bit vectory 1585 * @return String to be used for call forward query MMI response text. 1586 * Returns null if unrecognized 1587 */ 1588 1589 private CharSequence serviceClassToCFString(int serviceClass)1590 serviceClassToCFString (int serviceClass) { 1591 switch (serviceClass) { 1592 case SERVICE_CLASS_VOICE: 1593 return mContext.getText(com.android.internal.R.string.serviceClassVoice); 1594 case SERVICE_CLASS_DATA: 1595 return mContext.getText(com.android.internal.R.string.serviceClassData); 1596 case SERVICE_CLASS_FAX: 1597 return mContext.getText(com.android.internal.R.string.serviceClassFAX); 1598 case SERVICE_CLASS_SMS: 1599 return mContext.getText(com.android.internal.R.string.serviceClassSMS); 1600 case SERVICE_CLASS_DATA_SYNC: 1601 return mContext.getText(com.android.internal.R.string.serviceClassDataSync); 1602 case SERVICE_CLASS_DATA_ASYNC: 1603 return mContext.getText(com.android.internal.R.string.serviceClassDataAsync); 1604 case SERVICE_CLASS_PACKET: 1605 return mContext.getText(com.android.internal.R.string.serviceClassPacket); 1606 case SERVICE_CLASS_PAD: 1607 return mContext.getText(com.android.internal.R.string.serviceClassPAD); 1608 default: 1609 return null; 1610 } 1611 } 1612 1613 1614 /** one CallForwardInfo + serviceClassMask -> one line of text */ 1615 private CharSequence makeCFQueryResultMessage(CallForwardInfo info, int serviceClassMask)1616 makeCFQueryResultMessage(CallForwardInfo info, int serviceClassMask) { 1617 CharSequence template; 1618 String sources[] = {"{0}", "{1}", "{2}"}; 1619 CharSequence destinations[] = new CharSequence[3]; 1620 boolean needTimeTemplate; 1621 1622 // CF_REASON_NO_REPLY also has a time value associated with 1623 // it. All others don't. 1624 1625 needTimeTemplate = 1626 (info.reason == CommandsInterface.CF_REASON_NO_REPLY); 1627 1628 if (info.status == 1) { 1629 if (needTimeTemplate) { 1630 template = mContext.getText( 1631 com.android.internal.R.string.cfTemplateForwardedTime); 1632 } else { 1633 template = mContext.getText( 1634 com.android.internal.R.string.cfTemplateForwarded); 1635 } 1636 } else if (info.status == 0 && isEmptyOrNull(info.number)) { 1637 template = mContext.getText( 1638 com.android.internal.R.string.cfTemplateNotForwarded); 1639 } else { /* (info.status == 0) && !isEmptyOrNull(info.number) */ 1640 // A call forward record that is not active but contains 1641 // a phone number is considered "registered" 1642 1643 if (needTimeTemplate) { 1644 template = mContext.getText( 1645 com.android.internal.R.string.cfTemplateRegisteredTime); 1646 } else { 1647 template = mContext.getText( 1648 com.android.internal.R.string.cfTemplateRegistered); 1649 } 1650 } 1651 1652 // In the template (from strings.xmls) 1653 // {0} is one of "bearerServiceCode*" 1654 // {1} is dialing number 1655 // {2} is time in seconds 1656 1657 destinations[0] = serviceClassToCFString(info.serviceClass & serviceClassMask); 1658 destinations[1] = formatLtr( 1659 PhoneNumberUtils.stringFromStringAndTOA(info.number, info.toa)); 1660 destinations[2] = Integer.toString(info.timeSeconds); 1661 1662 if (info.reason == CommandsInterface.CF_REASON_UNCONDITIONAL && 1663 (info.serviceClass & serviceClassMask) 1664 == CommandsInterface.SERVICE_CLASS_VOICE) { 1665 boolean cffEnabled = (info.status == 1); 1666 mPhone.setVoiceCallForwardingFlag(1, cffEnabled, info.number); 1667 } 1668 1669 return TextUtils.replace(template, sources, destinations); 1670 } 1671 1672 /** 1673 * Used to format a string that should be displayed as LTR even in RTL locales 1674 */ formatLtr(String str)1675 private String formatLtr(String str) { 1676 BidiFormatter fmt = BidiFormatter.getInstance(); 1677 return str == null ? str : fmt.unicodeWrap(str, TextDirectionHeuristics.LTR, true); 1678 } 1679 1680 private void onQueryCfComplete(AsyncResult ar)1681 onQueryCfComplete(AsyncResult ar) { 1682 StringBuilder sb = new StringBuilder(getScString()); 1683 sb.append("\n"); 1684 1685 if (ar.exception != null) { 1686 mState = State.FAILED; 1687 sb.append(getErrorMessage(ar)); 1688 } else { 1689 CallForwardInfo infos[]; 1690 1691 infos = (CallForwardInfo[]) ar.result; 1692 1693 if (infos == null || infos.length == 0) { 1694 // Assume the default is not active 1695 sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled)); 1696 1697 // Set unconditional CFF in SIM to false 1698 mPhone.setVoiceCallForwardingFlag(1, false, null); 1699 } else { 1700 1701 SpannableStringBuilder tb = new SpannableStringBuilder(); 1702 1703 // Each bit in the service class gets its own result line 1704 // The service classes may be split up over multiple 1705 // CallForwardInfos. So, for each service class, find out 1706 // which CallForwardInfo represents it and then build 1707 // the response text based on that 1708 1709 for (int serviceClassMask = 1 1710 ; serviceClassMask <= SERVICE_CLASS_MAX 1711 ; serviceClassMask <<= 1 1712 ) { 1713 for (int i = 0, s = infos.length; i < s ; i++) { 1714 if ((serviceClassMask & infos[i].serviceClass) != 0) { 1715 tb.append(makeCFQueryResultMessage(infos[i], 1716 serviceClassMask)); 1717 tb.append("\n"); 1718 } 1719 } 1720 } 1721 sb.append(tb); 1722 } 1723 1724 mState = State.COMPLETE; 1725 } 1726 1727 mMessage = sb; 1728 Rlog.d(LOG_TAG, "onQueryCfComplete: mmi=" + this); 1729 mPhone.onMMIDone(this); 1730 1731 } 1732 1733 private void onQueryComplete(AsyncResult ar)1734 onQueryComplete(AsyncResult ar) { 1735 StringBuilder sb = new StringBuilder(getScString()); 1736 sb.append("\n"); 1737 1738 if (ar.exception != null) { 1739 mState = State.FAILED; 1740 sb.append(getErrorMessage(ar)); 1741 } else { 1742 int[] ints = (int[])ar.result; 1743 1744 if (ints.length != 0) { 1745 if (ints[0] == 0) { 1746 sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled)); 1747 } else if (mSc.equals(SC_WAIT)) { 1748 // Call Waiting includes additional data in the response. 1749 sb.append(createQueryCallWaitingResultMessage(ints[1])); 1750 } else if (isServiceCodeCallBarring(mSc)) { 1751 // ints[0] for Call Barring is a bit vector of services 1752 sb.append(createQueryCallBarringResultMessage(ints[0])); 1753 } else if (ints[0] == 1) { 1754 // for all other services, treat it as a boolean 1755 sb.append(mContext.getText(com.android.internal.R.string.serviceEnabled)); 1756 } else { 1757 sb.append(mContext.getText(com.android.internal.R.string.mmiError)); 1758 } 1759 } else { 1760 sb.append(mContext.getText(com.android.internal.R.string.mmiError)); 1761 } 1762 mState = State.COMPLETE; 1763 } 1764 1765 mMessage = sb; 1766 Rlog.d(LOG_TAG, "onQueryComplete: mmi=" + this); 1767 mPhone.onMMIDone(this); 1768 } 1769 1770 private CharSequence createQueryCallWaitingResultMessage(int serviceClass)1771 createQueryCallWaitingResultMessage(int serviceClass) { 1772 StringBuilder sb = 1773 new StringBuilder( 1774 mContext.getText(com.android.internal.R.string.serviceEnabledFor)); 1775 1776 for (int classMask = 1 1777 ; classMask <= SERVICE_CLASS_MAX 1778 ; classMask <<= 1 1779 ) { 1780 if ((classMask & serviceClass) != 0) { 1781 sb.append("\n"); 1782 sb.append(serviceClassToCFString(classMask & serviceClass)); 1783 } 1784 } 1785 return sb; 1786 } 1787 private CharSequence createQueryCallBarringResultMessage(int serviceClass)1788 createQueryCallBarringResultMessage(int serviceClass) 1789 { 1790 StringBuilder sb = new StringBuilder( 1791 mContext.getText(com.android.internal.R.string.serviceEnabledFor)); 1792 1793 for (int classMask = 1 1794 ; classMask <= SERVICE_CLASS_MAX 1795 ; classMask <<= 1 1796 ) { 1797 if ((classMask & serviceClass) != 0) { 1798 sb.append("\n"); 1799 sb.append(serviceClassToCFString(classMask & serviceClass)); 1800 } 1801 } 1802 return sb; 1803 } 1804 1805 /** 1806 * Get the int config from carrier config manager. 1807 * 1808 * @param key config key defined in CarrierConfigManager 1809 * @return integer value of corresponding key. 1810 */ getIntCarrierConfig(String key)1811 private int getIntCarrierConfig(String key) { 1812 CarrierConfigManager ConfigManager = mContext.getSystemService(CarrierConfigManager.class); 1813 PersistableBundle b = null; 1814 if (ConfigManager != null) { 1815 // If an invalid subId is used, this bundle will contain default values. 1816 b = ConfigManager.getConfigForSubId(mPhone.getSubId()); 1817 } 1818 if (b != null) { 1819 return b.getInt(key); 1820 } else { 1821 // Return static default defined in CarrierConfigManager. 1822 return CarrierConfigManager.getDefaultConfig().getInt(key); 1823 } 1824 } 1825 getUssdCallbackReceiver()1826 public ResultReceiver getUssdCallbackReceiver() { 1827 return this.mCallbackReceiver; 1828 } 1829 1830 /*** 1831 * TODO: It would be nice to have a method here that can take in a dialstring and 1832 * figure out if there is an MMI code embedded within it. This code would replace 1833 * some of the string parsing functionality in the Phone App's 1834 * SpecialCharSequenceMgr class. 1835 */ 1836 1837 @Override toString()1838 public String toString() { 1839 StringBuilder sb = new StringBuilder("GsmMmiCode {"); 1840 1841 sb.append("State=" + getState()); 1842 if (mAction != null) sb.append(" action=" + mAction); 1843 if (mSc != null) sb.append(" sc=" + mSc); 1844 if (mSia != null) sb.append(" sia=" + Rlog.pii(LOG_TAG, mSia)); 1845 if (mSib != null) sb.append(" sib=" + Rlog.pii(LOG_TAG, mSib)); 1846 if (mSic != null) sb.append(" sic=" + Rlog.pii(LOG_TAG, mSic)); 1847 if (mPoundString != null) sb.append(" poundString=" + Rlog.pii(LOG_TAG, mPoundString)); 1848 if (mDialingNumber != null) { 1849 sb.append(" dialingNumber=" + Rlog.pii(LOG_TAG, mDialingNumber)); 1850 } 1851 if (mPwd != null) sb.append(" pwd=" + Rlog.pii(LOG_TAG, mPwd)); 1852 if (mCallbackReceiver != null) sb.append(" hasReceiver"); 1853 sb.append("}"); 1854 return sb.toString(); 1855 } 1856 } 1857