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 @VisibleForTesting getErrorMessage(AsyncResult ar)1337 public 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 } else if (err == CommandException.Error.REQUEST_NOT_SUPPORTED 1366 || err == CommandException.Error.OPERATION_NOT_ALLOWED) { 1367 Rlog.i(LOG_TAG, "REQUEST_NOT_SUPPORTED/OPERATION_NOT_ALLOWED"); 1368 // getResources().getText() is the same as getText(), however getText() is final and 1369 // cannot be mocked in tests. 1370 return mContext.getResources().getText( 1371 com.android.internal.R.string.mmiErrorNotSupported); 1372 } 1373 } 1374 1375 return mContext.getText(com.android.internal.R.string.mmiError); 1376 } 1377 1378 @UnsupportedAppUsage getScString()1379 private CharSequence getScString() { 1380 if (mSc != null) { 1381 if (isServiceCodeCallBarring(mSc)) { 1382 return mContext.getText(com.android.internal.R.string.BaMmi); 1383 } else if (isServiceCodeCallForwarding(mSc)) { 1384 return mContext.getText(com.android.internal.R.string.CfMmi); 1385 } else if (mSc.equals(SC_CLIP)) { 1386 return mContext.getText(com.android.internal.R.string.ClipMmi); 1387 } else if (mSc.equals(SC_CLIR)) { 1388 return mContext.getText(com.android.internal.R.string.ClirMmi); 1389 } else if (mSc.equals(SC_PWD)) { 1390 return mContext.getText(com.android.internal.R.string.PwdMmi); 1391 } else if (mSc.equals(SC_WAIT)) { 1392 return mContext.getText(com.android.internal.R.string.CwMmi); 1393 } else if (isPinPukCommand()) { 1394 return mContext.getText(com.android.internal.R.string.PinMmi); 1395 } 1396 } 1397 1398 return ""; 1399 } 1400 1401 private void onSetComplete(Message msg, AsyncResult ar)1402 onSetComplete(Message msg, AsyncResult ar){ 1403 StringBuilder sb = new StringBuilder(getScString()); 1404 sb.append("\n"); 1405 1406 if (ar.exception != null) { 1407 mState = State.FAILED; 1408 if (ar.exception instanceof CommandException) { 1409 CommandException.Error err = ((CommandException)(ar.exception)).getCommandError(); 1410 if (err == CommandException.Error.PASSWORD_INCORRECT) { 1411 if (isPinPukCommand()) { 1412 // look specifically for the PUK commands and adjust 1413 // the message accordingly. 1414 if (mSc.equals(SC_PUK) || mSc.equals(SC_PUK2)) { 1415 sb.append(mContext.getText( 1416 com.android.internal.R.string.badPuk)); 1417 } else { 1418 sb.append(mContext.getText( 1419 com.android.internal.R.string.badPin)); 1420 } 1421 // Get the No. of retries remaining to unlock PUK/PUK2 1422 int attemptsRemaining = msg.arg1; 1423 if (attemptsRemaining <= 0) { 1424 Rlog.d(LOG_TAG, "onSetComplete: PUK locked," 1425 + " cancel as lock screen will handle this"); 1426 mState = State.CANCELLED; 1427 } else if (attemptsRemaining > 0) { 1428 Rlog.d(LOG_TAG, "onSetComplete: attemptsRemaining="+attemptsRemaining); 1429 sb.append(mContext.getResources().getQuantityString( 1430 com.android.internal.R.plurals.pinpuk_attempts, 1431 attemptsRemaining, attemptsRemaining)); 1432 } 1433 } else { 1434 sb.append(mContext.getText( 1435 com.android.internal.R.string.passwordIncorrect)); 1436 } 1437 } else if (err == CommandException.Error.SIM_PUK2) { 1438 sb.append(mContext.getText( 1439 com.android.internal.R.string.badPin)); 1440 sb.append("\n"); 1441 sb.append(mContext.getText( 1442 com.android.internal.R.string.needPuk2)); 1443 } else if (err == CommandException.Error.REQUEST_NOT_SUPPORTED) { 1444 if (mSc.equals(SC_PIN)) { 1445 sb.append(mContext.getText(com.android.internal.R.string.enablePin)); 1446 } 1447 } else if (err == CommandException.Error.FDN_CHECK_FAILURE) { 1448 Rlog.i(LOG_TAG, "FDN_CHECK_FAILURE"); 1449 sb.append(mContext.getText(com.android.internal.R.string.mmiFdnError)); 1450 } else if (err == CommandException.Error.MODEM_ERR) { 1451 // Some carriers do not allow changing call forwarding settings while roaming 1452 // and will return an error from the modem. 1453 if (isServiceCodeCallForwarding(mSc) 1454 && mPhone.getServiceState().getVoiceRoaming() 1455 && !mPhone.supports3gppCallForwardingWhileRoaming()) { 1456 sb.append(mContext.getText( 1457 com.android.internal.R.string.mmiErrorWhileRoaming)); 1458 } else { 1459 sb.append(getErrorMessage(ar)); 1460 } 1461 } else { 1462 sb.append(getErrorMessage(ar)); 1463 } 1464 } else { 1465 sb.append(mContext.getText( 1466 com.android.internal.R.string.mmiError)); 1467 } 1468 } else if (isActivate()) { 1469 mState = State.COMPLETE; 1470 if (mIsCallFwdReg) { 1471 sb.append(mContext.getText( 1472 com.android.internal.R.string.serviceRegistered)); 1473 } else { 1474 sb.append(mContext.getText( 1475 com.android.internal.R.string.serviceEnabled)); 1476 } 1477 // Record CLIR setting 1478 if (mSc.equals(SC_CLIR)) { 1479 mPhone.saveClirSetting(CommandsInterface.CLIR_INVOCATION); 1480 } 1481 } else if (isDeactivate()) { 1482 mState = State.COMPLETE; 1483 sb.append(mContext.getText( 1484 com.android.internal.R.string.serviceDisabled)); 1485 // Record CLIR setting 1486 if (mSc.equals(SC_CLIR)) { 1487 mPhone.saveClirSetting(CommandsInterface.CLIR_SUPPRESSION); 1488 } 1489 } else if (isRegister()) { 1490 mState = State.COMPLETE; 1491 sb.append(mContext.getText( 1492 com.android.internal.R.string.serviceRegistered)); 1493 } else if (isErasure()) { 1494 mState = State.COMPLETE; 1495 sb.append(mContext.getText( 1496 com.android.internal.R.string.serviceErased)); 1497 } else { 1498 mState = State.FAILED; 1499 sb.append(mContext.getText( 1500 com.android.internal.R.string.mmiError)); 1501 } 1502 1503 mMessage = sb; 1504 Rlog.d(LOG_TAG, "onSetComplete mmi=" + this); 1505 mPhone.onMMIDone(this); 1506 } 1507 1508 private void onGetClirComplete(AsyncResult ar)1509 onGetClirComplete(AsyncResult ar) { 1510 StringBuilder sb = new StringBuilder(getScString()); 1511 sb.append("\n"); 1512 1513 if (ar.exception != null) { 1514 mState = State.FAILED; 1515 sb.append(getErrorMessage(ar)); 1516 } else { 1517 int clirArgs[]; 1518 1519 clirArgs = (int[])ar.result; 1520 1521 // the 'm' parameter from TS 27.007 7.7 1522 switch (clirArgs[1]) { 1523 case 0: // CLIR not provisioned 1524 sb.append(mContext.getText( 1525 com.android.internal.R.string.serviceNotProvisioned)); 1526 mState = State.COMPLETE; 1527 break; 1528 1529 case 1: // CLIR provisioned in permanent mode 1530 sb.append(mContext.getText( 1531 com.android.internal.R.string.CLIRPermanent)); 1532 mState = State.COMPLETE; 1533 break; 1534 1535 case 2: // unknown (e.g. no network, etc.) 1536 sb.append(mContext.getText( 1537 com.android.internal.R.string.mmiError)); 1538 mState = State.FAILED; 1539 break; 1540 1541 case 3: // CLIR temporary mode presentation restricted 1542 1543 // the 'n' parameter from TS 27.007 7.7 1544 switch (clirArgs[0]) { 1545 default: 1546 case 0: // Default 1547 sb.append(mContext.getText( 1548 com.android.internal.R.string.CLIRDefaultOnNextCallOn)); 1549 break; 1550 case 1: // CLIR invocation 1551 sb.append(mContext.getText( 1552 com.android.internal.R.string.CLIRDefaultOnNextCallOn)); 1553 break; 1554 case 2: // CLIR suppression 1555 sb.append(mContext.getText( 1556 com.android.internal.R.string.CLIRDefaultOnNextCallOff)); 1557 break; 1558 } 1559 mState = State.COMPLETE; 1560 break; 1561 1562 case 4: // CLIR temporary mode presentation allowed 1563 // the 'n' parameter from TS 27.007 7.7 1564 switch (clirArgs[0]) { 1565 default: 1566 case 0: // Default 1567 sb.append(mContext.getText( 1568 com.android.internal.R.string.CLIRDefaultOffNextCallOff)); 1569 break; 1570 case 1: // CLIR invocation 1571 sb.append(mContext.getText( 1572 com.android.internal.R.string.CLIRDefaultOffNextCallOn)); 1573 break; 1574 case 2: // CLIR suppression 1575 sb.append(mContext.getText( 1576 com.android.internal.R.string.CLIRDefaultOffNextCallOff)); 1577 break; 1578 } 1579 1580 mState = State.COMPLETE; 1581 break; 1582 } 1583 } 1584 1585 mMessage = sb; 1586 Rlog.d(LOG_TAG, "onGetClirComplete: mmi=" + this); 1587 mPhone.onMMIDone(this); 1588 } 1589 1590 /** 1591 * @param serviceClass 1 bit of the service class bit vectory 1592 * @return String to be used for call forward query MMI response text. 1593 * Returns null if unrecognized 1594 */ 1595 1596 private CharSequence serviceClassToCFString(int serviceClass)1597 serviceClassToCFString (int serviceClass) { 1598 switch (serviceClass) { 1599 case SERVICE_CLASS_VOICE: 1600 return mContext.getText(com.android.internal.R.string.serviceClassVoice); 1601 case SERVICE_CLASS_DATA: 1602 return mContext.getText(com.android.internal.R.string.serviceClassData); 1603 case SERVICE_CLASS_FAX: 1604 return mContext.getText(com.android.internal.R.string.serviceClassFAX); 1605 case SERVICE_CLASS_SMS: 1606 return mContext.getText(com.android.internal.R.string.serviceClassSMS); 1607 case SERVICE_CLASS_DATA_SYNC: 1608 return mContext.getText(com.android.internal.R.string.serviceClassDataSync); 1609 case SERVICE_CLASS_DATA_ASYNC: 1610 return mContext.getText(com.android.internal.R.string.serviceClassDataAsync); 1611 case SERVICE_CLASS_PACKET: 1612 return mContext.getText(com.android.internal.R.string.serviceClassPacket); 1613 case SERVICE_CLASS_PAD: 1614 return mContext.getText(com.android.internal.R.string.serviceClassPAD); 1615 default: 1616 return null; 1617 } 1618 } 1619 1620 1621 /** one CallForwardInfo + serviceClassMask -> one line of text */ 1622 private CharSequence makeCFQueryResultMessage(CallForwardInfo info, int serviceClassMask)1623 makeCFQueryResultMessage(CallForwardInfo info, int serviceClassMask) { 1624 CharSequence template; 1625 String sources[] = {"{0}", "{1}", "{2}"}; 1626 CharSequence destinations[] = new CharSequence[3]; 1627 boolean needTimeTemplate; 1628 1629 // CF_REASON_NO_REPLY also has a time value associated with 1630 // it. All others don't. 1631 1632 needTimeTemplate = 1633 (info.reason == CommandsInterface.CF_REASON_NO_REPLY); 1634 1635 if (info.status == 1) { 1636 if (needTimeTemplate) { 1637 template = mContext.getText( 1638 com.android.internal.R.string.cfTemplateForwardedTime); 1639 } else { 1640 template = mContext.getText( 1641 com.android.internal.R.string.cfTemplateForwarded); 1642 } 1643 } else if (info.status == 0 && isEmptyOrNull(info.number)) { 1644 template = mContext.getText( 1645 com.android.internal.R.string.cfTemplateNotForwarded); 1646 } else { /* (info.status == 0) && !isEmptyOrNull(info.number) */ 1647 // A call forward record that is not active but contains 1648 // a phone number is considered "registered" 1649 1650 if (needTimeTemplate) { 1651 template = mContext.getText( 1652 com.android.internal.R.string.cfTemplateRegisteredTime); 1653 } else { 1654 template = mContext.getText( 1655 com.android.internal.R.string.cfTemplateRegistered); 1656 } 1657 } 1658 1659 // In the template (from strings.xmls) 1660 // {0} is one of "bearerServiceCode*" 1661 // {1} is dialing number 1662 // {2} is time in seconds 1663 1664 destinations[0] = serviceClassToCFString(info.serviceClass & serviceClassMask); 1665 destinations[1] = formatLtr( 1666 PhoneNumberUtils.stringFromStringAndTOA(info.number, info.toa)); 1667 destinations[2] = Integer.toString(info.timeSeconds); 1668 1669 if (info.reason == CommandsInterface.CF_REASON_UNCONDITIONAL && 1670 (info.serviceClass & serviceClassMask) 1671 == CommandsInterface.SERVICE_CLASS_VOICE) { 1672 boolean cffEnabled = (info.status == 1); 1673 mPhone.setVoiceCallForwardingFlag(1, cffEnabled, info.number); 1674 } 1675 1676 return TextUtils.replace(template, sources, destinations); 1677 } 1678 1679 /** 1680 * Used to format a string that should be displayed as LTR even in RTL locales 1681 */ formatLtr(String str)1682 private String formatLtr(String str) { 1683 BidiFormatter fmt = BidiFormatter.getInstance(); 1684 return str == null ? str : fmt.unicodeWrap(str, TextDirectionHeuristics.LTR, true); 1685 } 1686 1687 private void onQueryCfComplete(AsyncResult ar)1688 onQueryCfComplete(AsyncResult ar) { 1689 StringBuilder sb = new StringBuilder(getScString()); 1690 sb.append("\n"); 1691 1692 if (ar.exception != null) { 1693 mState = State.FAILED; 1694 sb.append(getErrorMessage(ar)); 1695 } else { 1696 CallForwardInfo infos[]; 1697 1698 infos = (CallForwardInfo[]) ar.result; 1699 1700 if (infos == null || infos.length == 0) { 1701 // Assume the default is not active 1702 sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled)); 1703 1704 // Set unconditional CFF in SIM to false 1705 mPhone.setVoiceCallForwardingFlag(1, false, null); 1706 } else { 1707 1708 SpannableStringBuilder tb = new SpannableStringBuilder(); 1709 1710 // Each bit in the service class gets its own result line 1711 // The service classes may be split up over multiple 1712 // CallForwardInfos. So, for each service class, find out 1713 // which CallForwardInfo represents it and then build 1714 // the response text based on that 1715 1716 for (int serviceClassMask = 1 1717 ; serviceClassMask <= SERVICE_CLASS_MAX 1718 ; serviceClassMask <<= 1 1719 ) { 1720 for (int i = 0, s = infos.length; i < s ; i++) { 1721 if ((serviceClassMask & infos[i].serviceClass) != 0) { 1722 tb.append(makeCFQueryResultMessage(infos[i], 1723 serviceClassMask)); 1724 tb.append("\n"); 1725 } 1726 } 1727 } 1728 sb.append(tb); 1729 } 1730 1731 mState = State.COMPLETE; 1732 } 1733 1734 mMessage = sb; 1735 Rlog.d(LOG_TAG, "onQueryCfComplete: mmi=" + this); 1736 mPhone.onMMIDone(this); 1737 1738 } 1739 1740 private void onQueryComplete(AsyncResult ar)1741 onQueryComplete(AsyncResult ar) { 1742 StringBuilder sb = new StringBuilder(getScString()); 1743 sb.append("\n"); 1744 1745 if (ar.exception != null) { 1746 mState = State.FAILED; 1747 sb.append(getErrorMessage(ar)); 1748 } else { 1749 int[] ints = (int[])ar.result; 1750 1751 if (ints.length != 0) { 1752 if (ints[0] == 0) { 1753 sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled)); 1754 } else if (mSc.equals(SC_WAIT)) { 1755 // Call Waiting includes additional data in the response. 1756 sb.append(createQueryCallWaitingResultMessage(ints[1])); 1757 } else if (isServiceCodeCallBarring(mSc)) { 1758 // ints[0] for Call Barring is a bit vector of services 1759 sb.append(createQueryCallBarringResultMessage(ints[0])); 1760 } else if (ints[0] == 1) { 1761 // for all other services, treat it as a boolean 1762 sb.append(mContext.getText(com.android.internal.R.string.serviceEnabled)); 1763 } else { 1764 sb.append(mContext.getText(com.android.internal.R.string.mmiError)); 1765 } 1766 } else { 1767 sb.append(mContext.getText(com.android.internal.R.string.mmiError)); 1768 } 1769 mState = State.COMPLETE; 1770 } 1771 1772 mMessage = sb; 1773 Rlog.d(LOG_TAG, "onQueryComplete: mmi=" + this); 1774 mPhone.onMMIDone(this); 1775 } 1776 1777 private CharSequence createQueryCallWaitingResultMessage(int serviceClass)1778 createQueryCallWaitingResultMessage(int serviceClass) { 1779 StringBuilder sb = 1780 new StringBuilder( 1781 mContext.getText(com.android.internal.R.string.serviceEnabledFor)); 1782 1783 for (int classMask = 1 1784 ; classMask <= SERVICE_CLASS_MAX 1785 ; classMask <<= 1 1786 ) { 1787 if ((classMask & serviceClass) != 0) { 1788 sb.append("\n"); 1789 sb.append(serviceClassToCFString(classMask & serviceClass)); 1790 } 1791 } 1792 return sb; 1793 } 1794 private CharSequence createQueryCallBarringResultMessage(int serviceClass)1795 createQueryCallBarringResultMessage(int serviceClass) 1796 { 1797 StringBuilder sb = new StringBuilder( 1798 mContext.getText(com.android.internal.R.string.serviceEnabledFor)); 1799 1800 for (int classMask = 1 1801 ; classMask <= SERVICE_CLASS_MAX 1802 ; classMask <<= 1 1803 ) { 1804 if ((classMask & serviceClass) != 0) { 1805 sb.append("\n"); 1806 sb.append(serviceClassToCFString(classMask & serviceClass)); 1807 } 1808 } 1809 return sb; 1810 } 1811 1812 /** 1813 * Get the int config from carrier config manager. 1814 * 1815 * @param key config key defined in CarrierConfigManager 1816 * @return integer value of corresponding key. 1817 */ getIntCarrierConfig(String key)1818 private int getIntCarrierConfig(String key) { 1819 CarrierConfigManager ConfigManager = mContext.getSystemService(CarrierConfigManager.class); 1820 PersistableBundle b = null; 1821 if (ConfigManager != null) { 1822 // If an invalid subId is used, this bundle will contain default values. 1823 b = ConfigManager.getConfigForSubId(mPhone.getSubId()); 1824 } 1825 if (b != null) { 1826 return b.getInt(key); 1827 } else { 1828 // Return static default defined in CarrierConfigManager. 1829 return CarrierConfigManager.getDefaultConfig().getInt(key); 1830 } 1831 } 1832 getUssdCallbackReceiver()1833 public ResultReceiver getUssdCallbackReceiver() { 1834 return this.mCallbackReceiver; 1835 } 1836 1837 /*** 1838 * TODO: It would be nice to have a method here that can take in a dialstring and 1839 * figure out if there is an MMI code embedded within it. This code would replace 1840 * some of the string parsing functionality in the Phone App's 1841 * SpecialCharSequenceMgr class. 1842 */ 1843 1844 @Override toString()1845 public String toString() { 1846 StringBuilder sb = new StringBuilder("GsmMmiCode {"); 1847 1848 sb.append("State=" + getState()); 1849 if (mAction != null) sb.append(" action=" + mAction); 1850 if (mSc != null) sb.append(" sc=" + mSc); 1851 if (mSia != null) sb.append(" sia=" + Rlog.pii(LOG_TAG, mSia)); 1852 if (mSib != null) sb.append(" sib=" + Rlog.pii(LOG_TAG, mSib)); 1853 if (mSic != null) sb.append(" sic=" + Rlog.pii(LOG_TAG, mSic)); 1854 if (mPoundString != null) sb.append(" poundString=" + Rlog.pii(LOG_TAG, mPoundString)); 1855 if (mDialingNumber != null) { 1856 sb.append(" dialingNumber=" + Rlog.pii(LOG_TAG, mDialingNumber)); 1857 } 1858 if (mPwd != null) sb.append(" pwd=" + Rlog.pii(LOG_TAG, mPwd)); 1859 if (mCallbackReceiver != null) sb.append(" hasReceiver"); 1860 sb.append("}"); 1861 return sb.toString(); 1862 } 1863 } 1864