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