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.util.Log;
     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 = "CDMA_MMI";
     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 phone;
     56     Context context;
     57 
     58     String action;              // ACTION_REGISTER
     59     String sc;                  // Service Code
     60     String sia, sib, sic;       // Service Info a,b,c
     61     String poundString;         // Entire MMI string up to and including #
     62     String dialingNumber;
     63     String pwd;                 // For password registration
     64 
     65     State state = State.PENDING;
     66     CharSequence message;
     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.poundString = makeEmptyNull(m.group(MATCH_GROUP_POUND_STRING));
    111             ret.action = makeEmptyNull(m.group(MATCH_GROUP_ACTION));
    112             ret.sc = makeEmptyNull(m.group(MATCH_GROUP_SERVICE_CODE));
    113             ret.sia = makeEmptyNull(m.group(MATCH_GROUP_SIA));
    114             ret.sib = makeEmptyNull(m.group(MATCH_GROUP_SIB));
    115             ret.sic = makeEmptyNull(m.group(MATCH_GROUP_SIC));
    116             ret.pwd = makeEmptyNull(m.group(MATCH_GROUP_PWD_CONFIRM));
    117             ret.dialingNumber = 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         this.phone = phone;
    141         this.context = phone.getContext();
    142     }
    143 
    144     // MmiCode implementation
    145 
    146     public State
    147     getState() {
    148         return state;
    149     }
    150 
    151     public CharSequence
    152     getMessage() {
    153         return message;
    154     }
    155 
    156     // inherited javadoc suffices
    157     public void
    158     cancel() {
    159         // Complete or failed cannot be cancelled
    160         if (state == State.COMPLETE || state == State.FAILED) {
    161             return;
    162         }
    163 
    164         state = State.CANCELLED;
    165         phone.onMMIDone (this);
    166     }
    167 
    168     public boolean isCancelable() {
    169         return false;
    170     }
    171 
    172     // Instance Methods
    173 
    174     /**
    175      * @return true if the Service Code is PIN/PIN2/PUK/PUK2-related
    176      */
    177     boolean isPukCommand() {
    178         return sc != null && sc.equals(SC_PUK);
    179      }
    180 
    181     boolean isRegister() {
    182         return action != null && action.equals(ACTION_REGISTER);
    183     }
    184 
    185     public boolean isUssdRequest() {
    186         Log.w(LOG_TAG, "isUssdRequest is not implemented in CdmaMmiCode");
    187         return false;
    188     }
    189 
    190     /** Process a MMI PUK code */
    191     void
    192     processCode () {
    193         try {
    194             if (isPukCommand()) {
    195                 // sia = old PUK
    196                 // sib = new PIN
    197                 // sic = new PIN
    198                 String oldPinOrPuk = sia;
    199                 String newPin = sib;
    200                 int pinLen = newPin.length();
    201                 if (isRegister()) {
    202                     if (!newPin.equals(sic)) {
    203                         // password mismatch; return error
    204                         handlePasswordError(com.android.internal.R.string.mismatchPin);
    205                     } else if (pinLen < 4 || pinLen > 8 ) {
    206                         // invalid length
    207                         handlePasswordError(com.android.internal.R.string.invalidPin);
    208                     } else {
    209                         phone.mCM.supplyIccPuk(oldPinOrPuk, newPin,
    210                                 obtainMessage(EVENT_SET_COMPLETE, this));
    211                     }
    212                 } else {
    213                     throw new RuntimeException ("Invalid or Unsupported MMI Code");
    214                 }
    215             } else {
    216                 throw new RuntimeException ("Invalid or Unsupported MMI Code");
    217             }
    218         } catch (RuntimeException exc) {
    219             state = State.FAILED;
    220             message = context.getText(com.android.internal.R.string.mmiError);
    221             phone.onMMIDone(this);
    222         }
    223     }
    224 
    225     private void handlePasswordError(int res) {
    226         state = State.FAILED;
    227         StringBuilder sb = new StringBuilder(getScString());
    228         sb.append("\n");
    229         sb.append(context.getText(res));
    230         message = sb;
    231         phone.onMMIDone(this);
    232     }
    233 
    234     public void
    235     handleMessage (Message msg) {
    236         AsyncResult ar;
    237 
    238         if (msg.what == EVENT_SET_COMPLETE) {
    239             ar = (AsyncResult) (msg.obj);
    240             onSetComplete(ar);
    241         } else {
    242             Log.e(LOG_TAG, "Unexpected reply");
    243         }
    244     }
    245     // Private instance methods
    246 
    247     private CharSequence getScString() {
    248         if (sc != null) {
    249             if (isPukCommand()) {
    250                 return context.getText(com.android.internal.R.string.PinMmi);
    251             }
    252         }
    253 
    254         return "";
    255     }
    256 
    257     private void
    258     onSetComplete(AsyncResult ar){
    259         StringBuilder sb = new StringBuilder(getScString());
    260         sb.append("\n");
    261 
    262         if (ar.exception != null) {
    263             state = State.FAILED;
    264             if (ar.exception instanceof CommandException) {
    265                 CommandException.Error err = ((CommandException)(ar.exception)).getCommandError();
    266                 if (err == CommandException.Error.PASSWORD_INCORRECT) {
    267                     if (isPukCommand()) {
    268                         sb.append(context.getText(
    269                                 com.android.internal.R.string.badPuk));
    270                     } else {
    271                         sb.append(context.getText(
    272                                 com.android.internal.R.string.passwordIncorrect));
    273                     }
    274                 } else {
    275                     sb.append(context.getText(
    276                             com.android.internal.R.string.mmiError));
    277                 }
    278             } else {
    279                 sb.append(context.getText(
    280                         com.android.internal.R.string.mmiError));
    281             }
    282         } else if (isRegister()) {
    283             state = State.COMPLETE;
    284             sb.append(context.getText(
    285                     com.android.internal.R.string.serviceRegistered));
    286         } else {
    287             state = State.FAILED;
    288             sb.append(context.getText(
    289                     com.android.internal.R.string.mmiError));
    290         }
    291 
    292         message = sb;
    293         phone.onMMIDone(this);
    294     }
    295 
    296 }
    297