• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
KeyguardSimPinView(Context context)65     public KeyguardSimPinView(Context context) {
66         this(context, null);
67     }
68 
KeyguardSimPinView(Context context, AttributeSet attrs)69     public KeyguardSimPinView(Context context, AttributeSet attrs) {
70         super(context, attrs);
71     }
72 
resetState()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 
getPinPasswordErrorMessage(int attemptsRemaining)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
shouldLockout(long deadline)116     protected boolean shouldLockout(long deadline) {
117         // SIM PIN doesn't have a timed lockout
118         return false;
119     }
120 
121     @Override
getPasswordTextViewId()122     protected int getPasswordTextViewId() {
123         return R.id.simPinEntry;
124     }
125 
126     @Override
onFinishInflate()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
onAttachedToWindow()138     protected void onAttachedToWindow() {
139         super.onAttachedToWindow();
140         KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitorCallback);
141     }
142 
143     @Override
onDetachedFromWindow()144     protected void onDetachedFromWindow() {
145         super.onDetachedFromWindow();
146         KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateMonitorCallback);
147     }
148 
149     @Override
showUsabilityHint()150     public void showUsabilityHint() {
151     }
152 
153     @Override
onPause()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 
CheckSimPin(String pin, int subId)170         protected CheckSimPin(String pin, int subId) {
171             mPin = pin;
172             mSubId = subId;
173         }
174 
onSimCheckResponse(final int result, final int attemptsRemaining)175         abstract void onSimCheckResponse(final int result, final int attemptsRemaining);
176 
177         @Override
run()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 
getSimUnlockProgressDialog()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 
getSimRemainingAttemptsDialog(int remaining)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
verifyPasswordAndUnlock()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
startAppearAnimation()291     public void startAppearAnimation() {
292         // noop.
293     }
294 
295     @Override
startDisappearAnimation(Runnable finishRunnable)296     public boolean startDisappearAnimation(Runnable finishRunnable) {
297         return false;
298     }
299 }
300 
301