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 */ 16package com.android.internal.policy.impl.keyguard; 17 18import android.app.Activity; 19import android.app.Dialog; 20import android.app.ProgressDialog; 21import android.content.Context; 22import android.os.RemoteException; 23import android.os.ServiceManager; 24import android.text.Editable; 25import android.text.InputType; 26import android.text.TextWatcher; 27import android.text.method.DigitsKeyListener; 28import android.util.AttributeSet; 29import android.view.View; 30import android.view.WindowManager; 31import android.widget.TextView.OnEditorActionListener; 32 33import com.android.internal.telephony.ITelephony; 34 35import com.android.internal.R; 36 37/** 38 * Displays a PIN pad for entering a PUK (Pin Unlock Kode) provided by a carrier. 39 */ 40public class KeyguardSimPukView extends KeyguardAbsKeyInputView 41 implements KeyguardSecurityView, OnEditorActionListener, TextWatcher { 42 43 private ProgressDialog mSimUnlockProgressDialog = null; 44 private volatile boolean mCheckInProgress; 45 private String mPukText; 46 private String mPinText; 47 private StateMachine mStateMachine = new StateMachine(); 48 49 private class StateMachine { 50 final int ENTER_PUK = 0; 51 final int ENTER_PIN = 1; 52 final int CONFIRM_PIN = 2; 53 final int DONE = 3; 54 private int state = ENTER_PUK; 55 56 public void next() { 57 int msg = 0; 58 if (state == ENTER_PUK) { 59 if (checkPuk()) { 60 state = ENTER_PIN; 61 msg = R.string.kg_puk_enter_pin_hint; 62 } else { 63 msg = R.string.kg_invalid_sim_puk_hint; 64 } 65 } else if (state == ENTER_PIN) { 66 if (checkPin()) { 67 state = CONFIRM_PIN; 68 msg = R.string.kg_enter_confirm_pin_hint; 69 } else { 70 msg = R.string.kg_invalid_sim_pin_hint; 71 } 72 } else if (state == CONFIRM_PIN) { 73 if (confirmPin()) { 74 state = DONE; 75 msg = 76 com.android.internal.R.string.lockscreen_sim_unlock_progress_dialog_message; 77 updateSim(); 78 } else { 79 state = ENTER_PIN; // try again? 80 msg = R.string.kg_invalid_confirm_pin_hint; 81 } 82 } 83 mPasswordEntry.setText(null); 84 if (msg != 0) { 85 mSecurityMessageDisplay.setMessage(msg, true); 86 } 87 } 88 89 void reset() { 90 mPinText=""; 91 mPukText=""; 92 state = ENTER_PUK; 93 mSecurityMessageDisplay.setMessage(R.string.kg_puk_enter_puk_hint, true); 94 mPasswordEntry.requestFocus(); 95 } 96 } 97 98 public KeyguardSimPukView(Context context) { 99 this(context, null); 100 } 101 102 public KeyguardSimPukView(Context context, AttributeSet attrs) { 103 super(context, attrs); 104 } 105 106 public void resetState() { 107 mStateMachine.reset(); 108 mPasswordEntry.setEnabled(true); 109 } 110 111 @Override 112 protected int getPasswordTextViewId() { 113 return R.id.pinEntry; 114 } 115 116 @Override 117 protected void onFinishInflate() { 118 super.onFinishInflate(); 119 120 final View ok = findViewById(R.id.key_enter); 121 if (ok != null) { 122 ok.setOnClickListener(new View.OnClickListener() { 123 @Override 124 public void onClick(View v) { 125 doHapticKeyClick(); 126 verifyPasswordAndUnlock(); 127 } 128 }); 129 } 130 131 // The delete button is of the PIN keyboard itself in some (e.g. tablet) layouts, 132 // not a separate view 133 View pinDelete = findViewById(R.id.delete_button); 134 if (pinDelete != null) { 135 pinDelete.setVisibility(View.VISIBLE); 136 pinDelete.setOnClickListener(new OnClickListener() { 137 public void onClick(View v) { 138 CharSequence str = mPasswordEntry.getText(); 139 if (str.length() > 0) { 140 mPasswordEntry.setText(str.subSequence(0, str.length()-1)); 141 } 142 doHapticKeyClick(); 143 } 144 }); 145 pinDelete.setOnLongClickListener(new View.OnLongClickListener() { 146 public boolean onLongClick(View v) { 147 mPasswordEntry.setText(""); 148 doHapticKeyClick(); 149 return true; 150 } 151 }); 152 } 153 154 mPasswordEntry.setKeyListener(DigitsKeyListener.getInstance()); 155 mPasswordEntry.setInputType(InputType.TYPE_CLASS_NUMBER 156 | InputType.TYPE_NUMBER_VARIATION_PASSWORD); 157 158 mPasswordEntry.requestFocus(); 159 160 mSecurityMessageDisplay.setTimeout(0); // don't show ownerinfo/charging status by default 161 } 162 163 @Override 164 public void showUsabilityHint() { 165 } 166 167 @Override 168 public void onPause() { 169 // dismiss the dialog. 170 if (mSimUnlockProgressDialog != null) { 171 mSimUnlockProgressDialog.dismiss(); 172 mSimUnlockProgressDialog = null; 173 } 174 } 175 176 /** 177 * Since the IPC can block, we want to run the request in a separate thread 178 * with a callback. 179 */ 180 private abstract class CheckSimPuk extends Thread { 181 182 private final String mPin, mPuk; 183 184 protected CheckSimPuk(String puk, String pin) { 185 mPuk = puk; 186 mPin = pin; 187 } 188 189 abstract void onSimLockChangedResponse(boolean success); 190 191 @Override 192 public void run() { 193 try { 194 final boolean result = ITelephony.Stub.asInterface(ServiceManager 195 .checkService("phone")).supplyPuk(mPuk, mPin); 196 197 post(new Runnable() { 198 public void run() { 199 onSimLockChangedResponse(result); 200 } 201 }); 202 } catch (RemoteException e) { 203 post(new Runnable() { 204 public void run() { 205 onSimLockChangedResponse(false); 206 } 207 }); 208 } 209 } 210 } 211 212 private Dialog getSimUnlockProgressDialog() { 213 if (mSimUnlockProgressDialog == null) { 214 mSimUnlockProgressDialog = new ProgressDialog(mContext); 215 mSimUnlockProgressDialog.setMessage( 216 mContext.getString(R.string.kg_sim_unlock_progress_dialog_message)); 217 mSimUnlockProgressDialog.setIndeterminate(true); 218 mSimUnlockProgressDialog.setCancelable(false); 219 if (!(mContext instanceof Activity)) { 220 mSimUnlockProgressDialog.getWindow().setType( 221 WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); 222 } 223 } 224 return mSimUnlockProgressDialog; 225 } 226 227 private boolean checkPuk() { 228 // make sure the puk is at least 8 digits long. 229 if (mPasswordEntry.getText().length() >= 8) { 230 mPukText = mPasswordEntry.getText().toString(); 231 return true; 232 } 233 return false; 234 } 235 236 private boolean checkPin() { 237 // make sure the PIN is between 4 and 8 digits 238 int length = mPasswordEntry.getText().length(); 239 if (length >= 4 && length <= 8) { 240 mPinText = mPasswordEntry.getText().toString(); 241 return true; 242 } 243 return false; 244 } 245 246 public boolean confirmPin() { 247 return mPinText.equals(mPasswordEntry.getText().toString()); 248 } 249 250 private void updateSim() { 251 getSimUnlockProgressDialog().show(); 252 253 if (!mCheckInProgress) { 254 mCheckInProgress = true; 255 new CheckSimPuk(mPukText, mPinText) { 256 void onSimLockChangedResponse(final boolean success) { 257 post(new Runnable() { 258 public void run() { 259 if (mSimUnlockProgressDialog != null) { 260 mSimUnlockProgressDialog.hide(); 261 } 262 if (success) { 263 mCallback.dismiss(true); 264 } else { 265 mStateMachine.reset(); 266 mSecurityMessageDisplay.setMessage(R.string.kg_invalid_puk, true); 267 } 268 mCheckInProgress = false; 269 } 270 }); 271 } 272 }.start(); 273 } 274 } 275 276 @Override 277 protected void verifyPasswordAndUnlock() { 278 mStateMachine.next(); 279 } 280} 281 282 283