KeyguardSimPukView.java revision fa9189fd7425e7a097e46d54e545fbec631f9cac
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 } 190 191 @Override 192 public void showUsabilityHint() { 193 } 194 195 @Override 196 public void onPause() { 197 // dismiss the dialog. 198 if (mSimUnlockProgressDialog != null) { 199 mSimUnlockProgressDialog.dismiss(); 200 mSimUnlockProgressDialog = null; 201 } 202 } 203 204 /** 205 * Since the IPC can block, we want to run the request in a separate thread 206 * with a callback. 207 */ 208 private abstract class CheckSimPuk extends Thread { 209 210 private final String mPin, mPuk; 211 212 protected CheckSimPuk(String puk, String pin) { 213 mPuk = puk; 214 mPin = pin; 215 } 216 217 abstract void onSimLockChangedResponse(final int result, final int attemptsRemaining); 218 219 @Override 220 public void run() { 221 try { 222 Log.v(TAG, "call supplyPukReportResult()"); 223 final int[] result = ITelephony.Stub.asInterface(ServiceManager 224 .checkService("phone")).supplyPukReportResult(mPuk, mPin); 225 Log.v(TAG, "supplyPukReportResult returned: " + result[0] + " " + result[1]); 226 post(new Runnable() { 227 public void run() { 228 onSimLockChangedResponse(result[0], result[1]); 229 } 230 }); 231 } catch (RemoteException e) { 232 Log.e(TAG, "RemoteException for supplyPukReportResult:", e); 233 post(new Runnable() { 234 public void run() { 235 onSimLockChangedResponse(PhoneConstants.PIN_GENERAL_FAILURE, -1); 236 } 237 }); 238 } 239 } 240 } 241 242 private Dialog getSimUnlockProgressDialog() { 243 if (mSimUnlockProgressDialog == null) { 244 mSimUnlockProgressDialog = new ProgressDialog(mContext); 245 mSimUnlockProgressDialog.setMessage( 246 mContext.getString(R.string.kg_sim_unlock_progress_dialog_message)); 247 mSimUnlockProgressDialog.setIndeterminate(true); 248 mSimUnlockProgressDialog.setCancelable(false); 249 if (!(mContext instanceof Activity)) { 250 mSimUnlockProgressDialog.getWindow().setType( 251 WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); 252 } 253 } 254 return mSimUnlockProgressDialog; 255 } 256 257 private Dialog getPukRemainingAttemptsDialog(int remaining) { 258 String msg = getPukPasswordErrorMessage(remaining); 259 if (mRemainingAttemptsDialog == null) { 260 AlertDialog.Builder builder = new AlertDialog.Builder(mContext); 261 builder.setMessage(msg); 262 builder.setCancelable(false); 263 builder.setNeutralButton(R.string.ok, null); 264 mRemainingAttemptsDialog = builder.create(); 265 mRemainingAttemptsDialog.getWindow().setType( 266 WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); 267 } else { 268 mRemainingAttemptsDialog.setMessage(msg); 269 } 270 return mRemainingAttemptsDialog; 271 } 272 273 private boolean checkPuk() { 274 // make sure the puk is at least 8 digits long. 275 if (mPasswordEntry.getText().length() == 8) { 276 mPukText = mPasswordEntry.getText().toString(); 277 return true; 278 } 279 return false; 280 } 281 282 private boolean checkPin() { 283 // make sure the PIN is between 4 and 8 digits 284 int length = mPasswordEntry.getText().length(); 285 if (length >= 4 && length <= 8) { 286 mPinText = mPasswordEntry.getText().toString(); 287 return true; 288 } 289 return false; 290 } 291 292 public boolean confirmPin() { 293 return mPinText.equals(mPasswordEntry.getText().toString()); 294 } 295 296 private void updateSim() { 297 getSimUnlockProgressDialog().show(); 298 299 if (mCheckSimPukThread == null) { 300 mCheckSimPukThread = new CheckSimPuk(mPukText, mPinText) { 301 void onSimLockChangedResponse(final int result, final int attemptsRemaining) { 302 post(new Runnable() { 303 public void run() { 304 if (mSimUnlockProgressDialog != null) { 305 mSimUnlockProgressDialog.hide(); 306 } 307 if (result == PhoneConstants.PIN_RESULT_SUCCESS) { 308 KeyguardUpdateMonitor.getInstance(getContext()).reportSimUnlocked(); 309 mCallback.dismiss(true); 310 } else { 311 if (result == PhoneConstants.PIN_PASSWORD_INCORRECT) { 312 if (attemptsRemaining <= 2) { 313 // this is getting critical - show dialog 314 getPukRemainingAttemptsDialog(attemptsRemaining).show(); 315 } else { 316 // show message 317 mSecurityMessageDisplay.setMessage( 318 getPukPasswordErrorMessage(attemptsRemaining), true); 319 } 320 } else { 321 mSecurityMessageDisplay.setMessage(getContext().getString( 322 R.string.kg_password_puk_failed), true); 323 } 324 if (DEBUG) Log.d(LOG_TAG, "verifyPasswordAndUnlock " 325 + " UpdateSim.onSimCheckResponse: " 326 + " attemptsRemaining=" + attemptsRemaining); 327 mStateMachine.reset(); 328 } 329 mCheckSimPukThread = null; 330 } 331 }); 332 } 333 }; 334 mCheckSimPukThread.start(); 335 } 336 } 337 338 @Override 339 protected void verifyPasswordAndUnlock() { 340 mStateMachine.next(); 341 } 342} 343 344 345