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