• 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 
74     @Override
resetState()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);
96             mSimImageView.setImageTintList(ColorStateList.valueOf(color));
97         }
98     }
99 
100     @Override
onConfigurationChanged(Configuration newConfig)101     protected void onConfigurationChanged(Configuration newConfig) {
102         super.onConfigurationChanged(newConfig);
103         resetState();
104     }
105 
106     @Override
getPromtReasonStringRes(int reason)107     protected int getPromtReasonStringRes(int reason) {
108         // No message on SIM Pin
109         return 0;
110     }
111 
getPinPasswordErrorMessage(int attemptsRemaining)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
shouldLockout(long deadline)130     protected boolean shouldLockout(long deadline) {
131         // SIM PIN doesn't have a timed lockout
132         return false;
133     }
134 
135     @Override
getPasswordTextViewId()136     protected int getPasswordTextViewId() {
137         return R.id.simPinEntry;
138     }
139 
140     @Override
onFinishInflate()141     protected void onFinishInflate() {
142         super.onFinishInflate();
143 
144         if (mEcaView instanceof EmergencyCarrierArea) {
145             ((EmergencyCarrierArea) mEcaView).setCarrierTextVisible(true);
146         }
147         mSimImageView = 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                     @Override
203                     public void run() {
204                         onSimCheckResponse(result[0], result[1]);
205                     }
206                 });
207             } catch (RemoteException e) {
208                 Log.e(TAG, "RemoteException for supplyPinReportResult:", e);
209                 post(new Runnable() {
210                     @Override
211                     public void run() {
212                         onSimCheckResponse(PhoneConstants.PIN_GENERAL_FAILURE, -1);
213                     }
214                 });
215             }
216         }
217     }
218 
getSimUnlockProgressDialog()219     private Dialog getSimUnlockProgressDialog() {
220         if (mSimUnlockProgressDialog == null) {
221             mSimUnlockProgressDialog = new ProgressDialog(mContext);
222             mSimUnlockProgressDialog.setMessage(
223                     mContext.getString(R.string.kg_sim_unlock_progress_dialog_message));
224             mSimUnlockProgressDialog.setIndeterminate(true);
225             mSimUnlockProgressDialog.setCancelable(false);
226             mSimUnlockProgressDialog.getWindow().setType(
227                     WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
228         }
229         return mSimUnlockProgressDialog;
230     }
231 
getSimRemainingAttemptsDialog(int remaining)232     private Dialog getSimRemainingAttemptsDialog(int remaining) {
233         String msg = getPinPasswordErrorMessage(remaining);
234         if (mRemainingAttemptsDialog == null) {
235             Builder builder = new AlertDialog.Builder(mContext);
236             builder.setMessage(msg);
237             builder.setCancelable(false);
238             builder.setNeutralButton(R.string.ok, null);
239             mRemainingAttemptsDialog = builder.create();
240             mRemainingAttemptsDialog.getWindow().setType(
241                     WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
242         } else {
243             mRemainingAttemptsDialog.setMessage(msg);
244         }
245         return mRemainingAttemptsDialog;
246     }
247 
248     @Override
verifyPasswordAndUnlock()249     protected void verifyPasswordAndUnlock() {
250         String entry = mPasswordEntry.getText();
251 
252         if (entry.length() < 4) {
253             // otherwise, display a message to the user, and don't submit.
254             mSecurityMessageDisplay.setMessage(R.string.kg_invalid_sim_pin_hint);
255             resetPasswordText(true /* animate */, true /* announce */);
256             mCallback.userActivity();
257             return;
258         }
259 
260         getSimUnlockProgressDialog().show();
261 
262         if (mCheckSimPinThread == null) {
263             mCheckSimPinThread = new CheckSimPin(mPasswordEntry.getText(), mSubId) {
264                 @Override
265                 void onSimCheckResponse(final int result, final int attemptsRemaining) {
266                     post(new Runnable() {
267                         @Override
268                         public void run() {
269                             if (mSimUnlockProgressDialog != null) {
270                                 mSimUnlockProgressDialog.hide();
271                             }
272                             resetPasswordText(true /* animate */,
273                                     result != PhoneConstants.PIN_RESULT_SUCCESS /* announce */);
274                             if (result == PhoneConstants.PIN_RESULT_SUCCESS) {
275                                 KeyguardUpdateMonitor.getInstance(getContext())
276                                         .reportSimUnlocked(mSubId);
277                                 mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
278                             } else {
279                                 if (result == PhoneConstants.PIN_PASSWORD_INCORRECT) {
280                                     if (attemptsRemaining <= 2) {
281                                         // this is getting critical - show dialog
282                                         getSimRemainingAttemptsDialog(attemptsRemaining).show();
283                                     } else {
284                                         // show message
285                                         mSecurityMessageDisplay.setMessage(
286                                                 getPinPasswordErrorMessage(attemptsRemaining));
287                                     }
288                                 } else {
289                                     // "PIN operation failed!" - no idea what this was and no way to
290                                     // find out. :/
291                                     mSecurityMessageDisplay.setMessage(getContext().getString(
292                                             R.string.kg_password_pin_failed));
293                                 }
294                                 if (DEBUG) Log.d(LOG_TAG, "verifyPasswordAndUnlock "
295                                         + " CheckSimPin.onSimCheckResponse: " + result
296                                         + " attemptsRemaining=" + attemptsRemaining);
297                             }
298                             mCallback.userActivity();
299                             mCheckSimPinThread = null;
300                         }
301                     });
302                 }
303             };
304             mCheckSimPinThread.start();
305         }
306     }
307 
308     @Override
startAppearAnimation()309     public void startAppearAnimation() {
310         // noop.
311     }
312 
313     @Override
startDisappearAnimation(Runnable finishRunnable)314     public boolean startDisappearAnimation(Runnable finishRunnable) {
315         return false;
316     }
317 }
318 
319