KeyguardSimPukView.java revision 195d9143e2c00550b494e5ea00d79cf9d018fbf6
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.content.res.ColorStateList; 21import android.content.res.Resources; 22import android.app.Activity; 23import android.app.AlertDialog; 24import android.app.Dialog; 25import android.app.ProgressDialog; 26import android.graphics.Color; 27import android.os.RemoteException; 28import android.os.ServiceManager; 29import android.telephony.SubscriptionInfo; 30import android.telephony.SubscriptionManager; 31import android.telephony.TelephonyManager; 32import android.telephony.euicc.EuiccManager; 33import android.util.AttributeSet; 34import android.util.Log; 35import android.view.WindowManager; 36import android.widget.ImageView; 37 38import com.android.internal.telephony.ITelephony; 39import com.android.internal.telephony.IccCardConstants; 40import com.android.internal.telephony.PhoneConstants; 41import com.android.internal.telephony.IccCardConstants.State; 42 43 44/** 45 * Displays a PIN pad for entering a PUK (Pin Unlock Kode) provided by a carrier. 46 */ 47public class KeyguardSimPukView extends KeyguardPinBasedInputView { 48 private static final String LOG_TAG = "KeyguardSimPukView"; 49 private static final boolean DEBUG = KeyguardConstants.DEBUG; 50 public static final String TAG = "KeyguardSimPukView"; 51 52 private ProgressDialog mSimUnlockProgressDialog = null; 53 private CheckSimPuk mCheckSimPukThread; 54 private String mPukText; 55 private String mPinText; 56 private StateMachine mStateMachine = new StateMachine(); 57 private AlertDialog mRemainingAttemptsDialog; 58 private int mSubId; 59 private ImageView mSimImageView; 60 61 KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() { 62 @Override 63 public void onSimStateChanged(int subId, int slotId, State simState) { 64 if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")"); 65 resetState(); 66 }; 67 }; 68 69 public KeyguardSimPukView(Context context) { 70 this(context, null); 71 } 72 73 public KeyguardSimPukView(Context context, AttributeSet attrs) { 74 super(context, attrs); 75 } 76 77 private class StateMachine { 78 final int ENTER_PUK = 0; 79 final int ENTER_PIN = 1; 80 final int CONFIRM_PIN = 2; 81 final int DONE = 3; 82 private int state = ENTER_PUK; 83 84 public void next() { 85 int msg = 0; 86 if (state == ENTER_PUK) { 87 if (checkPuk()) { 88 state = ENTER_PIN; 89 msg = R.string.kg_puk_enter_pin_hint; 90 } else { 91 msg = R.string.kg_invalid_sim_puk_hint; 92 } 93 } else if (state == ENTER_PIN) { 94 if (checkPin()) { 95 state = CONFIRM_PIN; 96 msg = R.string.kg_enter_confirm_pin_hint; 97 } else { 98 msg = R.string.kg_invalid_sim_pin_hint; 99 } 100 } else if (state == CONFIRM_PIN) { 101 if (confirmPin()) { 102 state = DONE; 103 msg = R.string.keyguard_sim_unlock_progress_dialog_message; 104 updateSim(); 105 } else { 106 state = ENTER_PIN; // try again? 107 msg = R.string.kg_invalid_confirm_pin_hint; 108 } 109 } 110 resetPasswordText(true /* animate */, true /* announce */); 111 if (msg != 0) { 112 mSecurityMessageDisplay.setMessage(msg); 113 } 114 } 115 116 void reset() { 117 mPinText=""; 118 mPukText=""; 119 state = ENTER_PUK; 120 KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext); 121 mSubId = monitor.getNextSubIdForState(IccCardConstants.State.PUK_REQUIRED); 122 if (SubscriptionManager.isValidSubscriptionId(mSubId)) { 123 int count = TelephonyManager.getDefault().getSimCount(); 124 Resources rez = getResources(); 125 String msg; 126 int color = Color.WHITE; 127 if (count < 2) { 128 msg = rez.getString(R.string.kg_puk_enter_puk_hint); 129 } else { 130 SubscriptionInfo info = monitor.getSubscriptionInfoForSubId(mSubId); 131 CharSequence displayName = info != null ? info.getDisplayName() : ""; 132 msg = rez.getString(R.string.kg_puk_enter_puk_hint_multi, displayName); 133 if (info != null) { 134 color = info.getIconTint(); 135 } 136 } 137 EuiccManager euiccManager = 138 (EuiccManager) mContext.getSystemService(Context.EUICC_SERVICE); 139 if (euiccManager.isEnabled()) { 140 msg = msg + " " + rez.getString(R.string.kg_sim_lock_instructions_esim); 141 } 142 mSecurityMessageDisplay.setMessage(msg); 143 mSimImageView.setImageTintList(ColorStateList.valueOf(color)); 144 } 145 mPasswordEntry.requestFocus(); 146 } 147 } 148 149 @Override 150 protected int getPromtReasonStringRes(int reason) { 151 // No message on SIM Puk 152 return 0; 153 } 154 155 private String getPukPasswordErrorMessage(int attemptsRemaining) { 156 String displayMessage; 157 158 if (attemptsRemaining == 0) { 159 displayMessage = getContext().getString(R.string.kg_password_wrong_puk_code_dead); 160 } else if (attemptsRemaining > 0) { 161 displayMessage = getContext().getResources() 162 .getQuantityString(R.plurals.kg_password_wrong_puk_code, attemptsRemaining, 163 attemptsRemaining); 164 } else { 165 displayMessage = getContext().getString(R.string.kg_password_puk_failed); 166 } 167 if (DEBUG) Log.d(LOG_TAG, "getPukPasswordErrorMessage:" 168 + " attemptsRemaining=" + attemptsRemaining + " displayMessage=" + displayMessage); 169 return displayMessage; 170 } 171 172 @Override 173 public void resetState() { 174 super.resetState(); 175 mStateMachine.reset(); 176 } 177 178 @Override 179 protected boolean shouldLockout(long deadline) { 180 // SIM PUK doesn't have a timed lockout 181 return false; 182 } 183 184 @Override 185 protected int getPasswordTextViewId() { 186 return R.id.pukEntry; 187 } 188 189 @Override 190 protected void onFinishInflate() { 191 super.onFinishInflate(); 192 193 if (mEcaView instanceof EmergencyCarrierArea) { 194 ((EmergencyCarrierArea) mEcaView).setCarrierTextVisible(true); 195 } 196 mSimImageView = findViewById(R.id.keyguard_sim); 197 } 198 199 @Override 200 protected void onAttachedToWindow() { 201 super.onAttachedToWindow(); 202 KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitorCallback); 203 } 204 205 @Override 206 protected void onDetachedFromWindow() { 207 super.onDetachedFromWindow(); 208 KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateMonitorCallback); 209 } 210 211 @Override 212 public void showUsabilityHint() { 213 } 214 215 @Override 216 public void onPause() { 217 // dismiss the dialog. 218 if (mSimUnlockProgressDialog != null) { 219 mSimUnlockProgressDialog.dismiss(); 220 mSimUnlockProgressDialog = null; 221 } 222 } 223 224 /** 225 * Since the IPC can block, we want to run the request in a separate thread 226 * with a callback. 227 */ 228 private abstract class CheckSimPuk extends Thread { 229 230 private final String mPin, mPuk; 231 private final int mSubId; 232 233 protected CheckSimPuk(String puk, String pin, int subId) { 234 mPuk = puk; 235 mPin = pin; 236 mSubId = subId; 237 } 238 239 abstract void onSimLockChangedResponse(final int result, final int attemptsRemaining); 240 241 @Override 242 public void run() { 243 try { 244 if (DEBUG) Log.v(TAG, "call supplyPukReportResult()"); 245 final int[] result = ITelephony.Stub.asInterface(ServiceManager 246 .checkService("phone")).supplyPukReportResultForSubscriber(mSubId, mPuk, mPin); 247 if (DEBUG) { 248 Log.v(TAG, "supplyPukReportResult returned: " + result[0] + " " + result[1]); 249 } 250 post(new Runnable() { 251 @Override 252 public void run() { 253 onSimLockChangedResponse(result[0], result[1]); 254 } 255 }); 256 } catch (RemoteException e) { 257 Log.e(TAG, "RemoteException for supplyPukReportResult:", e); 258 post(new Runnable() { 259 @Override 260 public void run() { 261 onSimLockChangedResponse(PhoneConstants.PIN_GENERAL_FAILURE, -1); 262 } 263 }); 264 } 265 } 266 } 267 268 private Dialog getSimUnlockProgressDialog() { 269 if (mSimUnlockProgressDialog == null) { 270 mSimUnlockProgressDialog = new ProgressDialog(mContext); 271 mSimUnlockProgressDialog.setMessage( 272 mContext.getString(R.string.kg_sim_unlock_progress_dialog_message)); 273 mSimUnlockProgressDialog.setIndeterminate(true); 274 mSimUnlockProgressDialog.setCancelable(false); 275 if (!(mContext instanceof Activity)) { 276 mSimUnlockProgressDialog.getWindow().setType( 277 WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); 278 } 279 } 280 return mSimUnlockProgressDialog; 281 } 282 283 private Dialog getPukRemainingAttemptsDialog(int remaining) { 284 String msg = getPukPasswordErrorMessage(remaining); 285 if (mRemainingAttemptsDialog == null) { 286 AlertDialog.Builder builder = new AlertDialog.Builder(mContext); 287 builder.setMessage(msg); 288 builder.setCancelable(false); 289 builder.setNeutralButton(R.string.ok, null); 290 mRemainingAttemptsDialog = builder.create(); 291 mRemainingAttemptsDialog.getWindow().setType( 292 WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); 293 } else { 294 mRemainingAttemptsDialog.setMessage(msg); 295 } 296 return mRemainingAttemptsDialog; 297 } 298 299 private boolean checkPuk() { 300 // make sure the puk is at least 8 digits long. 301 if (mPasswordEntry.getText().length() == 8) { 302 mPukText = mPasswordEntry.getText(); 303 return true; 304 } 305 return false; 306 } 307 308 private boolean checkPin() { 309 // make sure the PIN is between 4 and 8 digits 310 int length = mPasswordEntry.getText().length(); 311 if (length >= 4 && length <= 8) { 312 mPinText = mPasswordEntry.getText(); 313 return true; 314 } 315 return false; 316 } 317 318 public boolean confirmPin() { 319 return mPinText.equals(mPasswordEntry.getText()); 320 } 321 322 private void updateSim() { 323 getSimUnlockProgressDialog().show(); 324 325 if (mCheckSimPukThread == null) { 326 mCheckSimPukThread = new CheckSimPuk(mPukText, mPinText, mSubId) { 327 @Override 328 void onSimLockChangedResponse(final int result, final int attemptsRemaining) { 329 post(new Runnable() { 330 @Override 331 public void run() { 332 if (mSimUnlockProgressDialog != null) { 333 mSimUnlockProgressDialog.hide(); 334 } 335 resetPasswordText(true /* animate */, 336 result != PhoneConstants.PIN_RESULT_SUCCESS /* announce */); 337 if (result == PhoneConstants.PIN_RESULT_SUCCESS) { 338 KeyguardUpdateMonitor.getInstance(getContext()) 339 .reportSimUnlocked(mSubId); 340 mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser()); 341 } else { 342 if (result == PhoneConstants.PIN_PASSWORD_INCORRECT) { 343 if (attemptsRemaining <= 2) { 344 // this is getting critical - show dialog 345 getPukRemainingAttemptsDialog(attemptsRemaining).show(); 346 } else { 347 // show message 348 mSecurityMessageDisplay.setMessage( 349 getPukPasswordErrorMessage(attemptsRemaining)); 350 } 351 } else { 352 mSecurityMessageDisplay.setMessage(getContext().getString( 353 R.string.kg_password_puk_failed)); 354 } 355 if (DEBUG) Log.d(LOG_TAG, "verifyPasswordAndUnlock " 356 + " UpdateSim.onSimCheckResponse: " 357 + " attemptsRemaining=" + attemptsRemaining); 358 mStateMachine.reset(); 359 } 360 mCheckSimPukThread = null; 361 } 362 }); 363 } 364 }; 365 mCheckSimPukThread.start(); 366 } 367 } 368 369 @Override 370 protected void verifyPasswordAndUnlock() { 371 mStateMachine.next(); 372 } 373 374 @Override 375 public void startAppearAnimation() { 376 // noop. 377 } 378 379 @Override 380 public boolean startDisappearAnimation(Runnable finishRunnable) { 381 return false; 382 } 383} 384 385 386