KeyguardSimPinView.java revision 76a1623afc170a13923b68f3256057d8adeb7937
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
17package com.android.keyguard;
18
19import com.android.internal.telephony.ITelephony;
20import com.android.internal.telephony.PhoneConstants;
21
22import android.content.Context;
23import android.app.AlertDialog;
24import android.app.AlertDialog.Builder;
25import android.app.Dialog;
26import android.app.ProgressDialog;
27import android.os.RemoteException;
28import android.os.ServiceManager;
29import android.util.AttributeSet;
30import android.util.Log;
31import android.view.WindowManager;
32
33/**
34 * Displays a PIN pad for unlocking.
35 */
36public class KeyguardSimPinView extends KeyguardPinBasedInputView {
37    private static final String LOG_TAG = "KeyguardSimPinView";
38    private static final boolean DEBUG = KeyguardConstants.DEBUG;
39    public static final String TAG = "KeyguardSimPinView";
40
41    private ProgressDialog mSimUnlockProgressDialog = null;
42    private CheckSimPin mCheckSimPinThread;
43
44    private AlertDialog mRemainingAttemptsDialog;
45
46    public KeyguardSimPinView(Context context) {
47        this(context, null);
48    }
49
50    public KeyguardSimPinView(Context context, AttributeSet attrs) {
51        super(context, attrs);
52    }
53
54    public void resetState() {
55        super.resetState();
56        mSecurityMessageDisplay.setMessage(R.string.kg_sim_pin_instructions, true);
57    }
58
59    private String getPinPasswordErrorMessage(int attemptsRemaining) {
60        String displayMessage;
61
62        if (attemptsRemaining == 0) {
63            displayMessage = getContext().getString(R.string.kg_password_wrong_pin_code_pukked);
64        } else if (attemptsRemaining > 0) {
65            displayMessage = getContext().getResources()
66                    .getQuantityString(R.plurals.kg_password_wrong_pin_code, attemptsRemaining,
67                            attemptsRemaining);
68        } else {
69            displayMessage = getContext().getString(R.string.kg_password_pin_failed);
70        }
71        if (DEBUG) Log.d(LOG_TAG, "getPinPasswordErrorMessage:"
72                + " attemptsRemaining=" + attemptsRemaining + " displayMessage=" + displayMessage);
73        return displayMessage;
74    }
75
76    @Override
77    protected boolean shouldLockout(long deadline) {
78        // SIM PIN doesn't have a timed lockout
79        return false;
80    }
81
82    @Override
83    protected int getPasswordTextViewId() {
84        return R.id.simPinEntry;
85    }
86
87    @Override
88    protected void onFinishInflate() {
89        super.onFinishInflate();
90
91        mSecurityMessageDisplay.setTimeout(0); // don't show ownerinfo/charging status by default
92        if (mEcaView instanceof EmergencyCarrierArea) {
93            ((EmergencyCarrierArea) mEcaView).setCarrierTextVisible(true);
94        }
95    }
96
97    @Override
98    public void showUsabilityHint() {
99    }
100
101    @Override
102    public void onPause() {
103        // dismiss the dialog.
104        if (mSimUnlockProgressDialog != null) {
105            mSimUnlockProgressDialog.dismiss();
106            mSimUnlockProgressDialog = null;
107        }
108    }
109
110    /**
111     * Since the IPC can block, we want to run the request in a separate thread
112     * with a callback.
113     */
114    private abstract class CheckSimPin extends Thread {
115        private final String mPin;
116
117        protected CheckSimPin(String pin) {
118            mPin = pin;
119        }
120
121        abstract void onSimCheckResponse(final int result, final int attemptsRemaining);
122
123        @Override
124        public void run() {
125            try {
126                Log.v(TAG, "call supplyPinReportResult()");
127                final int[] result = ITelephony.Stub.asInterface(ServiceManager
128                        .checkService("phone")).supplyPinReportResult(mPin);
129                Log.v(TAG, "supplyPinReportResult returned: " + result[0] + " " + result[1]);
130                post(new Runnable() {
131                    public void run() {
132                        onSimCheckResponse(result[0], result[1]);
133                    }
134                });
135            } catch (RemoteException e) {
136                Log.e(TAG, "RemoteException for supplyPinReportResult:", e);
137                post(new Runnable() {
138                    public void run() {
139                        onSimCheckResponse(PhoneConstants.PIN_GENERAL_FAILURE, -1);
140                    }
141                });
142            }
143        }
144    }
145
146    private Dialog getSimUnlockProgressDialog() {
147        if (mSimUnlockProgressDialog == null) {
148            mSimUnlockProgressDialog = new ProgressDialog(mContext);
149            mSimUnlockProgressDialog.setMessage(
150                    mContext.getString(R.string.kg_sim_unlock_progress_dialog_message));
151            mSimUnlockProgressDialog.setIndeterminate(true);
152            mSimUnlockProgressDialog.setCancelable(false);
153            mSimUnlockProgressDialog.getWindow().setType(
154                    WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
155        }
156        return mSimUnlockProgressDialog;
157    }
158
159    private Dialog getSimRemainingAttemptsDialog(int remaining) {
160        String msg = getPinPasswordErrorMessage(remaining);
161        if (mRemainingAttemptsDialog == null) {
162            Builder builder = new AlertDialog.Builder(mContext);
163            builder.setMessage(msg);
164            builder.setCancelable(false);
165            builder.setNeutralButton(R.string.ok, null);
166            mRemainingAttemptsDialog = builder.create();
167            mRemainingAttemptsDialog.getWindow().setType(
168                    WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
169        } else {
170            mRemainingAttemptsDialog.setMessage(msg);
171        }
172        return mRemainingAttemptsDialog;
173    }
174
175    @Override
176    protected void verifyPasswordAndUnlock() {
177        String entry = mPasswordEntry.getText();
178
179        if (entry.length() < 4) {
180            // otherwise, display a message to the user, and don't submit.
181            mSecurityMessageDisplay.setMessage(R.string.kg_invalid_sim_pin_hint, true);
182            resetPasswordText(true);
183            mCallback.userActivity();
184            return;
185        }
186
187        getSimUnlockProgressDialog().show();
188
189        if (mCheckSimPinThread == null) {
190            mCheckSimPinThread = new CheckSimPin(mPasswordEntry.getText()) {
191                void onSimCheckResponse(final int result, final int attemptsRemaining) {
192                    post(new Runnable() {
193                        public void run() {
194                            if (mSimUnlockProgressDialog != null) {
195                                mSimUnlockProgressDialog.hide();
196                            }
197                            if (result == PhoneConstants.PIN_RESULT_SUCCESS) {
198                                KeyguardUpdateMonitor.getInstance(getContext()).reportSimUnlocked();
199                                mCallback.dismiss(true);
200                            } else {
201                                if (result == PhoneConstants.PIN_PASSWORD_INCORRECT) {
202                                    if (attemptsRemaining <= 2) {
203                                        // this is getting critical - show dialog
204                                        getSimRemainingAttemptsDialog(attemptsRemaining).show();
205                                    } else {
206                                        // show message
207                                        mSecurityMessageDisplay.setMessage(
208                                                getPinPasswordErrorMessage(attemptsRemaining), true);
209                                    }
210                                } else {
211                                    // "PIN operation failed!" - no idea what this was and no way to
212                                    // find out. :/
213                                    mSecurityMessageDisplay.setMessage(getContext().getString(
214                                            R.string.kg_password_pin_failed), true);
215                                }
216                                if (DEBUG) Log.d(LOG_TAG, "verifyPasswordAndUnlock "
217                                        + " CheckSimPin.onSimCheckResponse: " + result
218                                        + " attemptsRemaining=" + attemptsRemaining);
219                                resetPasswordText(true /* animate */);
220                            }
221                            mCallback.userActivity();
222                            mCheckSimPinThread = null;
223                        }
224                    });
225                }
226            };
227            mCheckSimPinThread.start();
228        }
229    }
230
231    @Override
232    public void startAppearAnimation() {
233        // noop.
234    }
235
236    @Override
237    public boolean startDisappearAnimation(Runnable finishRunnable) {
238        return false;
239    }
240}
241
242