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