• 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.cdma;
18 
19 import static com.android.internal.telephony.CommandsInterface.CF_ACTION_DISABLE;
20 import static com.android.internal.telephony.CommandsInterface.CF_ACTION_REGISTRATION;
21 import static com.android.internal.telephony.CommandsInterface.CF_REASON_BUSY;
22 import static com.android.internal.telephony.CommandsInterface.CF_REASON_NOT_REACHABLE;
23 import static com.android.internal.telephony.CommandsInterface.CF_REASON_NO_REPLY;
24 import static com.android.internal.telephony.CommandsInterface.CF_REASON_UNCONDITIONAL;
25 
26 import android.compat.annotation.UnsupportedAppUsage;
27 import android.content.Context;
28 import android.os.AsyncResult;
29 import android.os.Build;
30 import android.os.Handler;
31 import android.os.Message;
32 import android.os.ResultReceiver;
33 
34 import com.android.internal.telephony.CommandException;
35 import com.android.internal.telephony.GsmCdmaPhone;
36 import com.android.internal.telephony.MmiCode;
37 import com.android.internal.telephony.Phone;
38 import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
39 import com.android.internal.telephony.uicc.UiccCardApplication;
40 import com.android.telephony.Rlog;
41 
42 import java.util.regex.Matcher;
43 import java.util.regex.Pattern;
44 
45 /**
46  * This class can handle Puk code Mmi
47  *
48  * {@hide}
49  *
50  */
51 public final class CdmaMmiCode  extends Handler implements MmiCode {
52     static final String LOG_TAG = "CdmaMmiCode";
53 
54     // Constants
55 
56     // From TS 22.030 6.5.2
57     static final String ACTION_REGISTER = "**";
58 
59     // Supplementary Service codes for PIN/PIN2/PUK/PUK2 from TS 22.030 Annex B
60     static final String SC_PIN          = "04";
61     static final String SC_PIN2         = "042";
62     static final String SC_PUK          = "05";
63     static final String SC_PUK2         = "052";
64 
65     // Event Constant
66 
67     static final int EVENT_SET_COMPLETE = 1;
68 
69     // Instance Variables
70 
71     GsmCdmaPhone mPhone;
72     Context mContext;
73     UiccCardApplication mUiccApplication;
74 
75     String mAction;              // ACTION_REGISTER
76     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
77     String mSc;                  // Service Code
78     String mSia, mSib, mSic;     // Service Info a,b,c
79     String mPoundString;         // Entire MMI string up to and including #
80     String mDialingNumber;
81     String mPwd;                 // For password registration
82 
83     State mState = State.PENDING;
84     CharSequence mMessage;
85 
86     // Class Variables
87 
88     static Pattern sPatternSuppService = Pattern.compile(
89         "((\\*|#|\\*#|\\*\\*|##)(\\d{2,3})(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*))?)?)?)?#)(.*)");
90 /*       1  2                    3          4  5       6   7         8    9     10  11             12
91 
92          1 = Full string up to and including #
93          2 = action
94          3 = service code
95          5 = SIA
96          7 = SIB
97          9 = SIC
98          10 = dialing number
99 */
100 
101     static final int MATCH_GROUP_POUND_STRING = 1;
102     static final int MATCH_GROUP_ACTION = 2;
103     static final int MATCH_GROUP_SERVICE_CODE = 3;
104     static final int MATCH_GROUP_SIA = 5;
105     static final int MATCH_GROUP_SIB = 7;
106     static final int MATCH_GROUP_SIC = 9;
107     static final int MATCH_GROUP_PWD_CONFIRM = 11;
108     static final int MATCH_GROUP_DIALING_NUMBER = 12;
109 
110 
111     // Public Class methods
112 
113     /**
114      * Check if provided string contains Mmi code in it and create corresponding
115      * Mmi if it does
116      */
117 
118     public static CdmaMmiCode
newFromDialString(String dialString, GsmCdmaPhone phone, UiccCardApplication app)119     newFromDialString(String dialString, GsmCdmaPhone phone, UiccCardApplication app) {
120         Matcher m;
121         CdmaMmiCode ret = null;
122 
123         m = sPatternSuppService.matcher(dialString);
124 
125         // Is this formatted like a standard supplementary service code?
126         if (m.matches()) {
127             ret = new CdmaMmiCode(phone,app);
128             ret.mPoundString = makeEmptyNull(m.group(MATCH_GROUP_POUND_STRING));
129             ret.mAction = makeEmptyNull(m.group(MATCH_GROUP_ACTION));
130             ret.mSc = makeEmptyNull(m.group(MATCH_GROUP_SERVICE_CODE));
131             ret.mSia = makeEmptyNull(m.group(MATCH_GROUP_SIA));
132             ret.mSib = makeEmptyNull(m.group(MATCH_GROUP_SIB));
133             ret.mSic = makeEmptyNull(m.group(MATCH_GROUP_SIC));
134             ret.mPwd = makeEmptyNull(m.group(MATCH_GROUP_PWD_CONFIRM));
135             ret.mDialingNumber = makeEmptyNull(m.group(MATCH_GROUP_DIALING_NUMBER));
136 
137         }
138 
139         return ret;
140     }
141 
142     // Private Class methods
143 
144     /** make empty strings be null.
145      *  Regexp returns empty strings for empty groups
146      */
147     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
148     private static String
makeEmptyNull(String s)149     makeEmptyNull (String s) {
150         if (s != null && s.length() == 0) return null;
151 
152         return s;
153     }
154 
155     // Constructor
156 
CdmaMmiCode(GsmCdmaPhone phone, UiccCardApplication app)157     CdmaMmiCode (GsmCdmaPhone phone, UiccCardApplication app) {
158         super(phone.getHandler().getLooper());
159         mPhone = phone;
160         mContext = phone.getContext();
161         mUiccApplication = app;
162     }
163 
164     // MmiCode implementation
165 
166     @Override
167     public State
getState()168     getState() {
169         return mState;
170     }
171 
172     @Override
173     public CharSequence
getMessage()174     getMessage() {
175         return mMessage;
176     }
177 
178     public Phone
getPhone()179     getPhone() {
180         return ((Phone) mPhone);
181     }
182 
183     // inherited javadoc suffices
184     @Override
185     public void
cancel()186     cancel() {
187         // Complete or failed cannot be cancelled
188         if (mState == State.COMPLETE || mState == State.FAILED) {
189             return;
190         }
191 
192         mState = State.CANCELLED;
193         mPhone.onMMIDone (this);
194     }
195 
196     @Override
isCancelable()197     public boolean isCancelable() {
198         return false;
199     }
200 
201     // Instance Methods
202 
203     /**
204      * @return true if the Service Code is PIN/PIN2/PUK/PUK2-related
205      */
isPinPukCommand()206     public boolean isPinPukCommand() {
207         return mSc != null && (mSc.equals(SC_PIN) || mSc.equals(SC_PIN2)
208                               || mSc.equals(SC_PUK) || mSc.equals(SC_PUK2));
209     }
210 
isRegister()211     boolean isRegister() {
212         return mAction != null && mAction.equals(ACTION_REGISTER);
213     }
214 
215     @Override
isUssdRequest()216     public boolean isUssdRequest() {
217         Rlog.w(LOG_TAG, "isUssdRequest is not implemented in CdmaMmiCode");
218         return false;
219     }
220 
221     @Override
getDialString()222     public String getDialString() {
223         return null;
224     }
225 
226     /** Process a MMI PUK code */
227     public void
processCode()228     processCode() {
229         try {
230             if (isPinPukCommand()) {
231                 // TODO: This is the same as the code in GsmMmiCode.java,
232                 // MmiCode should be an abstract or base class and this and
233                 // other common variables and code should be promoted.
234 
235                 // sia = old PIN or PUK
236                 // sib = new PIN
237                 // sic = new PIN
238                 String oldPinOrPuk = mSia;
239                 String newPinOrPuk = mSib;
240                 int pinLen = newPinOrPuk.length();
241                 if (isRegister()) {
242                     if (!newPinOrPuk.equals(mSic)) {
243                         // password mismatch; return error
244                         handlePasswordError(com.android.internal.R.string.mismatchPin);
245                     } else if (pinLen < 4 || pinLen > 8 ) {
246                         // invalid length
247                         handlePasswordError(com.android.internal.R.string.invalidPin);
248                     } else if (mSc.equals(SC_PIN)
249                             && mUiccApplication != null
250                             && mUiccApplication.getState() == AppState.APPSTATE_PUK) {
251                         // Sim is puk-locked
252                         handlePasswordError(com.android.internal.R.string.needPuk);
253                     } else if (mUiccApplication != null) {
254                         Rlog.d(LOG_TAG, "process mmi service code using UiccApp sc=" + mSc);
255 
256                         // We have an app and the pre-checks are OK
257                         if (mSc.equals(SC_PIN)) {
258                             mUiccApplication.changeIccLockPassword(oldPinOrPuk, newPinOrPuk,
259                                     obtainMessage(EVENT_SET_COMPLETE, this));
260                         } else if (mSc.equals(SC_PIN2)) {
261                             mUiccApplication.changeIccFdnPassword(oldPinOrPuk, newPinOrPuk,
262                                     obtainMessage(EVENT_SET_COMPLETE, this));
263                         } else if (mSc.equals(SC_PUK)) {
264                             mUiccApplication.supplyPuk(oldPinOrPuk, newPinOrPuk,
265                                     obtainMessage(EVENT_SET_COMPLETE, this));
266                         } else if (mSc.equals(SC_PUK2)) {
267                             mUiccApplication.supplyPuk2(oldPinOrPuk, newPinOrPuk,
268                                     obtainMessage(EVENT_SET_COMPLETE, this));
269                         } else {
270                             throw new RuntimeException("Unsupported service code=" + mSc);
271                         }
272                     } else {
273                         throw new RuntimeException("No application mUiccApplicaiton is null");
274                     }
275                 } else {
276                     throw new RuntimeException ("Ivalid register/action=" + mAction);
277                 }
278             }
279         } catch (RuntimeException exc) {
280             mState = State.FAILED;
281             mMessage = mContext.getText(com.android.internal.R.string.mmiError);
282             mPhone.onMMIDone(this);
283         }
284     }
285 
handlePasswordError(int res)286     private void handlePasswordError(int res) {
287         mState = State.FAILED;
288         StringBuilder sb = new StringBuilder(getScString());
289         sb.append("\n");
290         sb.append(mContext.getText(res));
291         mMessage = sb;
292         mPhone.onMMIDone(this);
293     }
294 
295     @Override
296     public void
handleMessage(Message msg)297     handleMessage (Message msg) {
298         AsyncResult ar;
299 
300         if (msg.what == EVENT_SET_COMPLETE) {
301             ar = (AsyncResult) (msg.obj);
302             onSetComplete(msg, ar);
303         } else {
304             Rlog.e(LOG_TAG, "Unexpected reply");
305         }
306     }
307     // Private instance methods
308 
getScString()309     private CharSequence getScString() {
310         if (mSc != null) {
311             if (isPinPukCommand()) {
312                 return mContext.getText(com.android.internal.R.string.PinMmi);
313             }
314         }
315 
316         return "";
317     }
318 
319     private void
onSetComplete(Message msg, AsyncResult ar)320     onSetComplete(Message msg, AsyncResult ar){
321         StringBuilder sb = new StringBuilder(getScString());
322         sb.append("\n");
323 
324         if (ar.exception != null) {
325             mState = State.FAILED;
326             if (ar.exception instanceof CommandException) {
327                 CommandException.Error err = ((CommandException)(ar.exception)).getCommandError();
328                 if (err == CommandException.Error.PASSWORD_INCORRECT) {
329                     if (isPinPukCommand()) {
330                         // look specifically for the PUK commands and adjust
331                         // the message accordingly.
332                         if (mSc.equals(SC_PUK) || mSc.equals(SC_PUK2)) {
333                             sb.append(mContext.getText(
334                                     com.android.internal.R.string.badPuk));
335                         } else {
336                             sb.append(mContext.getText(
337                                     com.android.internal.R.string.badPin));
338                         }
339                         // Get the No. of retries remaining to unlock PUK/PUK2
340                         int attemptsRemaining = msg.arg1;
341                         if (attemptsRemaining <= 0) {
342                             Rlog.d(LOG_TAG, "onSetComplete: PUK locked,"
343                                     + " cancel as lock screen will handle this");
344                             mState = State.CANCELLED;
345                         } else if (attemptsRemaining > 0) {
346                             Rlog.d(LOG_TAG, "onSetComplete: attemptsRemaining="+attemptsRemaining);
347                             sb.append(mContext.getResources().getQuantityString(
348                                     com.android.internal.R.plurals.pinpuk_attempts,
349                                     attemptsRemaining, attemptsRemaining));
350                         }
351                     } else {
352                         sb.append(mContext.getText(
353                                 com.android.internal.R.string.passwordIncorrect));
354                     }
355                 } else if (err == CommandException.Error.SIM_PUK2) {
356                     sb.append(mContext.getText(
357                             com.android.internal.R.string.badPin));
358                     sb.append("\n");
359                     sb.append(mContext.getText(
360                             com.android.internal.R.string.needPuk2));
361                 } else if (err == CommandException.Error.REQUEST_NOT_SUPPORTED) {
362                     if (mSc.equals(SC_PIN)) {
363                         sb.append(mContext.getText(com.android.internal.R.string.enablePin));
364                     }
365                 } else {
366                     sb.append(mContext.getText(
367                             com.android.internal.R.string.mmiError));
368                 }
369             } else {
370                 sb.append(mContext.getText(
371                         com.android.internal.R.string.mmiError));
372             }
373         } else if (isRegister()) {
374             mState = State.COMPLETE;
375             sb.append(mContext.getText(
376                     com.android.internal.R.string.serviceRegistered));
377         } else {
378             mState = State.FAILED;
379             sb.append(mContext.getText(
380                     com.android.internal.R.string.mmiError));
381         }
382 
383         mMessage = sb;
384         mPhone.onMMIDone(this);
385     }
386 
387     @Override
getUssdCallbackReceiver()388     public ResultReceiver getUssdCallbackReceiver() {
389         return null;
390     }
391 
getCallForwardingPrefixAndNumber(int action, int reason, String number)392     public static String getCallForwardingPrefixAndNumber(int action, int reason, String number) {
393         String prefixWithNum = "";
394         switch(reason) {
395             case CF_REASON_UNCONDITIONAL: {
396                 if (action == CF_ACTION_REGISTRATION) {
397                     prefixWithNum = "*72" + number;
398                 } else if (action == CF_ACTION_DISABLE) {
399                     prefixWithNum = "*720";
400                 }
401                 break;
402             }
403             case CF_REASON_BUSY: {
404                 if (action == CF_ACTION_REGISTRATION) {
405                     prefixWithNum = "*90" + number;
406                 } else if (action == CF_ACTION_DISABLE) {
407                     prefixWithNum = "*900";
408                 }
409                 break;
410             }
411             case CF_REASON_NO_REPLY: {
412                 if (action == CF_ACTION_REGISTRATION) {
413                     prefixWithNum = "*92" + number;
414                 } else if (action == CF_ACTION_DISABLE) {
415                     prefixWithNum = "*920";
416                 }
417                 break;
418             }
419             case CF_REASON_NOT_REACHABLE: {
420                 if (action == CF_ACTION_REGISTRATION) {
421                     prefixWithNum = "*68" + number;
422                 } else if (action == CF_ACTION_DISABLE) {
423                     prefixWithNum = "*680";
424                 }
425                 break;
426             }
427             default:
428                 Rlog.d(LOG_TAG, "getCallForwardingPrefix not match any prefix");
429                 break;
430         }
431         return prefixWithNum;
432     }
433 
getCallWaitingPrefix(boolean enable)434     public static String getCallWaitingPrefix(boolean enable) {
435         if (enable) {
436             return "*74";
437         } else {
438             return "*740";
439         }
440     }
441 
442     @Override
isNetworkInitiatedUssd()443     public boolean isNetworkInitiatedUssd() {
444         Rlog.w(LOG_TAG, "isNetworkInitiated is not implemented in CdmaMmiCode");
445         return false;
446     }
447 }
448