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