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