1 /*
2  * Copyright (C) 2006 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.phone;
18 
19 import android.app.Activity;
20 import android.app.AlertDialog;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.res.Resources;
24 import android.os.AsyncResult;
25 import android.os.Bundle;
26 import android.os.Handler;
27 import android.os.Message;
28 import android.os.UserManager;
29 import android.text.method.DigitsKeyListener;
30 import android.util.Log;
31 import android.view.View;
32 import android.widget.Button;
33 import android.widget.EditText;
34 import android.widget.LinearLayout;
35 import android.widget.ScrollView;
36 import android.widget.TextView;
37 import android.widget.Toast;
38 
39 import com.android.internal.telephony.CommandException;
40 import com.android.internal.telephony.IccCard;
41 import com.android.internal.telephony.Phone;
42 
43 /**
44  * "Change ICC PIN" UI for the Phone app.
45  */
46 public class ChangeIccPinScreen extends Activity {
47     private static final String LOG_TAG = PhoneGlobals.LOG_TAG;
48     private static final boolean DBG = false;
49 
50     private static final int EVENT_PIN_CHANGED = 100;
51 
52     private enum EntryState {
53         ES_PIN,
54         ES_PUK
55     }
56 
57     private EntryState mState;
58 
59     private static final int NO_ERROR = 0;
60     private static final int PIN_MISMATCH = 1;
61     private static final int PIN_INVALID_LENGTH = 2;
62 
63     private static final int MIN_PIN_LENGTH = 4;
64     private static final int MAX_PIN_LENGTH = 8;
65 
66     private UserManager mUserManager;
67     private boolean mDisallowedConfig;
68     private Phone mPhone;
69     private boolean mChangePin2;
70     private TextView mBadPinError;
71     private TextView mMismatchError;
72     private EditText mOldPin;
73     private EditText mNewPin1;
74     private EditText mNewPin2;
75     private EditText mPUKCode;
76     private Button mButton;
77     private Button mPUKSubmit;
78     private ScrollView mScrollView;
79 
80     private LinearLayout mIccPUKPanel;
81 
82     private Handler mHandler = new Handler() {
83         public void handleMessage(Message msg) {
84             switch (msg.what) {
85                 case EVENT_PIN_CHANGED:
86                     AsyncResult ar = (AsyncResult) msg.obj;
87                     handleResult(ar);
88                     break;
89             }
90 
91             return;
92         }
93     };
94 
onCreate(Bundle icicle)95     public void onCreate(Bundle icicle) {
96         super.onCreate(icicle);
97 
98         mUserManager = this.getSystemService(UserManager.class);
99         if (mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS)) {
100             mDisallowedConfig = true;
101         }
102 
103         mPhone = PhoneGlobals.getPhone();
104 
105         resolveIntent();
106 
107         setContentView(R.layout.change_sim_pin_screen);
108         setupView();
109 
110         mState = EntryState.ES_PIN;
111     }
112 
setupView()113     private void setupView() {
114         mOldPin = (EditText) findViewById(R.id.old_pin);
115         mNewPin1 = (EditText) findViewById(R.id.new_pin1);
116         mNewPin2 = (EditText) findViewById(R.id.new_pin2);
117         mBadPinError = (TextView) findViewById(R.id.bad_pin);
118         mMismatchError = (TextView) findViewById(R.id.mismatch);
119         mButton = (Button) findViewById(R.id.button);
120         mScrollView = (ScrollView) findViewById(R.id.scroll);
121         mPUKCode = (EditText) findViewById(R.id.puk_code);
122         mPUKSubmit = (Button) findViewById(R.id.puk_submit);
123         mIccPUKPanel = (LinearLayout) findViewById(R.id.puk_panel);
124         int id = mChangePin2 ? R.string.change_pin2 : R.string.change_pin;
125         setTitle(getResources().getText(id));
126 
127         if (mDisallowedConfig) {
128             mOldPin.setEnabled(false);
129             mOldPin.setAlpha(.5f);
130 
131             mNewPin1.setEnabled(false);
132             mNewPin1.setAlpha(.5f);
133 
134             mNewPin2.setEnabled(false);
135             mNewPin2.setAlpha(.5f);
136 
137             mButton.setEnabled(false);
138             mButton.setAlpha(.5f);
139 
140             mPUKCode.setEnabled(false);
141             mPUKCode.setAlpha(.5f);
142 
143             mPUKSubmit.setEnabled(false);
144             mPUKSubmit.setAlpha(.5f);
145         } else {
146             mOldPin.setKeyListener(DigitsKeyListener.getInstance());
147             mOldPin.setMovementMethod(null);
148             mOldPin.setOnClickListener(mClicked);
149 
150             mNewPin1.setKeyListener(DigitsKeyListener.getInstance());
151             mNewPin1.setMovementMethod(null);
152             mNewPin1.setOnClickListener(mClicked);
153 
154             mNewPin2.setKeyListener(DigitsKeyListener.getInstance());
155             mNewPin2.setMovementMethod(null);
156             mNewPin2.setOnClickListener(mClicked);
157 
158             mButton.setOnClickListener(mClicked);
159 
160             mPUKCode.setKeyListener(DigitsKeyListener.getInstance());
161             mPUKCode.setMovementMethod(null);
162             mPUKCode.setOnClickListener(mClicked);
163 
164             mPUKSubmit.setOnClickListener(mClicked);
165         }
166     }
167 
resolveIntent()168     private void resolveIntent() {
169         Intent intent = getIntent();
170         mChangePin2 = intent.getBooleanExtra("pin2", mChangePin2);
171     }
172 
reset()173     private void reset() {
174         mScrollView.scrollTo(0, 0);
175         mBadPinError.setVisibility(View.GONE);
176         mMismatchError.setVisibility(View.GONE);
177     }
178 
validateNewPin(String p1, String p2)179     private int validateNewPin(String p1, String p2) {
180         if (p1 == null) {
181             return PIN_INVALID_LENGTH;
182         }
183 
184         if (!p1.equals(p2)) {
185             return PIN_MISMATCH;
186         }
187 
188         int len1 = p1.length();
189 
190         if (len1 < MIN_PIN_LENGTH || len1 > MAX_PIN_LENGTH) {
191             return PIN_INVALID_LENGTH;
192         }
193 
194         return NO_ERROR;
195     }
196 
197     private View.OnClickListener mClicked = new View.OnClickListener() {
198         public void onClick(View v) {
199             if (v == mOldPin) {
200                 mNewPin1.requestFocus();
201             } else if (v == mNewPin1) {
202                 mNewPin2.requestFocus();
203             } else if (v == mNewPin2) {
204                 mButton.requestFocus();
205             } else if (v == mButton) {
206                 IccCard iccCardInterface = mPhone.getIccCard();
207                 if (iccCardInterface != null) {
208                     String oldPin = mOldPin.getText().toString();
209                     String newPin1 = mNewPin1.getText().toString();
210                     String newPin2 = mNewPin2.getText().toString();
211 
212                     int error = validateNewPin(newPin1, newPin2);
213 
214                     switch (error) {
215                         case PIN_INVALID_LENGTH:
216                         case PIN_MISMATCH:
217                             mNewPin1.getText().clear();
218                             mNewPin2.getText().clear();
219                             mMismatchError.setVisibility(View.VISIBLE);
220 
221                             Resources r = getResources();
222                             CharSequence text;
223 
224                             if (error == PIN_MISMATCH) {
225                                 text = r.getString(R.string.mismatchPin);
226                             } else {
227                                 text = r.getString(R.string.invalidPin);
228                             }
229 
230                             mMismatchError.setText(text);
231                             break;
232 
233                         default:
234                             Message callBack = Message.obtain(mHandler,
235                                     EVENT_PIN_CHANGED);
236 
237                             if (DBG) log("change pin attempt: old=" + oldPin +
238                                     ", newPin=" + newPin1);
239 
240                             reset();
241 
242                             if (mChangePin2) {
243                                 iccCardInterface.changeIccFdnPassword(oldPin,
244                                         newPin1, callBack);
245                             } else {
246                                 iccCardInterface.changeIccLockPassword(oldPin,
247                                         newPin1, callBack);
248                             }
249 
250                             // TODO: show progress panel
251                     }
252                 }
253             } else if (v == mPUKCode) {
254                 mPUKSubmit.requestFocus();
255             } else if (v == mPUKSubmit) {
256                 mPhone.getIccCard().supplyPuk2(mPUKCode.getText().toString(),
257                         mNewPin1.getText().toString(),
258                         Message.obtain(mHandler, EVENT_PIN_CHANGED));
259             }
260         }
261     };
262 
handleResult(AsyncResult ar)263     private void handleResult(AsyncResult ar) {
264         if (ar.exception == null) {
265             if (DBG) log("handleResult: success!");
266 
267             if (mState == EntryState.ES_PUK) {
268                 mScrollView.setVisibility(View.VISIBLE);
269                 mIccPUKPanel.setVisibility(View.GONE);
270             }
271             // TODO: show success feedback
272             showConfirmation();
273 
274             mHandler.postDelayed(new Runnable() {
275                 public void run() {
276                     finish();
277                 }
278             }, 3000);
279 
280         } else if (ar.exception instanceof CommandException
281            /*  && ((CommandException)ar.exception).getCommandError() ==
282            CommandException.Error.PASSWORD_INCORRECT */ ) {
283             if (mState == EntryState.ES_PIN) {
284                 if (DBG) log("handleResult: pin failed!");
285                 mOldPin.getText().clear();
286                 mBadPinError.setVisibility(View.VISIBLE);
287                 CommandException ce = (CommandException) ar.exception;
288                 if (ce.getCommandError() == CommandException.Error.SIM_PUK2) {
289                     if (DBG) log("handleResult: puk requested!");
290                     mState = EntryState.ES_PUK;
291                     displayPUKAlert();
292                     mScrollView.setVisibility(View.GONE);
293                     mIccPUKPanel.setVisibility(View.VISIBLE);
294                     mPUKCode.requestFocus();
295                 }
296             } else if (mState == EntryState.ES_PUK) {
297                 //should really check to see if the error is CommandException.PASSWORD_INCORRECT...
298                 if (DBG) log("handleResult: puk2 failed!");
299                 displayPUKAlert();
300                 mPUKCode.getText().clear();
301                 mPUKCode.requestFocus();
302             }
303         }
304     }
305 
306     private AlertDialog mPUKAlert;
displayPUKAlert()307     private void displayPUKAlert () {
308         if (mPUKAlert == null) {
309             mPUKAlert = FrameworksUtils.makeAlertDialogBuilder(this)
310             .setMessage (R.string.puk_requested)
311             .setCancelable(false)
312             .show();
313         } else {
314             mPUKAlert.show();
315         }
316         //TODO: The 3 second delay here is somewhat arbitrary, reflecting the values
317         //used elsewhere for similar code.  This should get revisited with the framework
318         //crew to see if there is some standard we should adhere to.
319         mHandler.postDelayed(new Runnable() {
320             public void run() {
321                 mPUKAlert.dismiss();
322             }
323         }, 3000);
324     }
325 
showConfirmation()326     private void showConfirmation() {
327         int id = mChangePin2 ? R.string.pin2_changed : R.string.pin_changed;
328         Toast.makeText(this, id, Toast.LENGTH_SHORT).show();
329     }
330 
log(String msg)331     private void log(String msg) {
332         String prefix = mChangePin2 ? "[ChgPin2]" : "[ChgPin]";
333         Log.d(LOG_TAG, prefix + msg);
334     }
335 }
336