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