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.MmiCode;
     23 
     24 import android.os.AsyncResult;
     25 import android.os.Handler;
     26 import android.os.Message;
     27 import android.telephony.Rlog;
     28 
     29 import java.util.regex.Pattern;
     30 import java.util.regex.Matcher;
     31 
     32 /**
     33  * This class can handle Puk code Mmi
     34  *
     35  * {@hide}
     36  *
     37  */
     38 public final class CdmaMmiCode  extends Handler implements MmiCode {
     39     static final String LOG_TAG = "CdmaMmiCode";
     40 
     41     // Constants
     42 
     43     // From TS 22.030 6.5.2
     44     static final String ACTION_REGISTER = "**";
     45 
     46     // Supp Service codes from TS 22.030 Annex B
     47     static final String SC_PUK          = "05";
     48 
     49     // Event Constant
     50 
     51     static final int EVENT_SET_COMPLETE = 1;
     52 
     53     // Instance Variables
     54 
     55     CDMAPhone mPhone;
     56     Context mContext;
     57 
     58     String mAction;              // ACTION_REGISTER
     59     String mSc;                  // Service Code
     60     String mSia, mSib, mSic;     // Service Info a,b,c
     61     String mPoundString;         // Entire MMI string up to and including #
     62     String mDialingNumber;
     63     String mPwd;                 // For password registration
     64 
     65     State mState = State.PENDING;
     66     CharSequence mMessage;
     67 
     68     // Class Variables
     69 
     70     static Pattern sPatternSuppService = Pattern.compile(
     71         "((\\*|#|\\*#|\\*\\*|##)(\\d{2,3})(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*))?)?)?)?#)(.*)");
     72 /*       1  2                    3          4  5       6   7         8    9     10  11             12
     73 
     74          1 = Full string up to and including #
     75          2 = action
     76          3 = service code
     77          5 = SIA
     78          7 = SIB
     79          9 = SIC
     80          10 = dialing number
     81 */
     82 
     83     static final int MATCH_GROUP_POUND_STRING = 1;
     84     static final int MATCH_GROUP_ACTION = 2;
     85     static final int MATCH_GROUP_SERVICE_CODE = 3;
     86     static final int MATCH_GROUP_SIA = 5;
     87     static final int MATCH_GROUP_SIB = 7;
     88     static final int MATCH_GROUP_SIC = 9;
     89     static final int MATCH_GROUP_PWD_CONFIRM = 11;
     90     static final int MATCH_GROUP_DIALING_NUMBER = 12;
     91 
     92 
     93     // Public Class methods
     94 
     95     /**
     96      * Check if provided string contains Mmi code in it and create corresponding
     97      * Mmi if it does
     98      */
     99 
    100     public static CdmaMmiCode
    101     newFromDialString(String dialString, CDMAPhone phone) {
    102         Matcher m;
    103         CdmaMmiCode ret = null;
    104 
    105         m = sPatternSuppService.matcher(dialString);
    106 
    107         // Is this formatted like a standard supplementary service code?
    108         if (m.matches()) {
    109             ret = new CdmaMmiCode(phone);
    110             ret.mPoundString = makeEmptyNull(m.group(MATCH_GROUP_POUND_STRING));
    111             ret.mAction = makeEmptyNull(m.group(MATCH_GROUP_ACTION));
    112             ret.mSc = makeEmptyNull(m.group(MATCH_GROUP_SERVICE_CODE));
    113             ret.mSia = makeEmptyNull(m.group(MATCH_GROUP_SIA));
    114             ret.mSib = makeEmptyNull(m.group(MATCH_GROUP_SIB));
    115             ret.mSic = makeEmptyNull(m.group(MATCH_GROUP_SIC));
    116             ret.mPwd = makeEmptyNull(m.group(MATCH_GROUP_PWD_CONFIRM));
    117             ret.mDialingNumber = makeEmptyNull(m.group(MATCH_GROUP_DIALING_NUMBER));
    118 
    119         }
    120 
    121         return ret;
    122     }
    123 
    124     // Private Class methods
    125 
    126     /** make empty strings be null.
    127      *  Regexp returns empty strings for empty groups
    128      */
    129     private static String
    130     makeEmptyNull (String s) {
    131         if (s != null && s.length() == 0) return null;
    132 
    133         return s;
    134     }
    135 
    136     // Constructor
    137 
    138     CdmaMmiCode (CDMAPhone phone) {
    139         super(phone.getHandler().getLooper());
    140         mPhone = phone;
    141         mContext = phone.getContext();
    142     }
    143 
    144     // MmiCode implementation
    145 
    146     @Override
    147     public State
    148     getState() {
    149         return mState;
    150     }
    151 
    152     @Override
    153     public CharSequence
    154     getMessage() {
    155         return mMessage;
    156     }
    157 
    158     // inherited javadoc suffices
    159     @Override
    160     public void
    161     cancel() {
    162         // Complete or failed cannot be cancelled
    163         if (mState == State.COMPLETE || mState == State.FAILED) {
    164             return;
    165         }
    166 
    167         mState = State.CANCELLED;
    168         mPhone.onMMIDone (this);
    169     }
    170 
    171     @Override
    172     public boolean isCancelable() {
    173         return false;
    174     }
    175 
    176     // Instance Methods
    177 
    178     /**
    179      * @return true if the Service Code is PIN/PIN2/PUK/PUK2-related
    180      */
    181     boolean isPukCommand() {
    182         return mSc != null && mSc.equals(SC_PUK);
    183      }
    184 
    185     boolean isRegister() {
    186         return mAction != null && mAction.equals(ACTION_REGISTER);
    187     }
    188 
    189     @Override
    190     public boolean isUssdRequest() {
    191         Rlog.w(LOG_TAG, "isUssdRequest is not implemented in CdmaMmiCode");
    192         return false;
    193     }
    194 
    195     /** Process a MMI PUK code */
    196     void
    197     processCode () {
    198         try {
    199             if (isPukCommand()) {
    200                 // sia = old PUK
    201                 // sib = new PIN
    202                 // sic = new PIN
    203                 String oldPinOrPuk = mSia;
    204                 String newPin = mSib;
    205                 int pinLen = newPin.length();
    206                 if (isRegister()) {
    207                     if (!newPin.equals(mSic)) {
    208                         // password mismatch; return error
    209                         handlePasswordError(com.android.internal.R.string.mismatchPin);
    210                     } else if (pinLen < 4 || pinLen > 8 ) {
    211                         // invalid length
    212                         handlePasswordError(com.android.internal.R.string.invalidPin);
    213                     } else {
    214                         mPhone.mCi.supplyIccPuk(oldPinOrPuk, newPin,
    215                                 obtainMessage(EVENT_SET_COMPLETE, this));
    216                     }
    217                 } else {
    218                     throw new RuntimeException ("Invalid or Unsupported MMI Code");
    219                 }
    220             } else {
    221                 throw new RuntimeException ("Invalid or Unsupported MMI Code");
    222             }
    223         } catch (RuntimeException exc) {
    224             mState = State.FAILED;
    225             mMessage = mContext.getText(com.android.internal.R.string.mmiError);
    226             mPhone.onMMIDone(this);
    227         }
    228     }
    229 
    230     private void handlePasswordError(int res) {
    231         mState = State.FAILED;
    232         StringBuilder sb = new StringBuilder(getScString());
    233         sb.append("\n");
    234         sb.append(mContext.getText(res));
    235         mMessage = sb;
    236         mPhone.onMMIDone(this);
    237     }
    238 
    239     @Override
    240     public void
    241     handleMessage (Message msg) {
    242         AsyncResult ar;
    243 
    244         if (msg.what == EVENT_SET_COMPLETE) {
    245             ar = (AsyncResult) (msg.obj);
    246             onSetComplete(ar);
    247         } else {
    248             Rlog.e(LOG_TAG, "Unexpected reply");
    249         }
    250     }
    251     // Private instance methods
    252 
    253     private CharSequence getScString() {
    254         if (mSc != null) {
    255             if (isPukCommand()) {
    256                 return mContext.getText(com.android.internal.R.string.PinMmi);
    257             }
    258         }
    259 
    260         return "";
    261     }
    262 
    263     private void
    264     onSetComplete(AsyncResult ar){
    265         StringBuilder sb = new StringBuilder(getScString());
    266         sb.append("\n");
    267 
    268         if (ar.exception != null) {
    269             mState = State.FAILED;
    270             if (ar.exception instanceof CommandException) {
    271                 CommandException.Error err = ((CommandException)(ar.exception)).getCommandError();
    272                 if (err == CommandException.Error.PASSWORD_INCORRECT) {
    273                     if (isPukCommand()) {
    274                         sb.append(mContext.getText(
    275                                 com.android.internal.R.string.badPuk));
    276                     } else {
    277                         sb.append(mContext.getText(
    278                                 com.android.internal.R.string.passwordIncorrect));
    279                     }
    280                 } else {
    281                     sb.append(mContext.getText(
    282                             com.android.internal.R.string.mmiError));
    283                 }
    284             } else {
    285                 sb.append(mContext.getText(
    286                         com.android.internal.R.string.mmiError));
    287             }
    288         } else if (isRegister()) {
    289             mState = State.COMPLETE;
    290             sb.append(mContext.getText(
    291                     com.android.internal.R.string.serviceRegistered));
    292         } else {
    293             mState = State.FAILED;
    294             sb.append(mContext.getText(
    295                     com.android.internal.R.string.mmiError));
    296         }
    297 
    298         mMessage = sb;
    299         mPhone.onMMIDone(this);
    300     }
    301 
    302 }
    303