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