• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 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.ActionBar;
20 import android.app.AlertDialog;
21 import android.content.DialogInterface;
22 import android.os.AsyncResult;
23 import android.os.Bundle;
24 import android.os.Handler;
25 import android.os.Message;
26 import android.util.Log;
27 import android.preference.PreferenceActivity;
28 import android.preference.PreferenceScreen;
29 import android.view.MenuItem;
30 import android.view.WindowManager;
31 import android.widget.Toast;
32 
33 import com.android.internal.telephony.CommandException;
34 import com.android.internal.telephony.Phone;
35 
36 /**
37  * FDN settings UI for the Phone app.
38  * Rewritten to look and behave closer to the other preferences.
39  */
40 public class FdnSetting extends PreferenceActivity
41         implements EditPinPreference.OnPinEnteredListener, DialogInterface.OnCancelListener {
42 
43     private static final String LOG_TAG = PhoneGlobals.LOG_TAG;
44     private static final boolean DBG = false;
45 
46     private Phone mPhone;
47 
48     /**
49      * Events we handle.
50      * The first is used for toggling FDN enable, the second for the PIN change.
51      */
52     private static final int EVENT_PIN2_ENTRY_COMPLETE = 100;
53     private static final int EVENT_PIN2_CHANGE_COMPLETE = 200;
54 
55     // String keys for preference lookup
56     // We only care about the pin preferences here, the manage FDN contacts
57     // Preference is handled solely in xml.
58     private static final String BUTTON_FDN_ENABLE_KEY = "button_fdn_enable_key";
59     private static final String BUTTON_CHANGE_PIN2_KEY = "button_change_pin2_key";
60 
61     private EditPinPreference mButtonEnableFDN;
62     private EditPinPreference mButtonChangePin2;
63 
64     // State variables
65     private String mOldPin;
66     private String mNewPin;
67     private String mPuk2;
68     private static final int PIN_CHANGE_OLD = 0;
69     private static final int PIN_CHANGE_NEW = 1;
70     private static final int PIN_CHANGE_REENTER = 2;
71     private static final int PIN_CHANGE_PUK = 3;
72     private static final int PIN_CHANGE_NEW_PIN_FOR_PUK = 4;
73     private static final int PIN_CHANGE_REENTER_PIN_FOR_PUK = 5;
74     private int mPinChangeState;
75     private boolean mIsPuk2Locked;    // Indicates we know that we are PUK2 blocked.
76 
77     private static final String SKIP_OLD_PIN_KEY = "skip_old_pin_key";
78     private static final String PIN_CHANGE_STATE_KEY = "pin_change_state_key";
79     private static final String OLD_PIN_KEY = "old_pin_key";
80     private static final String NEW_PIN_KEY = "new_pin_key";
81     private static final String DIALOG_MESSAGE_KEY = "dialog_message_key";
82     private static final String DIALOG_PIN_ENTRY_KEY = "dialog_pin_entry_key";
83 
84     // size limits for the pin.
85     private static final int MIN_PIN_LENGTH = 4;
86     private static final int MAX_PIN_LENGTH = 8;
87 
88     /**
89      * Delegate to the respective handlers.
90      */
91     @Override
onPinEntered(EditPinPreference preference, boolean positiveResult)92     public void onPinEntered(EditPinPreference preference, boolean positiveResult) {
93         if (preference == mButtonEnableFDN) {
94             toggleFDNEnable(positiveResult);
95         } else if (preference == mButtonChangePin2){
96             updatePINChangeState(positiveResult);
97         }
98     }
99 
100     /**
101      * Attempt to toggle FDN activation.
102      */
toggleFDNEnable(boolean positiveResult)103     private void toggleFDNEnable(boolean positiveResult) {
104         if (!positiveResult) {
105             return;
106         }
107 
108         // validate the pin first, before submitting it to the RIL for FDN enable.
109         String password = mButtonEnableFDN.getText();
110         if (validatePin (password, false)) {
111             // get the relevant data for the icc call
112             boolean isEnabled = mPhone.getIccCard().getIccFdnEnabled();
113             Message onComplete = mFDNHandler.obtainMessage(EVENT_PIN2_ENTRY_COMPLETE);
114 
115             // make fdn request
116             mPhone.getIccCard().setIccFdnEnabled(!isEnabled, password, onComplete);
117         } else {
118             // throw up error if the pin is invalid.
119             displayMessage(R.string.invalidPin2);
120         }
121 
122         mButtonEnableFDN.setText("");
123     }
124 
125     /**
126      * Attempt to change the pin.
127      */
updatePINChangeState(boolean positiveResult)128     private void updatePINChangeState(boolean positiveResult) {
129         if (DBG) log("updatePINChangeState positive=" + positiveResult
130                 + " mPinChangeState=" + mPinChangeState
131                 + " mSkipOldPin=" + mIsPuk2Locked);
132 
133         if (!positiveResult) {
134             // reset the state on cancel, either to expect PUK2 or PIN2
135             if (!mIsPuk2Locked) {
136                 resetPinChangeState();
137             } else {
138                 resetPinChangeStateForPUK2();
139             }
140             return;
141         }
142 
143         // Progress through the dialog states, generally in this order:
144         //   1. Enter old pin
145         //   2. Enter new pin
146         //   3. Re-Enter new pin
147         // While handling any error conditions that may show up in between.
148         // Also handle the PUK2 entry, if it is requested.
149         //
150         // In general, if any invalid entries are made, the dialog re-
151         // appears with text to indicate what the issue is.
152         switch (mPinChangeState) {
153             case PIN_CHANGE_OLD:
154                 mOldPin = mButtonChangePin2.getText();
155                 mButtonChangePin2.setText("");
156                 // if the pin is not valid, display a message and reset the state.
157                 if (validatePin (mOldPin, false)) {
158                     mPinChangeState = PIN_CHANGE_NEW;
159                     displayPinChangeDialog();
160                 } else {
161                     displayPinChangeDialog(R.string.invalidPin2, true);
162                 }
163                 break;
164             case PIN_CHANGE_NEW:
165                 mNewPin = mButtonChangePin2.getText();
166                 mButtonChangePin2.setText("");
167                 // if the new pin is not valid, display a message and reset the state.
168                 if (validatePin (mNewPin, false)) {
169                     mPinChangeState = PIN_CHANGE_REENTER;
170                     displayPinChangeDialog();
171                 } else {
172                     displayPinChangeDialog(R.string.invalidPin2, true);
173                 }
174                 break;
175             case PIN_CHANGE_REENTER:
176                 // if the re-entered pin is not valid, display a message and reset the state.
177                 if (!mNewPin.equals(mButtonChangePin2.getText())) {
178                     mPinChangeState = PIN_CHANGE_NEW;
179                     mButtonChangePin2.setText("");
180                     displayPinChangeDialog(R.string.mismatchPin2, true);
181                 } else {
182                     // If the PIN is valid, then we submit the change PIN request.
183                     mButtonChangePin2.setText("");
184                     Message onComplete = mFDNHandler.obtainMessage(
185                             EVENT_PIN2_CHANGE_COMPLETE);
186                     mPhone.getIccCard().changeIccFdnPassword(
187                             mOldPin, mNewPin, onComplete);
188                 }
189                 break;
190             case PIN_CHANGE_PUK: {
191                     // Doh! too many incorrect requests, PUK requested.
192                     mPuk2 = mButtonChangePin2.getText();
193                     mButtonChangePin2.setText("");
194                     // if the puk is not valid, display
195                     // a message and reset the state.
196                     if (validatePin (mPuk2, true)) {
197                         mPinChangeState = PIN_CHANGE_NEW_PIN_FOR_PUK;
198                         displayPinChangeDialog();
199                     } else {
200                         displayPinChangeDialog(R.string.invalidPuk2, true);
201                     }
202                 }
203                 break;
204             case PIN_CHANGE_NEW_PIN_FOR_PUK:
205                 mNewPin = mButtonChangePin2.getText();
206                 mButtonChangePin2.setText("");
207                 // if the new pin is not valid, display
208                 // a message and reset the state.
209                 if (validatePin (mNewPin, false)) {
210                     mPinChangeState = PIN_CHANGE_REENTER_PIN_FOR_PUK;
211                     displayPinChangeDialog();
212                 } else {
213                     displayPinChangeDialog(R.string.invalidPin2, true);
214                 }
215                 break;
216             case PIN_CHANGE_REENTER_PIN_FOR_PUK:
217                 // if the re-entered pin is not valid, display
218                 // a message and reset the state.
219                 if (!mNewPin.equals(mButtonChangePin2.getText())) {
220                     mPinChangeState = PIN_CHANGE_NEW_PIN_FOR_PUK;
221                     mButtonChangePin2.setText("");
222                     displayPinChangeDialog(R.string.mismatchPin2, true);
223                 } else {
224                     // Both puk2 and new pin2 are ready to submit
225                     mButtonChangePin2.setText("");
226                     Message onComplete = mFDNHandler.obtainMessage(
227                             EVENT_PIN2_CHANGE_COMPLETE);
228                     mPhone.getIccCard().supplyPuk2(mPuk2, mNewPin, onComplete);
229                 }
230                 break;
231         }
232     }
233 
234     /**
235      * Handler for asynchronous replies from the sim.
236      */
237     private final Handler mFDNHandler = new Handler() {
238         @Override
239         public void handleMessage(Message msg) {
240             switch (msg.what) {
241 
242                 // when we are enabling FDN, either we are unsuccessful and display
243                 // a toast, or just update the UI.
244                 case EVENT_PIN2_ENTRY_COMPLETE: {
245                         AsyncResult ar = (AsyncResult) msg.obj;
246                         if (ar.exception != null && ar.exception instanceof CommandException) {
247                             int attemptsRemaining = msg.arg1;
248                             // see if PUK2 is requested and alert the user accordingly.
249                             CommandException.Error e =
250                                     ((CommandException) ar.exception).getCommandError();
251                             switch (e) {
252                                 case SIM_PUK2:
253                                     // make sure we set the PUK2 state so that we can skip
254                                     // some redundant behaviour.
255                                     displayMessage(R.string.fdn_enable_puk2_requested,
256                                             attemptsRemaining);
257                                     resetPinChangeStateForPUK2();
258                                     break;
259                                 case PASSWORD_INCORRECT:
260                                     displayMessage(R.string.pin2_invalid, attemptsRemaining);
261                                     break;
262                                 default:
263                                     displayMessage(R.string.fdn_failed, attemptsRemaining);
264                                     break;
265                             }
266                         }
267                         updateEnableFDN();
268                     }
269                     break;
270 
271                 // when changing the pin we need to pay attention to whether or not
272                 // the error requests a PUK (usually after too many incorrect tries)
273                 // Set the state accordingly.
274                 case EVENT_PIN2_CHANGE_COMPLETE: {
275                         if (DBG)
276                             log("Handle EVENT_PIN2_CHANGE_COMPLETE");
277                         AsyncResult ar = (AsyncResult) msg.obj;
278                         if (ar.exception != null) {
279                             int attemptsRemaining = msg.arg1;
280                             log("Handle EVENT_PIN2_CHANGE_COMPLETE attemptsRemaining="
281                                     + attemptsRemaining);
282                             CommandException ce = (CommandException) ar.exception;
283                             if (ce.getCommandError() == CommandException.Error.SIM_PUK2) {
284                                 // throw an alert dialog on the screen, displaying the
285                                 // request for a PUK2.  set the cancel listener to
286                                 // FdnSetting.onCancel().
287                                 AlertDialog a = new AlertDialog.Builder(FdnSetting.this)
288                                     .setMessage(R.string.puk2_requested)
289                                     .setCancelable(true)
290                                     .setOnCancelListener(FdnSetting.this)
291                                     .create();
292                                 a.getWindow().addFlags(
293                                         WindowManager.LayoutParams.FLAG_DIM_BEHIND);
294                                 a.show();
295                             } else {
296                                 // set the correct error message depending upon the state.
297                                 // Reset the state depending upon or knowledge of the PUK state.
298                                 if (!mIsPuk2Locked) {
299                                     displayMessage(R.string.badPin2, attemptsRemaining);
300                                     resetPinChangeState();
301                                 } else {
302                                     displayMessage(R.string.badPuk2, attemptsRemaining);
303                                     resetPinChangeStateForPUK2();
304                                 }
305                             }
306                         } else {
307                             if (mPinChangeState == PIN_CHANGE_PUK) {
308                                 displayMessage(R.string.pin2_unblocked);
309                             } else {
310                                 displayMessage(R.string.pin2_changed);
311                             }
312 
313                             // reset to normal behaviour on successful change.
314                             resetPinChangeState();
315                         }
316                     }
317                     break;
318             }
319         }
320     };
321 
322     /**
323      * Cancel listener for the PUK2 request alert dialog.
324      */
325     @Override
onCancel(DialogInterface dialog)326     public void onCancel(DialogInterface dialog) {
327         // set the state of the preference and then display the dialog.
328         resetPinChangeStateForPUK2();
329         displayPinChangeDialog(0, true);
330     }
331 
332     /**
333      * Display a toast for message, like the rest of the settings.
334      */
displayMessage(int strId, int attemptsRemaining)335     private final void displayMessage(int strId, int attemptsRemaining) {
336         String s = getString(strId);
337         if ((strId == R.string.badPin2) || (strId == R.string.badPuk2) ||
338                 (strId == R.string.pin2_invalid)) {
339             if (attemptsRemaining >= 0) {
340                 s = getString(strId) + getString(R.string.pin2_attempts, attemptsRemaining);
341             } else {
342                 s = getString(strId);
343             }
344         }
345         log("displayMessage: attemptsRemaining=" + attemptsRemaining + " s=" + s);
346         Toast.makeText(this, s, Toast.LENGTH_SHORT).show();
347     }
348 
displayMessage(int strId)349     private final void displayMessage(int strId) {
350         displayMessage(strId, -1);
351     }
352 
353     /**
354      * The next two functions are for updating the message field on the dialog.
355      */
displayPinChangeDialog()356     private final void displayPinChangeDialog() {
357         displayPinChangeDialog(0, true);
358     }
359 
displayPinChangeDialog(int strId, boolean shouldDisplay)360     private final void displayPinChangeDialog(int strId, boolean shouldDisplay) {
361         int msgId;
362         switch (mPinChangeState) {
363             case PIN_CHANGE_OLD:
364                 msgId = R.string.oldPin2Label;
365                 break;
366             case PIN_CHANGE_NEW:
367             case PIN_CHANGE_NEW_PIN_FOR_PUK:
368                 msgId = R.string.newPin2Label;
369                 break;
370             case PIN_CHANGE_REENTER:
371             case PIN_CHANGE_REENTER_PIN_FOR_PUK:
372                 msgId = R.string.confirmPin2Label;
373                 break;
374             case PIN_CHANGE_PUK:
375             default:
376                 msgId = R.string.label_puk2_code;
377                 break;
378         }
379 
380         // append the note / additional message, if needed.
381         if (strId != 0) {
382             mButtonChangePin2.setDialogMessage(getText(msgId) + "\n" + getText(strId));
383         } else {
384             mButtonChangePin2.setDialogMessage(msgId);
385         }
386 
387         // only display if requested.
388         if (shouldDisplay) {
389             mButtonChangePin2.showPinDialog();
390         }
391     }
392 
393     /**
394      * Reset the state of the pin change dialog.
395      */
resetPinChangeState()396     private final void resetPinChangeState() {
397         if (DBG) log("resetPinChangeState");
398         mPinChangeState = PIN_CHANGE_OLD;
399         displayPinChangeDialog(0, false);
400         mOldPin = mNewPin = "";
401         mIsPuk2Locked = false;
402     }
403 
404     /**
405      * Reset the state of the pin change dialog solely for PUK2 use.
406      */
resetPinChangeStateForPUK2()407     private final void resetPinChangeStateForPUK2() {
408         if (DBG) log("resetPinChangeStateForPUK2");
409         mPinChangeState = PIN_CHANGE_PUK;
410         displayPinChangeDialog(0, false);
411         mOldPin = mNewPin = mPuk2 = "";
412         mIsPuk2Locked = true;
413     }
414 
415     /**
416      * Validate the pin entry.
417      *
418      * @param pin This is the pin to validate
419      * @param isPuk Boolean indicating whether we are to treat
420      * the pin input as a puk.
421      */
validatePin(String pin, boolean isPuk)422     private boolean validatePin(String pin, boolean isPuk) {
423 
424         // for pin, we have 4-8 numbers, or puk, we use only 8.
425         int pinMinimum = isPuk ? MAX_PIN_LENGTH : MIN_PIN_LENGTH;
426 
427         // check validity
428         if (pin == null || pin.length() < pinMinimum || pin.length() > MAX_PIN_LENGTH) {
429             return false;
430         } else {
431             return true;
432         }
433     }
434 
435     /**
436      * Reflect the updated FDN state in the UI.
437      */
updateEnableFDN()438     private void updateEnableFDN() {
439         if (mPhone.getIccCard().getIccFdnEnabled()) {
440             mButtonEnableFDN.setTitle(R.string.enable_fdn_ok);
441             mButtonEnableFDN.setSummary(R.string.fdn_enabled);
442             mButtonEnableFDN.setDialogTitle(R.string.disable_fdn);
443         } else {
444             mButtonEnableFDN.setTitle(R.string.disable_fdn_ok);
445             mButtonEnableFDN.setSummary(R.string.fdn_disabled);
446             mButtonEnableFDN.setDialogTitle(R.string.enable_fdn);
447         }
448     }
449 
450     @Override
onCreate(Bundle icicle)451     protected void onCreate(Bundle icicle) {
452         super.onCreate(icicle);
453 
454         addPreferencesFromResource(R.xml.fdn_setting);
455 
456         mPhone = PhoneGlobals.getPhone();
457 
458         //get UI object references
459         PreferenceScreen prefSet = getPreferenceScreen();
460         mButtonEnableFDN = (EditPinPreference) prefSet.findPreference(BUTTON_FDN_ENABLE_KEY);
461         mButtonChangePin2 = (EditPinPreference) prefSet.findPreference(BUTTON_CHANGE_PIN2_KEY);
462 
463         //assign click listener and update state
464         mButtonEnableFDN.setOnPinEnteredListener(this);
465         updateEnableFDN();
466 
467         mButtonChangePin2.setOnPinEnteredListener(this);
468 
469         // Only reset the pin change dialog if we're not in the middle of changing it.
470         if (icicle == null) {
471             resetPinChangeState();
472         } else {
473             mIsPuk2Locked = icicle.getBoolean(SKIP_OLD_PIN_KEY);
474             mPinChangeState = icicle.getInt(PIN_CHANGE_STATE_KEY);
475             mOldPin = icicle.getString(OLD_PIN_KEY);
476             mNewPin = icicle.getString(NEW_PIN_KEY);
477             mButtonChangePin2.setDialogMessage(icicle.getString(DIALOG_MESSAGE_KEY));
478             mButtonChangePin2.setText(icicle.getString(DIALOG_PIN_ENTRY_KEY));
479         }
480 
481         ActionBar actionBar = getActionBar();
482         if (actionBar != null) {
483             // android.R.id.home will be triggered in onOptionsItemSelected()
484             actionBar.setDisplayHomeAsUpEnabled(true);
485         }
486     }
487 
488     @Override
onResume()489     protected void onResume() {
490         super.onResume();
491         mPhone = PhoneGlobals.getPhone();
492         updateEnableFDN();
493     }
494 
495     /**
496      * Save the state of the pin change.
497      */
498     @Override
onSaveInstanceState(Bundle out)499     protected void onSaveInstanceState(Bundle out) {
500         super.onSaveInstanceState(out);
501         out.putBoolean(SKIP_OLD_PIN_KEY, mIsPuk2Locked);
502         out.putInt(PIN_CHANGE_STATE_KEY, mPinChangeState);
503         out.putString(OLD_PIN_KEY, mOldPin);
504         out.putString(NEW_PIN_KEY, mNewPin);
505         out.putString(DIALOG_MESSAGE_KEY, mButtonChangePin2.getDialogMessage().toString());
506         out.putString(DIALOG_PIN_ENTRY_KEY, mButtonChangePin2.getText());
507     }
508 
509     @Override
onOptionsItemSelected(MenuItem item)510     public boolean onOptionsItemSelected(MenuItem item) {
511         final int itemId = item.getItemId();
512         if (itemId == android.R.id.home) {  // See ActionBar#setDisplayHomeAsUpEnabled()
513             CallFeaturesSetting.goUpToTopLevelSetting(this);
514             return true;
515         }
516         return super.onOptionsItemSelected(item);
517     }
518 
log(String msg)519     private void log(String msg) {
520         Log.d(LOG_TAG, "FdnSetting: " + msg);
521     }
522 }
523 
524