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