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