/* * Copyright (C) 2006 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.telephony.cdma; import android.content.Context; import com.android.internal.telephony.CommandException; import com.android.internal.telephony.MmiCode; import android.os.AsyncResult; import android.os.Handler; import android.os.Message; import android.telephony.Rlog; import java.util.regex.Pattern; import java.util.regex.Matcher; /** * This class can handle Puk code Mmi * * {@hide} * */ public final class CdmaMmiCode extends Handler implements MmiCode { static final String LOG_TAG = "CdmaMmiCode"; // Constants // From TS 22.030 6.5.2 static final String ACTION_REGISTER = "**"; // Supp Service codes from TS 22.030 Annex B static final String SC_PUK = "05"; // Event Constant static final int EVENT_SET_COMPLETE = 1; // Instance Variables CDMAPhone mPhone; Context mContext; String mAction; // ACTION_REGISTER String mSc; // Service Code String mSia, mSib, mSic; // Service Info a,b,c String mPoundString; // Entire MMI string up to and including # String mDialingNumber; String mPwd; // For password registration State mState = State.PENDING; CharSequence mMessage; // Class Variables static Pattern sPatternSuppService = Pattern.compile( "((\\*|#|\\*#|\\*\\*|##)(\\d{2,3})(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*))?)?)?)?#)(.*)"); /* 1 2 3 4 5 6 7 8 9 10 11 12 1 = Full string up to and including # 2 = action 3 = service code 5 = SIA 7 = SIB 9 = SIC 10 = dialing number */ static final int MATCH_GROUP_POUND_STRING = 1; static final int MATCH_GROUP_ACTION = 2; static final int MATCH_GROUP_SERVICE_CODE = 3; static final int MATCH_GROUP_SIA = 5; static final int MATCH_GROUP_SIB = 7; static final int MATCH_GROUP_SIC = 9; static final int MATCH_GROUP_PWD_CONFIRM = 11; static final int MATCH_GROUP_DIALING_NUMBER = 12; // Public Class methods /** * Check if provided string contains Mmi code in it and create corresponding * Mmi if it does */ public static CdmaMmiCode newFromDialString(String dialString, CDMAPhone phone) { Matcher m; CdmaMmiCode ret = null; m = sPatternSuppService.matcher(dialString); // Is this formatted like a standard supplementary service code? if (m.matches()) { ret = new CdmaMmiCode(phone); ret.mPoundString = makeEmptyNull(m.group(MATCH_GROUP_POUND_STRING)); ret.mAction = makeEmptyNull(m.group(MATCH_GROUP_ACTION)); ret.mSc = makeEmptyNull(m.group(MATCH_GROUP_SERVICE_CODE)); ret.mSia = makeEmptyNull(m.group(MATCH_GROUP_SIA)); ret.mSib = makeEmptyNull(m.group(MATCH_GROUP_SIB)); ret.mSic = makeEmptyNull(m.group(MATCH_GROUP_SIC)); ret.mPwd = makeEmptyNull(m.group(MATCH_GROUP_PWD_CONFIRM)); ret.mDialingNumber = makeEmptyNull(m.group(MATCH_GROUP_DIALING_NUMBER)); } return ret; } // Private Class methods /** make empty strings be null. * Regexp returns empty strings for empty groups */ private static String makeEmptyNull (String s) { if (s != null && s.length() == 0) return null; return s; } // Constructor CdmaMmiCode (CDMAPhone phone) { super(phone.getHandler().getLooper()); mPhone = phone; mContext = phone.getContext(); } // MmiCode implementation @Override public State getState() { return mState; } @Override public CharSequence getMessage() { return mMessage; } // inherited javadoc suffices @Override public void cancel() { // Complete or failed cannot be cancelled if (mState == State.COMPLETE || mState == State.FAILED) { return; } mState = State.CANCELLED; mPhone.onMMIDone (this); } @Override public boolean isCancelable() { return false; } // Instance Methods /** * @return true if the Service Code is PIN/PIN2/PUK/PUK2-related */ boolean isPukCommand() { return mSc != null && mSc.equals(SC_PUK); } boolean isRegister() { return mAction != null && mAction.equals(ACTION_REGISTER); } @Override public boolean isUssdRequest() { Rlog.w(LOG_TAG, "isUssdRequest is not implemented in CdmaMmiCode"); return false; } /** Process a MMI PUK code */ void processCode () { try { if (isPukCommand()) { // sia = old PUK // sib = new PIN // sic = new PIN String oldPinOrPuk = mSia; String newPin = mSib; int pinLen = newPin.length(); if (isRegister()) { if (!newPin.equals(mSic)) { // password mismatch; return error handlePasswordError(com.android.internal.R.string.mismatchPin); } else if (pinLen < 4 || pinLen > 8 ) { // invalid length handlePasswordError(com.android.internal.R.string.invalidPin); } else { mPhone.mCi.supplyIccPuk(oldPinOrPuk, newPin, obtainMessage(EVENT_SET_COMPLETE, this)); } } else { throw new RuntimeException ("Invalid or Unsupported MMI Code"); } } else { throw new RuntimeException ("Invalid or Unsupported MMI Code"); } } catch (RuntimeException exc) { mState = State.FAILED; mMessage = mContext.getText(com.android.internal.R.string.mmiError); mPhone.onMMIDone(this); } } private void handlePasswordError(int res) { mState = State.FAILED; StringBuilder sb = new StringBuilder(getScString()); sb.append("\n"); sb.append(mContext.getText(res)); mMessage = sb; mPhone.onMMIDone(this); } @Override public void handleMessage (Message msg) { AsyncResult ar; if (msg.what == EVENT_SET_COMPLETE) { ar = (AsyncResult) (msg.obj); onSetComplete(ar); } else { Rlog.e(LOG_TAG, "Unexpected reply"); } } // Private instance methods private CharSequence getScString() { if (mSc != null) { if (isPukCommand()) { return mContext.getText(com.android.internal.R.string.PinMmi); } } return ""; } private void onSetComplete(AsyncResult ar){ StringBuilder sb = new StringBuilder(getScString()); sb.append("\n"); if (ar.exception != null) { mState = State.FAILED; if (ar.exception instanceof CommandException) { CommandException.Error err = ((CommandException)(ar.exception)).getCommandError(); if (err == CommandException.Error.PASSWORD_INCORRECT) { if (isPukCommand()) { sb.append(mContext.getText( com.android.internal.R.string.badPuk)); } else { sb.append(mContext.getText( com.android.internal.R.string.passwordIncorrect)); } } else { sb.append(mContext.getText( com.android.internal.R.string.mmiError)); } } else { sb.append(mContext.getText( com.android.internal.R.string.mmiError)); } } else if (isRegister()) { mState = State.COMPLETE; sb.append(mContext.getText( com.android.internal.R.string.serviceRegistered)); } else { mState = State.FAILED; sb.append(mContext.getText( com.android.internal.R.string.mmiError)); } mMessage = sb; mPhone.onMMIDone(this); } }