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