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