KeyguardSimPukView.java revision 40a0b38160b1b80a48aa6a0d87b3fab888fd55f0
1/* 2 * Copyright (C) 2012 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.keyguard; 18 19import android.content.Context; 20import android.app.Activity; 21import android.app.AlertDialog; 22import android.app.Dialog; 23import android.app.ProgressDialog; 24import android.os.RemoteException; 25import android.os.ServiceManager; 26import android.text.InputType; 27import android.text.TextWatcher; 28import android.text.method.DigitsKeyListener; 29import android.util.AttributeSet; 30import android.util.Log; 31import android.view.View; 32import android.view.WindowManager; 33import android.widget.TextView.OnEditorActionListener; 34 35import com.android.internal.telephony.ITelephony; 36import com.android.internal.telephony.PhoneConstants; 37 38 39/** 40 * Displays a PIN pad for entering a PUK (Pin Unlock Kode) provided by a carrier. 41 */ 42public class KeyguardSimPukView extends KeyguardAbsKeyInputView 43 implements KeyguardSecurityView, OnEditorActionListener, TextWatcher { 44 private static final String LOG_TAG = "KeyguardSimPukView"; 45 private static final boolean DEBUG = KeyguardConstants.DEBUG; 46 public static final String TAG = "KeyguardSimPukView"; 47 48 private ProgressDialog mSimUnlockProgressDialog = null; 49 private CheckSimPuk mCheckSimPukThread; 50 private String mPukText; 51 private String mPinText; 52 private StateMachine mStateMachine = new StateMachine(); 53 private AlertDialog mRemainingAttemptsDialog; 54 55 private class StateMachine { 56 final int ENTER_PUK = 0; 57 final int ENTER_PIN = 1; 58 final int CONFIRM_PIN = 2; 59 final int DONE = 3; 60 private int state = ENTER_PUK; 61 62 public void next() { 63 int msg = 0; 64 if (state == ENTER_PUK) { 65 if (checkPuk()) { 66 state = ENTER_PIN; 67 msg = R.string.kg_puk_enter_pin_hint; 68 } else { 69 msg = R.string.kg_invalid_sim_puk_hint; 70 } 71 } else if (state == ENTER_PIN) { 72 if (checkPin()) { 73 state = CONFIRM_PIN; 74 msg = R.string.kg_enter_confirm_pin_hint; 75 } else { 76 msg = R.string.kg_invalid_sim_pin_hint; 77 } 78 } else if (state == CONFIRM_PIN) { 79 if (confirmPin()) { 80 state = DONE; 81 msg = R.string.keyguard_sim_unlock_progress_dialog_message; 82 updateSim(); 83 } else { 84 state = ENTER_PIN; // try again? 85 msg = R.string.kg_invalid_confirm_pin_hint; 86 } 87 } 88 mPasswordEntry.setText(null); 89 if (msg != 0) { 90 mSecurityMessageDisplay.setMessage(msg, true); 91 } 92 } 93 94 void reset() { 95 mPinText=""; 96 mPukText=""; 97 state = ENTER_PUK; 98 mSecurityMessageDisplay.setMessage(R.string.kg_puk_enter_puk_hint, true); 99 mPasswordEntry.requestFocus(); 100 } 101 } 102 103 private String getPukPasswordErrorMessage(int attemptsRemaining) { 104 String displayMessage; 105 106 if (attemptsRemaining == 0) { 107 displayMessage = getContext().getString(R.string.kg_password_wrong_puk_code_dead); 108 } else if (attemptsRemaining > 0) { 109 displayMessage = getContext().getResources() 110 .getQuantityString(R.plurals.kg_password_wrong_puk_code, attemptsRemaining, 111 attemptsRemaining); 112 } else { 113 displayMessage = getContext().getString(R.string.kg_password_puk_failed); 114 } 115 if (DEBUG) Log.d(LOG_TAG, "getPukPasswordErrorMessage:" 116 + " attemptsRemaining=" + attemptsRemaining + " displayMessage=" + displayMessage); 117 return displayMessage; 118 } 119 120 public KeyguardSimPukView(Context context) { 121 this(context, null); 122 } 123 124 public KeyguardSimPukView(Context context, AttributeSet attrs) { 125 super(context, attrs); 126 } 127 128 public void resetState() { 129 mStateMachine.reset(); 130 mPasswordEntry.setEnabled(true); 131 } 132 133 @Override 134 protected boolean shouldLockout(long deadline) { 135 // SIM PUK doesn't have a timed lockout 136 return false; 137 } 138 139 @Override 140 protected int getPasswordTextViewId() { 141 return R.id.pukEntry; 142 } 143 144 @Override 145 protected void onFinishInflate() { 146 super.onFinishInflate(); 147 148 final View ok = findViewById(R.id.key_enter); 149 if (ok != null) { 150 ok.setOnClickListener(new View.OnClickListener() { 151 @Override 152 public void onClick(View v) { 153 doHapticKeyClick(); 154 verifyPasswordAndUnlock(); 155 } 156 }); 157 } 158 159 // The delete button is of the PIN keyboard itself in some (e.g. tablet) layouts, 160 // not a separate view 161 View pinDelete = findViewById(R.id.delete_button); 162 if (pinDelete != null) { 163 pinDelete.setVisibility(View.VISIBLE); 164 pinDelete.setOnClickListener(new OnClickListener() { 165 public void onClick(View v) { 166 CharSequence str = mPasswordEntry.getText(); 167 if (str.length() > 0) { 168 mPasswordEntry.setText(str.subSequence(0, str.length()-1)); 169 } 170 doHapticKeyClick(); 171 } 172 }); 173 pinDelete.setOnLongClickListener(new View.OnLongClickListener() { 174 public boolean onLongClick(View v) { 175 mPasswordEntry.setText(""); 176 doHapticKeyClick(); 177 return true; 178 } 179 }); 180 } 181 182 mPasswordEntry.setKeyListener(DigitsKeyListener.getInstance()); 183 mPasswordEntry.setInputType(InputType.TYPE_CLASS_NUMBER 184 | InputType.TYPE_NUMBER_VARIATION_PASSWORD); 185 186 mPasswordEntry.requestFocus(); 187 188 mSecurityMessageDisplay.setTimeout(0); // don't show ownerinfo/charging status by default 189 if (mEcaView instanceof EmergencyCarrierArea) { 190 ((EmergencyCarrierArea) mEcaView).setCarrierTextVisible(true); 191 } 192 } 193 194 @Override 195 public void showUsabilityHint() { 196 } 197 198 @Override 199 public void onPause() { 200 // dismiss the dialog. 201 if (mSimUnlockProgressDialog != null) { 202 mSimUnlockProgressDialog.dismiss(); 203 mSimUnlockProgressDialog = null; 204 } 205 } 206 207 /** 208 * Since the IPC can block, we want to run the request in a separate thread 209 * with a callback. 210 */ 211 private abstract class CheckSimPuk extends Thread { 212 213 private final String mPin, mPuk; 214 215 protected CheckSimPuk(String puk, String pin) { 216 mPuk = puk; 217 mPin = pin; 218 } 219 220 abstract void onSimLockChangedResponse(final int result, final int attemptsRemaining); 221 222 @Override 223 public void run() { 224 try { 225 Log.v(TAG, "call supplyPukReportResult()"); 226 final int[] result = ITelephony.Stub.asInterface(ServiceManager 227 .checkService("phone")).supplyPukReportResult(mPuk, mPin); 228 Log.v(TAG, "supplyPukReportResult returned: " + result[0] + " " + result[1]); 229 post(new Runnable() { 230 public void run() { 231 onSimLockChangedResponse(result[0], result[1]); 232 } 233 }); 234 } catch (RemoteException e) { 235 Log.e(TAG, "RemoteException for supplyPukReportResult:", e); 236 post(new Runnable() { 237 public void run() { 238 onSimLockChangedResponse(PhoneConstants.PIN_GENERAL_FAILURE, -1); 239 } 240 }); 241 } 242 } 243 } 244 245 private Dialog getSimUnlockProgressDialog() { 246 if (mSimUnlockProgressDialog == null) { 247 mSimUnlockProgressDialog = new ProgressDialog(mContext); 248 mSimUnlockProgressDialog.setMessage( 249 mContext.getString(R.string.kg_sim_unlock_progress_dialog_message)); 250 mSimUnlockProgressDialog.setIndeterminate(true); 251 mSimUnlockProgressDialog.setCancelable(false); 252 if (!(mContext instanceof Activity)) { 253 mSimUnlockProgressDialog.getWindow().setType( 254 WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); 255 } 256 } 257 return mSimUnlockProgressDialog; 258 } 259 260 private Dialog getPukRemainingAttemptsDialog(int remaining) { 261 String msg = getPukPasswordErrorMessage(remaining); 262 if (mRemainingAttemptsDialog == null) { 263 AlertDialog.Builder builder = new AlertDialog.Builder(mContext); 264 builder.setMessage(msg); 265 builder.setCancelable(false); 266 builder.setNeutralButton(R.string.ok, null); 267 mRemainingAttemptsDialog = builder.create(); 268 mRemainingAttemptsDialog.getWindow().setType( 269 WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); 270 } else { 271 mRemainingAttemptsDialog.setMessage(msg); 272 } 273 return mRemainingAttemptsDialog; 274 } 275 276 private boolean checkPuk() { 277 // make sure the puk is at least 8 digits long. 278 if (mPasswordEntry.getText().length() == 8) { 279 mPukText = mPasswordEntry.getText().toString(); 280 return true; 281 } 282 return false; 283 } 284 285 private boolean checkPin() { 286 // make sure the PIN is between 4 and 8 digits 287 int length = mPasswordEntry.getText().length(); 288 if (length >= 4 && length <= 8) { 289 mPinText = mPasswordEntry.getText().toString(); 290 return true; 291 } 292 return false; 293 } 294 295 public boolean confirmPin() { 296 return mPinText.equals(mPasswordEntry.getText().toString()); 297 } 298 299 private void updateSim() { 300 getSimUnlockProgressDialog().show(); 301 302 if (mCheckSimPukThread == null) { 303 mCheckSimPukThread = new CheckSimPuk(mPukText, mPinText) { 304 void onSimLockChangedResponse(final int result, final int attemptsRemaining) { 305 post(new Runnable() { 306 public void run() { 307 if (mSimUnlockProgressDialog != null) { 308 mSimUnlockProgressDialog.hide(); 309 } 310 if (result == PhoneConstants.PIN_RESULT_SUCCESS) { 311 KeyguardUpdateMonitor.getInstance(getContext()).reportSimUnlocked(); 312 mCallback.dismiss(true); 313 } else { 314 if (result == PhoneConstants.PIN_PASSWORD_INCORRECT) { 315 if (attemptsRemaining <= 2) { 316 // this is getting critical - show dialog 317 getPukRemainingAttemptsDialog(attemptsRemaining).show(); 318 } else { 319 // show message 320 mSecurityMessageDisplay.setMessage( 321 getPukPasswordErrorMessage(attemptsRemaining), true); 322 } 323 } else { 324 mSecurityMessageDisplay.setMessage(getContext().getString( 325 R.string.kg_password_puk_failed), true); 326 } 327 if (DEBUG) Log.d(LOG_TAG, "verifyPasswordAndUnlock " 328 + " UpdateSim.onSimCheckResponse: " 329 + " attemptsRemaining=" + attemptsRemaining); 330 mStateMachine.reset(); 331 } 332 mCheckSimPukThread = null; 333 } 334 }); 335 } 336 }; 337 mCheckSimPukThread.start(); 338 } 339 } 340 341 @Override 342 protected void verifyPasswordAndUnlock() { 343 mStateMachine.next(); 344 } 345 346 @Override 347 public void startAppearAnimation() { 348 // noop. 349 } 350} 351 352 353