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