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 17 package com.android.keyguard; 18 19 import com.android.internal.telephony.ITelephony; 20 import com.android.internal.telephony.IccCardConstants; 21 import com.android.internal.telephony.IccCardConstants.State; 22 import com.android.internal.telephony.PhoneConstants; 23 24 import android.content.Context; 25 import android.content.res.ColorStateList; 26 import android.content.res.Resources; 27 import android.app.AlertDialog; 28 import android.app.AlertDialog.Builder; 29 import android.app.Dialog; 30 import android.app.ProgressDialog; 31 import android.graphics.Color; 32 import android.os.RemoteException; 33 import android.os.ServiceManager; 34 import android.telephony.SubscriptionInfo; 35 import android.telephony.SubscriptionManager; 36 import android.telephony.TelephonyManager; 37 import android.util.AttributeSet; 38 import android.util.Log; 39 import android.view.WindowManager; 40 import android.widget.ImageView; 41 42 /** 43 * Displays a PIN pad for unlocking. 44 */ 45 public class KeyguardSimPinView extends KeyguardPinBasedInputView { 46 private static final String LOG_TAG = "KeyguardSimPinView"; 47 private static final boolean DEBUG = KeyguardConstants.DEBUG_SIM_STATES; 48 public static final String TAG = "KeyguardSimPinView"; 49 50 private ProgressDialog mSimUnlockProgressDialog = null; 51 private CheckSimPin mCheckSimPinThread; 52 53 private AlertDialog mRemainingAttemptsDialog; 54 private int mSubId; 55 private ImageView mSimImageView; 56 57 KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() { 58 @Override 59 public void onSimStateChanged(int subId, int slotId, State simState) { 60 if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")"); 61 resetState(); 62 }; 63 }; 64 KeyguardSimPinView(Context context)65 public KeyguardSimPinView(Context context) { 66 this(context, null); 67 } 68 KeyguardSimPinView(Context context, AttributeSet attrs)69 public KeyguardSimPinView(Context context, AttributeSet attrs) { 70 super(context, attrs); 71 } 72 resetState()73 public void resetState() { 74 super.resetState(); 75 if (DEBUG) Log.v(TAG, "Resetting state"); 76 KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext); 77 mSubId = monitor.getNextSubIdForState(IccCardConstants.State.PIN_REQUIRED); 78 if (SubscriptionManager.isValidSubscriptionId(mSubId)) { 79 int count = TelephonyManager.getDefault().getSimCount(); 80 Resources rez = getResources(); 81 final String msg; 82 int color = Color.WHITE; 83 if (count < 2) { 84 msg = rez.getString(R.string.kg_sim_pin_instructions); 85 } else { 86 SubscriptionInfo info = monitor.getSubscriptionInfoForSubId(mSubId); 87 CharSequence displayName = info != null ? info.getDisplayName() : ""; // don't crash 88 msg = rez.getString(R.string.kg_sim_pin_instructions_multi, displayName); 89 if (info != null) { 90 color = info.getIconTint(); 91 } 92 } 93 mSecurityMessageDisplay.setMessage(msg, true); 94 mSimImageView.setImageTintList(ColorStateList.valueOf(color)); 95 } 96 } 97 98 @Override getPromtReasonStringRes(int reason)99 protected int getPromtReasonStringRes(int reason) { 100 // No message on SIM Pin 101 return 0; 102 } 103 getPinPasswordErrorMessage(int attemptsRemaining)104 private String getPinPasswordErrorMessage(int attemptsRemaining) { 105 String displayMessage; 106 107 if (attemptsRemaining == 0) { 108 displayMessage = getContext().getString(R.string.kg_password_wrong_pin_code_pukked); 109 } else if (attemptsRemaining > 0) { 110 displayMessage = getContext().getResources() 111 .getQuantityString(R.plurals.kg_password_wrong_pin_code, attemptsRemaining, 112 attemptsRemaining); 113 } else { 114 displayMessage = getContext().getString(R.string.kg_password_pin_failed); 115 } 116 if (DEBUG) Log.d(LOG_TAG, "getPinPasswordErrorMessage:" 117 + " attemptsRemaining=" + attemptsRemaining + " displayMessage=" + displayMessage); 118 return displayMessage; 119 } 120 121 @Override shouldLockout(long deadline)122 protected boolean shouldLockout(long deadline) { 123 // SIM PIN doesn't have a timed lockout 124 return false; 125 } 126 127 @Override getPasswordTextViewId()128 protected int getPasswordTextViewId() { 129 return R.id.simPinEntry; 130 } 131 132 @Override onFinishInflate()133 protected void onFinishInflate() { 134 super.onFinishInflate(); 135 136 mSecurityMessageDisplay.setTimeout(0); // don't show ownerinfo/charging status by default 137 if (mEcaView instanceof EmergencyCarrierArea) { 138 ((EmergencyCarrierArea) mEcaView).setCarrierTextVisible(true); 139 } 140 mSimImageView = (ImageView) findViewById(R.id.keyguard_sim); 141 } 142 143 @Override onAttachedToWindow()144 protected void onAttachedToWindow() { 145 super.onAttachedToWindow(); 146 KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitorCallback); 147 } 148 149 @Override onDetachedFromWindow()150 protected void onDetachedFromWindow() { 151 super.onDetachedFromWindow(); 152 KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateMonitorCallback); 153 } 154 155 @Override showUsabilityHint()156 public void showUsabilityHint() { 157 } 158 159 @Override onPause()160 public void onPause() { 161 // dismiss the dialog. 162 if (mSimUnlockProgressDialog != null) { 163 mSimUnlockProgressDialog.dismiss(); 164 mSimUnlockProgressDialog = null; 165 } 166 } 167 168 /** 169 * Since the IPC can block, we want to run the request in a separate thread 170 * with a callback. 171 */ 172 private abstract class CheckSimPin extends Thread { 173 private final String mPin; 174 private int mSubId; 175 CheckSimPin(String pin, int subId)176 protected CheckSimPin(String pin, int subId) { 177 mPin = pin; 178 mSubId = subId; 179 } 180 onSimCheckResponse(final int result, final int attemptsRemaining)181 abstract void onSimCheckResponse(final int result, final int attemptsRemaining); 182 183 @Override run()184 public void run() { 185 try { 186 if (DEBUG) { 187 Log.v(TAG, "call supplyPinReportResultForSubscriber(subid=" + mSubId + ")"); 188 } 189 final int[] result = ITelephony.Stub.asInterface(ServiceManager 190 .checkService("phone")).supplyPinReportResultForSubscriber(mSubId, mPin); 191 if (DEBUG) { 192 Log.v(TAG, "supplyPinReportResult returned: " + result[0] + " " + result[1]); 193 } 194 post(new Runnable() { 195 public void run() { 196 onSimCheckResponse(result[0], result[1]); 197 } 198 }); 199 } catch (RemoteException e) { 200 Log.e(TAG, "RemoteException for supplyPinReportResult:", e); 201 post(new Runnable() { 202 public void run() { 203 onSimCheckResponse(PhoneConstants.PIN_GENERAL_FAILURE, -1); 204 } 205 }); 206 } 207 } 208 } 209 getSimUnlockProgressDialog()210 private Dialog getSimUnlockProgressDialog() { 211 if (mSimUnlockProgressDialog == null) { 212 mSimUnlockProgressDialog = new ProgressDialog(mContext); 213 mSimUnlockProgressDialog.setMessage( 214 mContext.getString(R.string.kg_sim_unlock_progress_dialog_message)); 215 mSimUnlockProgressDialog.setIndeterminate(true); 216 mSimUnlockProgressDialog.setCancelable(false); 217 mSimUnlockProgressDialog.getWindow().setType( 218 WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); 219 } 220 return mSimUnlockProgressDialog; 221 } 222 getSimRemainingAttemptsDialog(int remaining)223 private Dialog getSimRemainingAttemptsDialog(int remaining) { 224 String msg = getPinPasswordErrorMessage(remaining); 225 if (mRemainingAttemptsDialog == null) { 226 Builder builder = new AlertDialog.Builder(mContext); 227 builder.setMessage(msg); 228 builder.setCancelable(false); 229 builder.setNeutralButton(R.string.ok, null); 230 mRemainingAttemptsDialog = builder.create(); 231 mRemainingAttemptsDialog.getWindow().setType( 232 WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); 233 } else { 234 mRemainingAttemptsDialog.setMessage(msg); 235 } 236 return mRemainingAttemptsDialog; 237 } 238 239 @Override verifyPasswordAndUnlock()240 protected void verifyPasswordAndUnlock() { 241 String entry = mPasswordEntry.getText(); 242 243 if (entry.length() < 4) { 244 // otherwise, display a message to the user, and don't submit. 245 mSecurityMessageDisplay.setMessage(R.string.kg_invalid_sim_pin_hint, true); 246 resetPasswordText(true); 247 mCallback.userActivity(); 248 return; 249 } 250 251 getSimUnlockProgressDialog().show(); 252 253 if (mCheckSimPinThread == null) { 254 mCheckSimPinThread = new CheckSimPin(mPasswordEntry.getText(), mSubId) { 255 void onSimCheckResponse(final int result, final int attemptsRemaining) { 256 post(new Runnable() { 257 public void run() { 258 if (mSimUnlockProgressDialog != null) { 259 mSimUnlockProgressDialog.hide(); 260 } 261 resetPasswordText(true /* animate */); 262 if (result == PhoneConstants.PIN_RESULT_SUCCESS) { 263 KeyguardUpdateMonitor.getInstance(getContext()) 264 .reportSimUnlocked(mSubId); 265 mCallback.dismiss(true); 266 } else { 267 if (result == PhoneConstants.PIN_PASSWORD_INCORRECT) { 268 if (attemptsRemaining <= 2) { 269 // this is getting critical - show dialog 270 getSimRemainingAttemptsDialog(attemptsRemaining).show(); 271 } else { 272 // show message 273 mSecurityMessageDisplay.setMessage( 274 getPinPasswordErrorMessage(attemptsRemaining), true); 275 } 276 } else { 277 // "PIN operation failed!" - no idea what this was and no way to 278 // find out. :/ 279 mSecurityMessageDisplay.setMessage(getContext().getString( 280 R.string.kg_password_pin_failed), true); 281 } 282 if (DEBUG) Log.d(LOG_TAG, "verifyPasswordAndUnlock " 283 + " CheckSimPin.onSimCheckResponse: " + result 284 + " attemptsRemaining=" + attemptsRemaining); 285 } 286 mCallback.userActivity(); 287 mCheckSimPinThread = null; 288 } 289 }); 290 } 291 }; 292 mCheckSimPinThread.start(); 293 } 294 } 295 296 @Override startAppearAnimation()297 public void startAppearAnimation() { 298 // noop. 299 } 300 301 @Override startDisappearAnimation(Runnable finishRunnable)302 public boolean startDisappearAnimation(Runnable finishRunnable) { 303 return false; 304 } 305 } 306 307