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