OSDN Git Service

Import translations. DO NOT MERGE
[android-x86/packages-apps-Settings.git] / src / com / android / settings / IccLockSettings.java
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.settings;
18
19 import android.content.BroadcastReceiver;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.IntentFilter;
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.support.v14.preference.SwitchPreference;
29 import android.support.v7.preference.Preference;
30 import android.support.v7.preference.PreferenceScreen;
31 import android.telephony.SubscriptionInfo;
32 import android.telephony.SubscriptionManager;
33 import android.telephony.TelephonyManager;
34 import android.util.Log;
35 import android.view.View;
36 import android.widget.ListView;
37 import android.widget.TabHost;
38 import android.widget.TabHost.OnTabChangeListener;
39 import android.widget.TabHost.TabContentFactory;
40 import android.widget.TabHost.TabSpec;
41 import android.widget.TabWidget;
42 import android.widget.Toast;
43
44 import com.android.internal.logging.MetricsProto.MetricsEvent;
45 import com.android.internal.telephony.Phone;
46 import com.android.internal.telephony.PhoneFactory;
47 import com.android.internal.telephony.TelephonyIntents;
48
49 /**
50  * Implements the preference screen to enable/disable ICC lock and
51  * also the dialogs to change the ICC PIN. In the former case, enabling/disabling
52  * the ICC lock will prompt the user for the current PIN.
53  * In the Change PIN case, it prompts the user for old pin, new pin and new pin
54  * again before attempting to change it. Calls the SimCard interface to execute
55  * these operations.
56  *
57  */
58 public class IccLockSettings extends InstrumentedPreferenceActivity
59         implements EditPinPreference.OnPinEnteredListener {
60     private static final String TAG = "IccLockSettings";
61     private static final boolean DBG = true;
62
63     private static final int OFF_MODE = 0;
64     // State when enabling/disabling ICC lock
65     private static final int ICC_LOCK_MODE = 1;
66     // State when entering the old pin
67     private static final int ICC_OLD_MODE = 2;
68     // State when entering the new pin - first time
69     private static final int ICC_NEW_MODE = 3;
70     // State when entering the new pin - second time
71     private static final int ICC_REENTER_MODE = 4;
72
73     // Keys in xml file
74     private static final String PIN_DIALOG = "sim_pin";
75     private static final String PIN_TOGGLE = "sim_toggle";
76     // Keys in icicle
77     private static final String DIALOG_STATE = "dialogState";
78     private static final String DIALOG_PIN = "dialogPin";
79     private static final String DIALOG_ERROR = "dialogError";
80     private static final String ENABLE_TO_STATE = "enableState";
81
82     // Save and restore inputted PIN code when configuration changed
83     // (ex. portrait<-->landscape) during change PIN code
84     private static final String OLD_PINCODE = "oldPinCode";
85     private static final String NEW_PINCODE = "newPinCode";
86
87     private static final int MIN_PIN_LENGTH = 4;
88     private static final int MAX_PIN_LENGTH = 8;
89     // Which dialog to show next when popped up
90     private int mDialogState = OFF_MODE;
91
92     private String mPin;
93     private String mOldPin;
94     private String mNewPin;
95     private String mError;
96     // Are we trying to enable or disable ICC lock?
97     private boolean mToState;
98
99     private TabHost mTabHost;
100     private TabWidget mTabWidget;
101     private ListView mListView;
102
103     private Phone mPhone;
104
105     private EditPinPreference mPinDialog;
106     private SwitchPreference mPinToggle;
107
108     private Resources mRes;
109
110     // For async handler to identify request type
111     private static final int MSG_ENABLE_ICC_PIN_COMPLETE = 100;
112     private static final int MSG_CHANGE_ICC_PIN_COMPLETE = 101;
113     private static final int MSG_SIM_STATE_CHANGED = 102;
114
115     // For replies from IccCard interface
116     private Handler mHandler = new Handler() {
117         public void handleMessage(Message msg) {
118             AsyncResult ar = (AsyncResult) msg.obj;
119             switch (msg.what) {
120                 case MSG_ENABLE_ICC_PIN_COMPLETE:
121                     iccLockChanged(ar.exception == null, msg.arg1);
122                     break;
123                 case MSG_CHANGE_ICC_PIN_COMPLETE:
124                     iccPinChanged(ar.exception == null, msg.arg1);
125                     break;
126                 case MSG_SIM_STATE_CHANGED:
127                     updatePreferences();
128                     break;
129             }
130
131             return;
132         }
133     };
134
135     private final BroadcastReceiver mSimStateReceiver = new BroadcastReceiver() {
136         public void onReceive(Context context, Intent intent) {
137             final String action = intent.getAction();
138             if (TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(action)) {
139                 mHandler.sendMessage(mHandler.obtainMessage(MSG_SIM_STATE_CHANGED));
140             }
141         }
142     };
143
144     // For top-level settings screen to query
145     static boolean isIccLockEnabled() {
146         return PhoneFactory.getDefaultPhone().getIccCard().getIccLockEnabled();
147     }
148
149     static String getSummary(Context context) {
150         Resources res = context.getResources();
151         String summary = isIccLockEnabled()
152                 ? res.getString(R.string.sim_lock_on)
153                 : res.getString(R.string.sim_lock_off);
154         return summary;
155     }
156
157     @Override
158     protected void onCreate(Bundle savedInstanceState) {
159         super.onCreate(savedInstanceState);
160         final Context context = getApplicationContext();
161         final TelephonyManager tm =
162                 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
163         final int numSims = tm.getSimCount();
164
165         if (Utils.isMonkeyRunning()) {
166             finish();
167             return;
168         }
169
170         addPreferencesFromResource(R.xml.sim_lock_settings);
171
172         mPinDialog = (EditPinPreference) findPreference(PIN_DIALOG);
173         mPinToggle = (SwitchPreference) findPreference(PIN_TOGGLE);
174         if (savedInstanceState != null && savedInstanceState.containsKey(DIALOG_STATE)) {
175             mDialogState = savedInstanceState.getInt(DIALOG_STATE);
176             mPin = savedInstanceState.getString(DIALOG_PIN);
177             mError = savedInstanceState.getString(DIALOG_ERROR);
178             mToState = savedInstanceState.getBoolean(ENABLE_TO_STATE);
179
180             // Restore inputted PIN code
181             switch (mDialogState) {
182                 case ICC_NEW_MODE:
183                     mOldPin = savedInstanceState.getString(OLD_PINCODE);
184                     break;
185
186                 case ICC_REENTER_MODE:
187                     mOldPin = savedInstanceState.getString(OLD_PINCODE);
188                     mNewPin = savedInstanceState.getString(NEW_PINCODE);
189                     break;
190
191                 case ICC_LOCK_MODE:
192                 case ICC_OLD_MODE:
193                 default:
194                     break;
195             }
196         }
197
198         mPinDialog.setOnPinEnteredListener(this);
199
200         // Don't need any changes to be remembered
201         getPreferenceScreen().setPersistent(false);
202
203         if (numSims > 1) {
204             setContentView(R.layout.icc_lock_tabs);
205
206             mTabHost = (TabHost) findViewById(android.R.id.tabhost);
207             mTabWidget = (TabWidget) findViewById(android.R.id.tabs);
208             mListView = (ListView) findViewById(android.R.id.list);
209
210             mTabHost.setup();
211             mTabHost.setOnTabChangedListener(mTabListener);
212             mTabHost.clearAllTabs();
213
214             SubscriptionManager sm = SubscriptionManager.from(this);
215             for (int i = 0; i < numSims; ++i) {
216                 final SubscriptionInfo subInfo = sm.getActiveSubscriptionInfoForSimSlotIndex(i);
217                 mTabHost.addTab(buildTabSpec(String.valueOf(i),
218                         String.valueOf(subInfo == null
219                             ? context.getString(R.string.sim_editor_title, i + 1)
220                             : subInfo.getDisplayName())));
221             }
222             final SubscriptionInfo sir = sm.getActiveSubscriptionInfoForSimSlotIndex(0);
223
224             mPhone = (sir == null) ? null
225                 : PhoneFactory.getPhone(SubscriptionManager.getPhoneId(sir.getSubscriptionId()));
226         } else {
227             mPhone = PhoneFactory.getDefaultPhone();
228         }
229         mRes = getResources();
230         updatePreferences();
231     }
232
233     private void updatePreferences() {
234         mPinDialog.setEnabled(mPhone != null);
235         mPinToggle.setEnabled(mPhone != null);
236
237         if (mPhone != null) {
238             mPinToggle.setChecked(mPhone.getIccCard().getIccLockEnabled());
239         }
240     }
241
242     @Override
243     protected int getMetricsCategory() {
244         return MetricsEvent.ICC_LOCK;
245     }
246
247     @Override
248     protected void onResume() {
249         super.onResume();
250
251         // ACTION_SIM_STATE_CHANGED is sticky, so we'll receive current state after this call,
252         // which will call updatePreferences().
253         final IntentFilter filter = new IntentFilter(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
254         registerReceiver(mSimStateReceiver, filter);
255
256         if (mDialogState != OFF_MODE) {
257             showPinDialog();
258         } else {
259             // Prep for standard click on "Change PIN"
260             resetDialogState();
261         }
262     }
263
264     @Override
265     protected void onPause() {
266         super.onPause();
267         unregisterReceiver(mSimStateReceiver);
268     }
269
270     @Override
271     protected void onSaveInstanceState(Bundle out) {
272         // Need to store this state for slider open/close
273         // There is one case where the dialog is popped up by the preference
274         // framework. In that case, let the preference framework store the
275         // dialog state. In other cases, where this activity manually launches
276         // the dialog, store the state of the dialog.
277         if (mPinDialog.isDialogOpen()) {
278             out.putInt(DIALOG_STATE, mDialogState);
279             out.putString(DIALOG_PIN, mPinDialog.getEditText().getText().toString());
280             out.putString(DIALOG_ERROR, mError);
281             out.putBoolean(ENABLE_TO_STATE, mToState);
282
283             // Save inputted PIN code
284             switch (mDialogState) {
285                 case ICC_NEW_MODE:
286                     out.putString(OLD_PINCODE, mOldPin);
287                     break;
288
289                 case ICC_REENTER_MODE:
290                     out.putString(OLD_PINCODE, mOldPin);
291                     out.putString(NEW_PINCODE, mNewPin);
292                     break;
293
294                 case ICC_LOCK_MODE:
295                 case ICC_OLD_MODE:
296                 default:
297                     break;
298             }
299         } else {
300             super.onSaveInstanceState(out);
301         }
302     }
303
304     private void showPinDialog() {
305         if (mDialogState == OFF_MODE) {
306             return;
307         }
308         setDialogValues();
309
310         mPinDialog.showPinDialog();
311     }
312
313     private void setDialogValues() {
314         mPinDialog.setText(mPin);
315         String message = "";
316         switch (mDialogState) {
317             case ICC_LOCK_MODE:
318                 message = mRes.getString(R.string.sim_enter_pin);
319                 mPinDialog.setDialogTitle(mToState
320                         ? mRes.getString(R.string.sim_enable_sim_lock)
321                         : mRes.getString(R.string.sim_disable_sim_lock));
322                 break;
323             case ICC_OLD_MODE:
324                 message = mRes.getString(R.string.sim_enter_old);
325                 mPinDialog.setDialogTitle(mRes.getString(R.string.sim_change_pin));
326                 break;
327             case ICC_NEW_MODE:
328                 message = mRes.getString(R.string.sim_enter_new);
329                 mPinDialog.setDialogTitle(mRes.getString(R.string.sim_change_pin));
330                 break;
331             case ICC_REENTER_MODE:
332                 message = mRes.getString(R.string.sim_reenter_new);
333                 mPinDialog.setDialogTitle(mRes.getString(R.string.sim_change_pin));
334                 break;
335         }
336         if (mError != null) {
337             message = mError + "\n" + message;
338             mError = null;
339         }
340         mPinDialog.setDialogMessage(message);
341     }
342
343     public void onPinEntered(EditPinPreference preference, boolean positiveResult) {
344         if (!positiveResult) {
345             resetDialogState();
346             return;
347         }
348
349         mPin = preference.getText();
350         if (!reasonablePin(mPin)) {
351             // inject error message and display dialog again
352             mError = mRes.getString(R.string.sim_bad_pin);
353             showPinDialog();
354             return;
355         }
356         switch (mDialogState) {
357             case ICC_LOCK_MODE:
358                 tryChangeIccLockState();
359                 break;
360             case ICC_OLD_MODE:
361                 mOldPin = mPin;
362                 mDialogState = ICC_NEW_MODE;
363                 mError = null;
364                 mPin = null;
365                 showPinDialog();
366                 break;
367             case ICC_NEW_MODE:
368                 mNewPin = mPin;
369                 mDialogState = ICC_REENTER_MODE;
370                 mPin = null;
371                 showPinDialog();
372                 break;
373             case ICC_REENTER_MODE:
374                 if (!mPin.equals(mNewPin)) {
375                     mError = mRes.getString(R.string.sim_pins_dont_match);
376                     mDialogState = ICC_NEW_MODE;
377                     mPin = null;
378                     showPinDialog();
379                 } else {
380                     mError = null;
381                     tryChangePin();
382                 }
383                 break;
384         }
385     }
386
387     public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
388         if (preference == mPinToggle) {
389             // Get the new, preferred state
390             mToState = mPinToggle.isChecked();
391             // Flip it back and pop up pin dialog
392             mPinToggle.setChecked(!mToState);
393             mDialogState = ICC_LOCK_MODE;
394             showPinDialog();
395         } else if (preference == mPinDialog) {
396             mDialogState = ICC_OLD_MODE;
397             return false;
398         }
399         return true;
400     }
401
402     private void tryChangeIccLockState() {
403         // Try to change icc lock. If it succeeds, toggle the lock state and
404         // reset dialog state. Else inject error message and show dialog again.
405         Message callback = Message.obtain(mHandler, MSG_ENABLE_ICC_PIN_COMPLETE);
406         mPhone.getIccCard().setIccLockEnabled(mToState, mPin, callback);
407         // Disable the setting till the response is received.
408         mPinToggle.setEnabled(false);
409     }
410
411     private void iccLockChanged(boolean success, int attemptsRemaining) {
412         if (success) {
413             mPinToggle.setChecked(mToState);
414         } else {
415             Toast.makeText(this, getPinPasswordErrorMessage(attemptsRemaining), Toast.LENGTH_LONG)
416                     .show();
417         }
418         mPinToggle.setEnabled(true);
419         resetDialogState();
420     }
421
422     private void iccPinChanged(boolean success, int attemptsRemaining) {
423         if (!success) {
424             Toast.makeText(this, getPinPasswordErrorMessage(attemptsRemaining),
425                     Toast.LENGTH_LONG)
426                     .show();
427         } else {
428             Toast.makeText(this, mRes.getString(R.string.sim_change_succeeded),
429                     Toast.LENGTH_SHORT)
430                     .show();
431
432         }
433         resetDialogState();
434     }
435
436     private void tryChangePin() {
437         Message callback = Message.obtain(mHandler, MSG_CHANGE_ICC_PIN_COMPLETE);
438         mPhone.getIccCard().changeIccLockPassword(mOldPin,
439                 mNewPin, callback);
440     }
441
442     private String getPinPasswordErrorMessage(int attemptsRemaining) {
443         String displayMessage;
444
445         if (attemptsRemaining == 0) {
446             displayMessage = mRes.getString(R.string.wrong_pin_code_pukked);
447         } else if (attemptsRemaining > 0) {
448             displayMessage = mRes
449                     .getQuantityString(R.plurals.wrong_pin_code, attemptsRemaining,
450                             attemptsRemaining);
451         } else {
452             displayMessage = mRes.getString(R.string.pin_failed);
453         }
454         if (DBG) Log.d(TAG, "getPinPasswordErrorMessage:"
455                 + " attemptsRemaining=" + attemptsRemaining + " displayMessage=" + displayMessage);
456         return displayMessage;
457     }
458
459     private boolean reasonablePin(String pin) {
460         if (pin == null || pin.length() < MIN_PIN_LENGTH || pin.length() > MAX_PIN_LENGTH) {
461             return false;
462         } else {
463             return true;
464         }
465     }
466
467     private void resetDialogState() {
468         mError = null;
469         mDialogState = ICC_OLD_MODE; // Default for when Change PIN is clicked
470         mPin = "";
471         setDialogValues();
472         mDialogState = OFF_MODE;
473     }
474
475     private OnTabChangeListener mTabListener = new OnTabChangeListener() {
476         @Override
477         public void onTabChanged(String tabId) {
478             final int slotId = Integer.parseInt(tabId);
479             final SubscriptionInfo sir = SubscriptionManager.from(getBaseContext())
480                     .getActiveSubscriptionInfoForSimSlotIndex(slotId);
481
482             mPhone = (sir == null) ? null
483                 : PhoneFactory.getPhone(SubscriptionManager.getPhoneId(sir.getSubscriptionId()));
484
485             // The User has changed tab; update the body.
486             updatePreferences();
487         }
488     };
489
490     private TabContentFactory mEmptyTabContent = new TabContentFactory() {
491         @Override
492         public View createTabContent(String tag) {
493             return new View(mTabHost.getContext());
494         }
495     };
496
497     private TabSpec buildTabSpec(String tag, String title) {
498         return mTabHost.newTabSpec(tag).setIndicator(title).setContent(
499                 mEmptyTabContent);
500     }
501 }