KeyguardSimPukView.java revision 7d5e00ab2b5134d11e40b5a84fa363f8e8b24d68
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;
20
21import android.content.Context;
22import android.app.Activity;
23import android.app.Dialog;
24import android.app.ProgressDialog;
25import android.os.RemoteException;
26import android.os.ServiceManager;
27import android.text.Editable;
28import android.text.InputType;
29import android.text.TextWatcher;
30import android.text.method.DigitsKeyListener;
31import android.util.AttributeSet;
32import android.view.View;
33import android.view.WindowManager;
34import android.widget.TextView.OnEditorActionListener;
35
36/**
37 * Displays a PIN pad for entering a PUK (Pin Unlock Kode) provided by a carrier.
38 */
39public class KeyguardSimPukView extends KeyguardAbsKeyInputView
40        implements KeyguardSecurityView, OnEditorActionListener, TextWatcher {
41
42    private ProgressDialog mSimUnlockProgressDialog = null;
43    private volatile boolean mCheckInProgress;
44    private String mPukText;
45    private String mPinText;
46    private StateMachine mStateMachine = new StateMachine();
47
48    private class StateMachine {
49        final int ENTER_PUK = 0;
50        final int ENTER_PIN = 1;
51        final int CONFIRM_PIN = 2;
52        final int DONE = 3;
53        private int state = ENTER_PUK;
54
55        public void next() {
56            int msg = 0;
57            if (state == ENTER_PUK) {
58                if (checkPuk()) {
59                    state = ENTER_PIN;
60                    msg = R.string.kg_puk_enter_pin_hint;
61                } else {
62                    msg = R.string.kg_invalid_sim_puk_hint;
63                }
64            } else if (state == ENTER_PIN) {
65                if (checkPin()) {
66                    state = CONFIRM_PIN;
67                    msg = R.string.kg_enter_confirm_pin_hint;
68                } else {
69                    msg = R.string.kg_invalid_sim_pin_hint;
70                }
71            } else if (state == CONFIRM_PIN) {
72                if (confirmPin()) {
73                    state = DONE;
74                    msg = R.string.keyguard_sim_unlock_progress_dialog_message;
75                    updateSim();
76                } else {
77                    state = ENTER_PIN; // try again?
78                    msg = R.string.kg_invalid_confirm_pin_hint;
79                }
80            }
81            mPasswordEntry.setText(null);
82            if (msg != 0) {
83                mSecurityMessageDisplay.setMessage(msg, true);
84            }
85        }
86
87        void reset() {
88            mPinText="";
89            mPukText="";
90            state = ENTER_PUK;
91            mSecurityMessageDisplay.setMessage(R.string.kg_puk_enter_puk_hint, true);
92            mPasswordEntry.requestFocus();
93        }
94    }
95
96    public KeyguardSimPukView(Context context) {
97        this(context, null);
98    }
99
100    public KeyguardSimPukView(Context context, AttributeSet attrs) {
101        super(context, attrs);
102    }
103
104    public void resetState() {
105        mStateMachine.reset();
106        mPasswordEntry.setEnabled(true);
107    }
108
109    @Override
110    protected boolean shouldLockout(long deadline) {
111        // SIM PUK doesn't have a timed lockout
112        return false;
113    }
114
115    @Override
116    protected int getPasswordTextViewId() {
117        return R.id.pinEntry;
118    }
119
120    @Override
121    protected void onFinishInflate() {
122        super.onFinishInflate();
123
124        final View ok = findViewById(R.id.key_enter);
125        if (ok != null) {
126            ok.setOnClickListener(new View.OnClickListener() {
127                @Override
128                public void onClick(View v) {
129                    doHapticKeyClick();
130                    verifyPasswordAndUnlock();
131                }
132            });
133        }
134
135        // The delete button is of the PIN keyboard itself in some (e.g. tablet) layouts,
136        // not a separate view
137        View pinDelete = findViewById(R.id.delete_button);
138        if (pinDelete != null) {
139            pinDelete.setVisibility(View.VISIBLE);
140            pinDelete.setOnClickListener(new OnClickListener() {
141                public void onClick(View v) {
142                    CharSequence str = mPasswordEntry.getText();
143                    if (str.length() > 0) {
144                        mPasswordEntry.setText(str.subSequence(0, str.length()-1));
145                    }
146                    doHapticKeyClick();
147                }
148            });
149            pinDelete.setOnLongClickListener(new View.OnLongClickListener() {
150                public boolean onLongClick(View v) {
151                    mPasswordEntry.setText("");
152                    doHapticKeyClick();
153                    return true;
154                }
155            });
156        }
157
158        mPasswordEntry.setKeyListener(DigitsKeyListener.getInstance());
159        mPasswordEntry.setInputType(InputType.TYPE_CLASS_NUMBER
160                | InputType.TYPE_NUMBER_VARIATION_PASSWORD);
161
162        mPasswordEntry.requestFocus();
163
164        mSecurityMessageDisplay.setTimeout(0); // don't show ownerinfo/charging status by default
165    }
166
167    @Override
168    public void showUsabilityHint() {
169    }
170
171    @Override
172    public void onPause() {
173        // dismiss the dialog.
174        if (mSimUnlockProgressDialog != null) {
175            mSimUnlockProgressDialog.dismiss();
176            mSimUnlockProgressDialog = null;
177        }
178    }
179
180    /**
181     * Since the IPC can block, we want to run the request in a separate thread
182     * with a callback.
183     */
184    private abstract class CheckSimPuk extends Thread {
185
186        private final String mPin, mPuk;
187
188        protected CheckSimPuk(String puk, String pin) {
189            mPuk = puk;
190            mPin = pin;
191        }
192
193        abstract void onSimLockChangedResponse(boolean success);
194
195        @Override
196        public void run() {
197            try {
198                final boolean result = ITelephony.Stub.asInterface(ServiceManager
199                        .checkService("phone")).supplyPuk(mPuk, mPin);
200
201                post(new Runnable() {
202                    public void run() {
203                        onSimLockChangedResponse(result);
204                    }
205                });
206            } catch (RemoteException e) {
207                post(new Runnable() {
208                    public void run() {
209                        onSimLockChangedResponse(false);
210                    }
211                });
212            }
213        }
214    }
215
216    private Dialog getSimUnlockProgressDialog() {
217        if (mSimUnlockProgressDialog == null) {
218            mSimUnlockProgressDialog = new ProgressDialog(mContext);
219            mSimUnlockProgressDialog.setMessage(
220                    mContext.getString(R.string.kg_sim_unlock_progress_dialog_message));
221            mSimUnlockProgressDialog.setIndeterminate(true);
222            mSimUnlockProgressDialog.setCancelable(false);
223            if (!(mContext instanceof Activity)) {
224                mSimUnlockProgressDialog.getWindow().setType(
225                        WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
226            }
227        }
228        return mSimUnlockProgressDialog;
229    }
230
231    private boolean checkPuk() {
232        // make sure the puk is at least 8 digits long.
233        if (mPasswordEntry.getText().length() >= 8) {
234            mPukText = mPasswordEntry.getText().toString();
235            return true;
236        }
237        return false;
238    }
239
240    private boolean checkPin() {
241        // make sure the PIN is between 4 and 8 digits
242        int length = mPasswordEntry.getText().length();
243        if (length >= 4 && length <= 8) {
244            mPinText = mPasswordEntry.getText().toString();
245            return true;
246        }
247        return false;
248    }
249
250    public boolean confirmPin() {
251        return mPinText.equals(mPasswordEntry.getText().toString());
252    }
253
254    private void updateSim() {
255        getSimUnlockProgressDialog().show();
256
257        if (!mCheckInProgress) {
258            mCheckInProgress = true;
259            new CheckSimPuk(mPukText, mPinText) {
260                void onSimLockChangedResponse(final boolean success) {
261                    post(new Runnable() {
262                        public void run() {
263                            if (mSimUnlockProgressDialog != null) {
264                                mSimUnlockProgressDialog.hide();
265                            }
266                            if (success) {
267                                mCallback.dismiss(true);
268                            } else {
269                                mStateMachine.reset();
270                                mSecurityMessageDisplay.setMessage(R.string.kg_invalid_puk, true);
271                            }
272                            mCheckInProgress = false;
273                        }
274                    });
275                }
276            }.start();
277        }
278    }
279
280    @Override
281    protected void verifyPasswordAndUnlock() {
282        mStateMachine.next();
283    }
284}
285
286
287