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 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 
     65     public KeyguardSimPinView(Context context) {
     66         this(context, null);
     67     }
     68 
     69     public KeyguardSimPinView(Context context, AttributeSet attrs) {
     70         super(context, attrs);
     71     }
     72 
     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     private String getPinPasswordErrorMessage(int attemptsRemaining) {
     99         String displayMessage;
    100 
    101         if (attemptsRemaining == 0) {
    102             displayMessage = getContext().getString(R.string.kg_password_wrong_pin_code_pukked);
    103         } else if (attemptsRemaining > 0) {
    104             displayMessage = getContext().getResources()
    105                     .getQuantityString(R.plurals.kg_password_wrong_pin_code, attemptsRemaining,
    106                             attemptsRemaining);
    107         } else {
    108             displayMessage = getContext().getString(R.string.kg_password_pin_failed);
    109         }
    110         if (DEBUG) Log.d(LOG_TAG, "getPinPasswordErrorMessage:"
    111                 + " attemptsRemaining=" + attemptsRemaining + " displayMessage=" + displayMessage);
    112         return displayMessage;
    113     }
    114 
    115     @Override
    116     protected boolean shouldLockout(long deadline) {
    117         // SIM PIN doesn't have a timed lockout
    118         return false;
    119     }
    120 
    121     @Override
    122     protected int getPasswordTextViewId() {
    123         return R.id.simPinEntry;
    124     }
    125 
    126     @Override
    127     protected void onFinishInflate() {
    128         super.onFinishInflate();
    129 
    130         mSecurityMessageDisplay.setTimeout(0); // don't show ownerinfo/charging status by default
    131         if (mEcaView instanceof EmergencyCarrierArea) {
    132             ((EmergencyCarrierArea) mEcaView).setCarrierTextVisible(true);
    133         }
    134         mSimImageView = (ImageView) findViewById(R.id.keyguard_sim);
    135     }
    136 
    137     @Override
    138     protected void onAttachedToWindow() {
    139         super.onAttachedToWindow();
    140         KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitorCallback);
    141     }
    142 
    143     @Override
    144     protected void onDetachedFromWindow() {
    145         super.onDetachedFromWindow();
    146         KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateMonitorCallback);
    147     }
    148 
    149     @Override
    150     public void showUsabilityHint() {
    151     }
    152 
    153     @Override
    154     public void onPause() {
    155         // dismiss the dialog.
    156         if (mSimUnlockProgressDialog != null) {
    157             mSimUnlockProgressDialog.dismiss();
    158             mSimUnlockProgressDialog = null;
    159         }
    160     }
    161 
    162     /**
    163      * Since the IPC can block, we want to run the request in a separate thread
    164      * with a callback.
    165      */
    166     private abstract class CheckSimPin extends Thread {
    167         private final String mPin;
    168         private int mSubId;
    169 
    170         protected CheckSimPin(String pin, int subId) {
    171             mPin = pin;
    172             mSubId = subId;
    173         }
    174 
    175         abstract void onSimCheckResponse(final int result, final int attemptsRemaining);
    176 
    177         @Override
    178         public void run() {
    179             try {
    180                 if (DEBUG) {
    181                     Log.v(TAG, "call supplyPinReportResultForSubscriber(subid=" + mSubId + ")");
    182                 }
    183                 final int[] result = ITelephony.Stub.asInterface(ServiceManager
    184                         .checkService("phone")).supplyPinReportResultForSubscriber(mSubId, mPin);
    185                 if (DEBUG) {
    186                     Log.v(TAG, "supplyPinReportResult returned: " + result[0] + " " + result[1]);
    187                 }
    188                 post(new Runnable() {
    189                     public void run() {
    190                         onSimCheckResponse(result[0], result[1]);
    191                     }
    192                 });
    193             } catch (RemoteException e) {
    194                 Log.e(TAG, "RemoteException for supplyPinReportResult:", e);
    195                 post(new Runnable() {
    196                     public void run() {
    197                         onSimCheckResponse(PhoneConstants.PIN_GENERAL_FAILURE, -1);
    198                     }
    199                 });
    200             }
    201         }
    202     }
    203 
    204     private Dialog getSimUnlockProgressDialog() {
    205         if (mSimUnlockProgressDialog == null) {
    206             mSimUnlockProgressDialog = new ProgressDialog(mContext);
    207             mSimUnlockProgressDialog.setMessage(
    208                     mContext.getString(R.string.kg_sim_unlock_progress_dialog_message));
    209             mSimUnlockProgressDialog.setIndeterminate(true);
    210             mSimUnlockProgressDialog.setCancelable(false);
    211             mSimUnlockProgressDialog.getWindow().setType(
    212                     WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
    213         }
    214         return mSimUnlockProgressDialog;
    215     }
    216 
    217     private Dialog getSimRemainingAttemptsDialog(int remaining) {
    218         String msg = getPinPasswordErrorMessage(remaining);
    219         if (mRemainingAttemptsDialog == null) {
    220             Builder builder = new AlertDialog.Builder(mContext);
    221             builder.setMessage(msg);
    222             builder.setCancelable(false);
    223             builder.setNeutralButton(R.string.ok, null);
    224             mRemainingAttemptsDialog = builder.create();
    225             mRemainingAttemptsDialog.getWindow().setType(
    226                     WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
    227         } else {
    228             mRemainingAttemptsDialog.setMessage(msg);
    229         }
    230         return mRemainingAttemptsDialog;
    231     }
    232 
    233     @Override
    234     protected void verifyPasswordAndUnlock() {
    235         String entry = mPasswordEntry.getText();
    236 
    237         if (entry.length() < 4) {
    238             // otherwise, display a message to the user, and don't submit.
    239             mSecurityMessageDisplay.setMessage(R.string.kg_invalid_sim_pin_hint, true);
    240             resetPasswordText(true);
    241             mCallback.userActivity();
    242             return;
    243         }
    244 
    245         getSimUnlockProgressDialog().show();
    246 
    247         if (mCheckSimPinThread == null) {
    248             mCheckSimPinThread = new CheckSimPin(mPasswordEntry.getText(), mSubId) {
    249                 void onSimCheckResponse(final int result, final int attemptsRemaining) {
    250                     post(new Runnable() {
    251                         public void run() {
    252                             if (mSimUnlockProgressDialog != null) {
    253                                 mSimUnlockProgressDialog.hide();
    254                             }
    255                             resetPasswordText(true /* animate */);
    256                             if (result == PhoneConstants.PIN_RESULT_SUCCESS) {
    257                                 KeyguardUpdateMonitor.getInstance(getContext())
    258                                         .reportSimUnlocked(mSubId);
    259                                 mCallback.dismiss(true);
    260                             } else {
    261                                 if (result == PhoneConstants.PIN_PASSWORD_INCORRECT) {
    262                                     if (attemptsRemaining <= 2) {
    263                                         // this is getting critical - show dialog
    264                                         getSimRemainingAttemptsDialog(attemptsRemaining).show();
    265                                     } else {
    266                                         // show message
    267                                         mSecurityMessageDisplay.setMessage(
    268                                                 getPinPasswordErrorMessage(attemptsRemaining), true);
    269                                     }
    270                                 } else {
    271                                     // "PIN operation failed!" - no idea what this was and no way to
    272                                     // find out. :/
    273                                     mSecurityMessageDisplay.setMessage(getContext().getString(
    274                                             R.string.kg_password_pin_failed), true);
    275                                 }
    276                                 if (DEBUG) Log.d(LOG_TAG, "verifyPasswordAndUnlock "
    277                                         + " CheckSimPin.onSimCheckResponse: " + result
    278                                         + " attemptsRemaining=" + attemptsRemaining);
    279                             }
    280                             mCallback.userActivity();
    281                             mCheckSimPinThread = null;
    282                         }
    283                     });
    284                 }
    285             };
    286             mCheckSimPinThread.start();
    287         }
    288     }
    289 
    290     @Override
    291     public void startAppearAnimation() {
    292         // noop.
    293     }
    294 
    295     @Override
    296     public boolean startDisappearAnimation(Runnable finishRunnable) {
    297         return false;
    298     }
    299 }
    300 
    301