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