CdmaMmiCode.java revision cbaa45bbf2cab852b6c9c3a887e9f803d4e857ea
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
17package com.android.internal.telephony.cdma;
18
19import android.content.Context;
20
21import com.android.internal.telephony.CommandException;
22import com.android.internal.telephony.MmiCode;
23
24import android.os.AsyncResult;
25import android.os.Handler;
26import android.os.Message;
27import android.telephony.Rlog;
28
29import java.util.regex.Pattern;
30import java.util.regex.Matcher;
31
32/**
33 * This class can handle Puk code Mmi
34 *
35 * {@hide}
36 *
37 */
38public 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 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    @Override
147    public State
148    getState() {
149        return state;
150    }
151
152    @Override
153    public CharSequence
154    getMessage() {
155        return message;
156    }
157
158    // inherited javadoc suffices
159    @Override
160    public void
161    cancel() {
162        // Complete or failed cannot be cancelled
163        if (state == State.COMPLETE || state == State.FAILED) {
164            return;
165        }
166
167        state = State.CANCELLED;
168        phone.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 sc != null && sc.equals(SC_PUK);
183     }
184
185    boolean isRegister() {
186        return action != null && action.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 = sia;
204                String newPin = sib;
205                int pinLen = newPin.length();
206                if (isRegister()) {
207                    if (!newPin.equals(sic)) {
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                        phone.mCM.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            state = State.FAILED;
225            message = context.getText(com.android.internal.R.string.mmiError);
226            phone.onMMIDone(this);
227        }
228    }
229
230    private void handlePasswordError(int res) {
231        state = State.FAILED;
232        StringBuilder sb = new StringBuilder(getScString());
233        sb.append("\n");
234        sb.append(context.getText(res));
235        message = sb;
236        phone.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 (sc != null) {
255            if (isPukCommand()) {
256                return context.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            state = 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(context.getText(
275                                com.android.internal.R.string.badPuk));
276                    } else {
277                        sb.append(context.getText(
278                                com.android.internal.R.string.passwordIncorrect));
279                    }
280                } else {
281                    sb.append(context.getText(
282                            com.android.internal.R.string.mmiError));
283                }
284            } else {
285                sb.append(context.getText(
286                        com.android.internal.R.string.mmiError));
287            }
288        } else if (isRegister()) {
289            state = State.COMPLETE;
290            sb.append(context.getText(
291                    com.android.internal.R.string.serviceRegistered));
292        } else {
293            state = State.FAILED;
294            sb.append(context.getText(
295                    com.android.internal.R.string.mmiError));
296        }
297
298        message = sb;
299        phone.onMMIDone(this);
300    }
301
302}
303