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