• 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.settings;
18 
19 import android.app.settings.SettingsEnums;
20 import android.content.BroadcastReceiver;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.IntentFilter;
24 import android.content.res.Configuration;
25 import android.content.res.Resources;
26 import android.graphics.PixelFormat;
27 import android.os.AsyncTask;
28 import android.os.Bundle;
29 import android.os.Handler;
30 import android.os.Message;
31 import android.os.PersistableBundle;
32 import android.telephony.CarrierConfigManager;
33 import android.telephony.PinResult;
34 import android.telephony.SubscriptionInfo;
35 import android.telephony.SubscriptionManager;
36 import android.telephony.TelephonyManager;
37 import android.text.TextUtils;
38 import android.util.Log;
39 import android.view.Gravity;
40 import android.view.LayoutInflater;
41 import android.view.View;
42 import android.view.ViewGroup;
43 import android.view.WindowInsets.Type;
44 import android.view.WindowManager;
45 import android.widget.EditText;
46 import android.widget.ListView;
47 import android.widget.TabHost;
48 import android.widget.TabHost.OnTabChangeListener;
49 import android.widget.TabHost.TabContentFactory;
50 import android.widget.TabHost.TabSpec;
51 import android.widget.TabWidget;
52 import android.widget.TextView;
53 import android.widget.Toast;
54 
55 import androidx.preference.Preference;
56 import androidx.preference.SwitchPreference;
57 
58 import com.android.settings.network.ProxySubscriptionManager;
59 import com.android.settings.network.SubscriptionUtil;
60 
61 import java.util.ArrayList;
62 import java.util.List;
63 
64 /**
65  * Implements the preference screen to enable/disable ICC lock and
66  * also the dialogs to change the ICC PIN. In the former case, enabling/disabling
67  * the ICC lock will prompt the user for the current PIN.
68  * In the Change PIN case, it prompts the user for old pin, new pin and new pin
69  * again before attempting to change it. Calls the SimCard interface to execute
70  * these operations.
71  *
72  */
73 public class IccLockSettings extends SettingsPreferenceFragment
74         implements EditPinPreference.OnPinEnteredListener {
75     private static final String TAG = "IccLockSettings";
76     private static final boolean DBG = false;
77 
78     private static final int OFF_MODE = 0;
79     // State when enabling/disabling ICC lock
80     private static final int ICC_LOCK_MODE = 1;
81     // State when entering the old pin
82     private static final int ICC_OLD_MODE = 2;
83     // State when entering the new pin - first time
84     private static final int ICC_NEW_MODE = 3;
85     // State when entering the new pin - second time
86     private static final int ICC_REENTER_MODE = 4;
87 
88     // Keys in xml file
89     private static final String PIN_DIALOG = "sim_pin";
90     private static final String PIN_TOGGLE = "sim_toggle";
91     // Keys in icicle
92     private static final String DIALOG_SUB_ID = "dialogSubId";
93     private static final String DIALOG_STATE = "dialogState";
94     private static final String DIALOG_PIN = "dialogPin";
95     private static final String DIALOG_ERROR = "dialogError";
96     private static final String ENABLE_TO_STATE = "enableState";
97     private static final String CURRENT_TAB = "currentTab";
98 
99     // Save and restore inputted PIN code when configuration changed
100     // (ex. portrait<-->landscape) during change PIN code
101     private static final String OLD_PINCODE = "oldPinCode";
102     private static final String NEW_PINCODE = "newPinCode";
103 
104     private static final int MIN_PIN_LENGTH = 4;
105     private static final int MAX_PIN_LENGTH = 8;
106     // Which dialog to show next when popped up
107     private int mDialogState = OFF_MODE;
108 
109     private String mPin;
110     private String mOldPin;
111     private String mNewPin;
112     private String mError;
113     // Are we trying to enable or disable ICC lock?
114     private boolean mToState;
115 
116     private TabHost mTabHost;
117     private TabWidget mTabWidget;
118     private ListView mListView;
119 
120     private ProxySubscriptionManager mProxySubscriptionMgr;
121 
122     private EditPinPreference mPinDialog;
123     private SwitchPreference mPinToggle;
124 
125     private Resources mRes;
126 
127     // For async handler to identify request type
128     private static final int MSG_SIM_STATE_CHANGED = 102;
129 
130     // @see android.widget.Toast$TN
131     private static final long LONG_DURATION_TIMEOUT = 7000;
132 
133     private int mSlotId = -1;
134     private int mSubId;
135     private TelephonyManager mTelephonyManager;
136 
137     // For replies from IccCard interface
138     private Handler mHandler = new Handler() {
139         public void handleMessage(Message msg) {
140             switch (msg.what) {
141                 case MSG_SIM_STATE_CHANGED:
142                     updatePreferences();
143                     break;
144             }
145 
146             return;
147         }
148     };
149 
150     private final BroadcastReceiver mSimStateReceiver = new BroadcastReceiver() {
151         public void onReceive(Context context, Intent intent) {
152             final String action = intent.getAction();
153             if (Intent.ACTION_SIM_STATE_CHANGED.equals(action)) {
154                 mHandler.sendMessage(mHandler.obtainMessage(MSG_SIM_STATE_CHANGED));
155             }
156         }
157     };
158 
159     // For top-level settings screen to query
isIccLockEnabled()160     private boolean isIccLockEnabled() {
161         mTelephonyManager = mTelephonyManager.createForSubscriptionId(mSubId);
162         return mTelephonyManager.isIccLockEnabled();
163     }
164 
getSummary(Context context)165     private String getSummary(Context context) {
166         final Resources res = context.getResources();
167         final String summary = isIccLockEnabled()
168                 ? res.getString(R.string.sim_lock_on)
169                 : res.getString(R.string.sim_lock_off);
170         return summary;
171     }
172 
173     @Override
onCreate(Bundle savedInstanceState)174     public void onCreate(Bundle savedInstanceState) {
175         super.onCreate(savedInstanceState);
176 
177         if (Utils.isMonkeyRunning()) {
178             finish();
179             return;
180         }
181 
182         // enable ProxySubscriptionMgr with Lifecycle support for all controllers
183         // live within this fragment
184         mProxySubscriptionMgr = ProxySubscriptionManager.getInstance(getContext());
185         mProxySubscriptionMgr.setLifecycle(getLifecycle());
186 
187         mTelephonyManager = getContext().getSystemService(TelephonyManager.class);
188 
189         addPreferencesFromResource(R.xml.sim_lock_settings);
190 
191         mPinDialog = (EditPinPreference) findPreference(PIN_DIALOG);
192         mPinToggle = (SwitchPreference) findPreference(PIN_TOGGLE);
193         if (savedInstanceState != null) {
194             if (savedInstanceState.containsKey(DIALOG_STATE)
195                     && restoreDialogStates(savedInstanceState)) {
196                 Log.d(TAG, "onCreate: restore dialog for slotId=" + mSlotId + ", subId=" + mSubId);
197             } else if (savedInstanceState.containsKey(CURRENT_TAB)
198                     && restoreTabFocus(savedInstanceState)) {
199                 Log.d(TAG, "onCreate: restore focus on slotId=" + mSlotId + ", subId=" + mSubId);
200             }
201         }
202 
203         mPinDialog.setOnPinEnteredListener(this);
204 
205         // Don't need any changes to be remembered
206         getPreferenceScreen().setPersistent(false);
207 
208         mRes = getResources();
209     }
210 
restoreDialogStates(Bundle savedInstanceState)211     private boolean restoreDialogStates(Bundle savedInstanceState) {
212         final SubscriptionInfo subInfo = mProxySubscriptionMgr
213                 .getActiveSubscriptionInfo(savedInstanceState.getInt(DIALOG_SUB_ID));
214         if (subInfo == null) {
215             return false;
216         }
217 
218         final SubscriptionInfo visibleSubInfo = getVisibleSubscriptionInfoForSimSlotIndex(
219                 subInfo.getSimSlotIndex());
220         if (visibleSubInfo == null) {
221             return false;
222         }
223         if (visibleSubInfo.getSubscriptionId() != subInfo.getSubscriptionId()) {
224             return false;
225         }
226 
227         mSlotId = subInfo.getSimSlotIndex();
228         mSubId = subInfo.getSubscriptionId();
229         mDialogState = savedInstanceState.getInt(DIALOG_STATE);
230         mPin = savedInstanceState.getString(DIALOG_PIN);
231         mError = savedInstanceState.getString(DIALOG_ERROR);
232         mToState = savedInstanceState.getBoolean(ENABLE_TO_STATE);
233 
234         // Restore inputted PIN code
235         switch (mDialogState) {
236             case ICC_NEW_MODE:
237                 mOldPin = savedInstanceState.getString(OLD_PINCODE);
238                 break;
239 
240             case ICC_REENTER_MODE:
241                 mOldPin = savedInstanceState.getString(OLD_PINCODE);
242                 mNewPin = savedInstanceState.getString(NEW_PINCODE);
243                 break;
244         }
245         return true;
246     }
247 
restoreTabFocus(Bundle savedInstanceState)248     private boolean restoreTabFocus(Bundle savedInstanceState) {
249         int slotId = 0;
250         try {
251             slotId = Integer.parseInt(savedInstanceState.getString(CURRENT_TAB));
252         } catch (NumberFormatException exception) {
253             return false;
254         }
255 
256         final SubscriptionInfo subInfo = getVisibleSubscriptionInfoForSimSlotIndex(slotId);
257         if (subInfo == null) {
258             return false;
259         }
260 
261         mSlotId = subInfo.getSimSlotIndex();
262         mSubId = subInfo.getSubscriptionId();
263         if (mTabHost != null) {
264             mTabHost.setCurrentTabByTag(getTagForSlotId(mSlotId));
265         }
266         return true;
267     }
268 
269     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)270     public View onCreateView(LayoutInflater inflater, ViewGroup container,
271             Bundle savedInstanceState) {
272 
273         final int numSims = mProxySubscriptionMgr.getActiveSubscriptionInfoCountMax();
274         final List<SubscriptionInfo> componenterList = new ArrayList<>();
275 
276         for (int i = 0; i < numSims; ++i) {
277             final SubscriptionInfo subInfo = getVisibleSubscriptionInfoForSimSlotIndex(i);
278             if (subInfo != null) {
279                 componenterList.add(subInfo);
280             }
281         }
282 
283         if (componenterList.size() == 0) {
284             Log.e(TAG, "onCreateView: no sim info");
285             return super.onCreateView(inflater, container, savedInstanceState);
286         }
287 
288         if (mSlotId < 0) {
289             mSlotId = componenterList.get(0).getSimSlotIndex();
290             mSubId = componenterList.get(0).getSubscriptionId();
291             Log.d(TAG, "onCreateView: default slotId=" + mSlotId + ", subId=" + mSubId);
292         }
293 
294         if (componenterList.size() > 1) {
295             final View view = inflater.inflate(R.layout.icc_lock_tabs, container, false);
296             final ViewGroup prefs_container = (ViewGroup) view.findViewById(R.id.prefs_container);
297             Utils.prepareCustomPreferencesList(container, view, prefs_container, false);
298             final View prefs = super.onCreateView(inflater, prefs_container, savedInstanceState);
299             prefs_container.addView(prefs);
300 
301             mTabHost = (TabHost) view.findViewById(android.R.id.tabhost);
302             mTabWidget = (TabWidget) view.findViewById(android.R.id.tabs);
303             mListView = (ListView) view.findViewById(android.R.id.list);
304 
305             mTabHost.setup();
306             mTabHost.clearAllTabs();
307 
308             for (SubscriptionInfo subInfo : componenterList) {
309                 final int slot = subInfo.getSimSlotIndex();
310                 final String tag = getTagForSlotId(slot);
311                 mTabHost.addTab(buildTabSpec(tag,
312                         String.valueOf(subInfo == null
313                                 ? getContext().getString(R.string.sim_editor_title, slot + 1)
314                                 : SubscriptionUtil.getUniqueSubscriptionDisplayName(
315                                         subInfo, getContext()))));
316             }
317 
318             mTabHost.setCurrentTabByTag(getTagForSlotId(mSlotId));
319             mTabHost.setOnTabChangedListener(mTabListener);
320             return view;
321         } else {
322             return super.onCreateView(inflater, container, savedInstanceState);
323         }
324     }
325 
326     @Override
onViewCreated(View view, Bundle savedInstanceState)327     public void onViewCreated(View view, Bundle savedInstanceState) {
328         super.onViewCreated(view, savedInstanceState);
329         updatePreferences();
330     }
331 
updatePreferences()332     private void updatePreferences() {
333 
334         final SubscriptionInfo sir = getVisibleSubscriptionInfoForSimSlotIndex(mSlotId);
335         final int subId = (sir != null) ? sir.getSubscriptionId()
336                 : SubscriptionManager.INVALID_SUBSCRIPTION_ID;
337 
338         if (mSubId != subId) {
339             mSubId = subId;
340             resetDialogState();
341             if ((mPinDialog != null) && mPinDialog.isDialogOpen()) {
342                 mPinDialog.getDialog().dismiss();
343             }
344         }
345 
346         if (mPinDialog != null) {
347             mPinDialog.setEnabled(sir != null);
348         }
349         if (mPinToggle != null) {
350             mPinToggle.setEnabled(sir != null);
351 
352             if (sir != null) {
353                 mPinToggle.setChecked(isIccLockEnabled());
354             }
355         }
356     }
357 
358     @Override
getMetricsCategory()359     public int getMetricsCategory() {
360         return SettingsEnums.ICC_LOCK;
361     }
362 
363     @Override
onResume()364     public void onResume() {
365         super.onResume();
366 
367         // ACTION_SIM_STATE_CHANGED is sticky, so we'll receive current state after this call,
368         // which will call updatePreferences().
369         final IntentFilter filter = new IntentFilter(Intent.ACTION_SIM_STATE_CHANGED);
370         getContext().registerReceiver(mSimStateReceiver, filter);
371 
372         if (mDialogState != OFF_MODE) {
373             showPinDialog();
374         } else {
375             // Prep for standard click on "Change PIN"
376             resetDialogState();
377         }
378     }
379 
380     @Override
onPause()381     public void onPause() {
382         super.onPause();
383         getContext().unregisterReceiver(mSimStateReceiver);
384     }
385 
386     @Override
getHelpResource()387     public int getHelpResource() {
388         return R.string.help_url_icc_lock;
389     }
390 
391     @Override
onSaveInstanceState(Bundle out)392     public void onSaveInstanceState(Bundle out) {
393         // Need to store this state for slider open/close
394         // There is one case where the dialog is popped up by the preference
395         // framework. In that case, let the preference framework store the
396         // dialog state. In other cases, where this activity manually launches
397         // the dialog, store the state of the dialog.
398         if (mPinDialog.isDialogOpen()) {
399             out.putInt(DIALOG_SUB_ID, mSubId);
400             out.putInt(DIALOG_STATE, mDialogState);
401             out.putString(DIALOG_PIN, mPinDialog.getEditText().getText().toString());
402             out.putString(DIALOG_ERROR, mError);
403             out.putBoolean(ENABLE_TO_STATE, mToState);
404 
405             // Save inputted PIN code
406             switch (mDialogState) {
407                 case ICC_NEW_MODE:
408                     out.putString(OLD_PINCODE, mOldPin);
409                     break;
410 
411                 case ICC_REENTER_MODE:
412                     out.putString(OLD_PINCODE, mOldPin);
413                     out.putString(NEW_PINCODE, mNewPin);
414                     break;
415             }
416         } else {
417             super.onSaveInstanceState(out);
418         }
419 
420         if (mTabHost != null) {
421             out.putString(CURRENT_TAB, mTabHost.getCurrentTabTag());
422         }
423     }
424 
showPinDialog()425     private void showPinDialog() {
426         if (mDialogState == OFF_MODE) {
427             return;
428         }
429         setDialogValues();
430 
431         mPinDialog.showPinDialog();
432 
433         final EditText editText = mPinDialog.getEditText();
434         if (!TextUtils.isEmpty(mPin) && editText != null) {
435             editText.setSelection(mPin.length());
436         }
437     }
438 
setDialogValues()439     private void setDialogValues() {
440         mPinDialog.setText(mPin);
441         String message = "";
442         switch (mDialogState) {
443             case ICC_LOCK_MODE:
444                 message = mRes.getString(R.string.sim_enter_pin);
445                 mPinDialog.setDialogTitle(mToState
446                         ? mRes.getString(R.string.sim_enable_sim_lock)
447                         : mRes.getString(R.string.sim_disable_sim_lock));
448                 break;
449             case ICC_OLD_MODE:
450                 message = mRes.getString(R.string.sim_enter_old);
451                 mPinDialog.setDialogTitle(mRes.getString(R.string.sim_change_pin));
452                 break;
453             case ICC_NEW_MODE:
454                 message = mRes.getString(R.string.sim_enter_new);
455                 mPinDialog.setDialogTitle(mRes.getString(R.string.sim_change_pin));
456                 break;
457             case ICC_REENTER_MODE:
458                 message = mRes.getString(R.string.sim_reenter_new);
459                 mPinDialog.setDialogTitle(mRes.getString(R.string.sim_change_pin));
460                 break;
461         }
462         if (mError != null) {
463             message = mError + "\n" + message;
464             mError = null;
465         }
466         mPinDialog.setDialogMessage(message);
467     }
468 
469     @Override
onPinEntered(EditPinPreference preference, boolean positiveResult)470     public void onPinEntered(EditPinPreference preference, boolean positiveResult) {
471         if (!positiveResult) {
472             resetDialogState();
473             return;
474         }
475 
476         mPin = preference.getText();
477         if (!reasonablePin(mPin)) {
478             // inject error message and display dialog again
479             mError = mRes.getString(R.string.sim_bad_pin);
480             showPinDialog();
481             return;
482         }
483         switch (mDialogState) {
484             case ICC_LOCK_MODE:
485                 tryChangeIccLockState();
486                 break;
487             case ICC_OLD_MODE:
488                 mOldPin = mPin;
489                 mDialogState = ICC_NEW_MODE;
490                 mError = null;
491                 mPin = null;
492                 showPinDialog();
493                 break;
494             case ICC_NEW_MODE:
495                 mNewPin = mPin;
496                 mDialogState = ICC_REENTER_MODE;
497                 mPin = null;
498                 showPinDialog();
499                 break;
500             case ICC_REENTER_MODE:
501                 if (!mPin.equals(mNewPin)) {
502                     mError = mRes.getString(R.string.sim_pins_dont_match);
503                     mDialogState = ICC_NEW_MODE;
504                     mPin = null;
505                     showPinDialog();
506                 } else {
507                     mError = null;
508                     tryChangePin();
509                 }
510                 break;
511         }
512     }
513 
514     @Override
onPreferenceTreeClick(Preference preference)515     public boolean onPreferenceTreeClick(Preference preference) {
516         if (preference == mPinToggle) {
517             // Get the new, preferred state
518             mToState = mPinToggle.isChecked();
519             // Flip it back and pop up pin dialog
520             mPinToggle.setChecked(!mToState);
521             mDialogState = ICC_LOCK_MODE;
522             showPinDialog();
523         } else if (preference == mPinDialog) {
524             mDialogState = ICC_OLD_MODE;
525             return false;
526         }
527         return true;
528     }
529 
tryChangeIccLockState()530     private void tryChangeIccLockState() {
531         // Try to change icc lock. If it succeeds, toggle the lock state and
532         // reset dialog state. Else inject error message and show dialog again.
533         new SetIccLockEnabled(mToState, mPin).execute();
534         // Disable the setting till the response is received.
535         mPinToggle.setEnabled(false);
536     }
537 
538     private class SetIccLockEnabled extends AsyncTask<Void, Void, PinResult> {
539         private final boolean mState;
540         private final String mPin;
541 
SetIccLockEnabled(boolean state, String pin)542         private SetIccLockEnabled(boolean state, String pin) {
543             mState = state;
544             mPin = pin;
545         }
546 
547         @Override
doInBackground(Void... params)548         protected PinResult doInBackground(Void... params) {
549             mTelephonyManager =  mTelephonyManager.createForSubscriptionId(mSubId);
550             return mTelephonyManager.setIccLockEnabled(mState, mPin);
551         }
552 
553         @Override
onPostExecute(PinResult pinResult)554         protected void onPostExecute(PinResult pinResult) {
555             iccLockChanged(pinResult.getResult() == PinResult.PIN_RESULT_TYPE_SUCCESS /* success */,
556                     pinResult.getAttemptsRemaining() /* attemptsRemaining */);
557         }
558     }
559 
iccLockChanged(boolean success, int attemptsRemaining)560     private void iccLockChanged(boolean success, int attemptsRemaining) {
561         Log.d(TAG, "iccLockChanged: success = " + success);
562         if (success) {
563             mPinToggle.setChecked(mToState);
564         } else {
565             if (attemptsRemaining >= 0) {
566                 createCustomTextToast(getPinPasswordErrorMessage(attemptsRemaining));
567             } else {
568                 if (mToState) {
569                     Toast.makeText(getContext(), mRes.getString(
570                             R.string.sim_pin_enable_failed), Toast.LENGTH_LONG).show();
571                 } else {
572                     Toast.makeText(getContext(), mRes.getString(
573                             R.string.sim_pin_disable_failed), Toast.LENGTH_LONG).show();
574                 }
575             }
576         }
577         mPinToggle.setEnabled(true);
578         resetDialogState();
579     }
580 
createCustomTextToast(CharSequence errorMessage)581     private void createCustomTextToast(CharSequence errorMessage) {
582         // Cannot overlay Toast on PUK unlock screen.
583         // The window type of Toast is set by NotificationManagerService.
584         // It can't be overwritten by LayoutParams.type.
585         // Ovarlay a custom window with LayoutParams (TYPE_STATUS_BAR_PANEL) on PUK unlock screen.
586         final View v = ((LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE))
587                 .inflate(com.android.internal.R.layout.transient_notification, null);
588         final TextView tv = (TextView) v.findViewById(com.android.internal.R.id.message);
589         tv.setText(errorMessage);
590 
591         final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
592         final Configuration config = v.getContext().getResources().getConfiguration();
593         final int gravity = Gravity.getAbsoluteGravity(
594                 getContext().getResources().getInteger(
595                         com.android.internal.R.integer.config_toastDefaultGravity),
596                 config.getLayoutDirection());
597         params.gravity = gravity;
598         if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {
599             params.horizontalWeight = 1.0f;
600         }
601         if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {
602             params.verticalWeight = 1.0f;
603         }
604         params.y = getContext().getResources().getDimensionPixelSize(
605                 com.android.internal.R.dimen.toast_y_offset);
606 
607         params.height = WindowManager.LayoutParams.WRAP_CONTENT;
608         params.width = WindowManager.LayoutParams.WRAP_CONTENT;
609         params.format = PixelFormat.TRANSLUCENT;
610         params.windowAnimations = com.android.internal.R.style.Animation_Toast;
611         params.type = WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
612         params.setFitInsetsTypes(params.getFitInsetsTypes() & ~Type.statusBars());
613         params.setTitle(errorMessage);
614         params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
615                 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
616                 | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
617 
618         final WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
619         wm.addView(v, params);
620 
621         mHandler.postDelayed(new Runnable() {
622             @Override
623             public void run() {
624                 wm.removeViewImmediate(v);
625             }
626         }, LONG_DURATION_TIMEOUT);
627     }
628 
iccPinChanged(boolean success, int attemptsRemaining)629     private void iccPinChanged(boolean success, int attemptsRemaining) {
630         Log.d(TAG, "iccPinChanged: success = " + success);
631         if (!success) {
632             createCustomTextToast(getPinPasswordErrorMessage(attemptsRemaining));
633         } else {
634             Toast.makeText(getContext(), mRes.getString(R.string.sim_change_succeeded),
635                     Toast.LENGTH_SHORT)
636                     .show();
637         }
638         resetDialogState();
639     }
640 
tryChangePin()641     private void tryChangePin() {
642         new ChangeIccLockPin(mOldPin, mNewPin).execute();
643     }
644 
645     private class ChangeIccLockPin extends AsyncTask<Void, Void, PinResult> {
646         private final String mOldPin;
647         private final String mNewPin;
648 
ChangeIccLockPin(String oldPin, String newPin)649         private ChangeIccLockPin(String oldPin, String newPin) {
650             mOldPin = oldPin;
651             mNewPin = newPin;
652         }
653 
654         @Override
doInBackground(Void... params)655         protected PinResult doInBackground(Void... params) {
656             mTelephonyManager = mTelephonyManager.createForSubscriptionId(mSubId);
657             return mTelephonyManager.changeIccLockPin(mOldPin, mNewPin);
658         }
659 
660         @Override
onPostExecute(PinResult pinResult)661         protected void onPostExecute(PinResult pinResult) {
662             iccPinChanged(pinResult.getResult() == PinResult.PIN_RESULT_TYPE_SUCCESS /* success */,
663                     pinResult.getAttemptsRemaining() /* attemptsRemaining */);
664         }
665     }
666 
getPinPasswordErrorMessage(int attemptsRemaining)667     private String getPinPasswordErrorMessage(int attemptsRemaining) {
668         String displayMessage;
669 
670         if (attemptsRemaining == 0) {
671             displayMessage = mRes.getString(R.string.wrong_pin_code_pukked);
672         } else if (attemptsRemaining == 1) {
673             displayMessage = mRes.getString(R.string.wrong_pin_code_one, attemptsRemaining);
674         } else if (attemptsRemaining > 1) {
675             displayMessage = mRes
676                     .getQuantityString(R.plurals.wrong_pin_code, attemptsRemaining,
677                             attemptsRemaining);
678         } else {
679             displayMessage = mRes.getString(R.string.pin_failed);
680         }
681         if (DBG) Log.d(TAG, "getPinPasswordErrorMessage:"
682                 + " attemptsRemaining=" + attemptsRemaining + " displayMessage=" + displayMessage);
683         return displayMessage;
684     }
685 
reasonablePin(String pin)686     private boolean reasonablePin(String pin) {
687         if (pin == null || pin.length() < MIN_PIN_LENGTH || pin.length() > MAX_PIN_LENGTH) {
688             return false;
689         } else {
690             return true;
691         }
692     }
693 
resetDialogState()694     private void resetDialogState() {
695         mError = null;
696         mDialogState = ICC_OLD_MODE; // Default for when Change PIN is clicked
697         mPin = "";
698         setDialogValues();
699         mDialogState = OFF_MODE;
700     }
701 
getTagForSlotId(int slotId)702     private String getTagForSlotId(int slotId) {
703         return String.valueOf(slotId);
704     }
705 
getSlotIndexFromTag(String tag)706     private int getSlotIndexFromTag(String tag) {
707         int slotId = -1;
708         try {
709             slotId = Integer.parseInt(tag);
710         } catch (NumberFormatException exception) {
711         }
712         return slotId;
713     }
714 
getVisibleSubscriptionInfoForSimSlotIndex(int slotId)715     private SubscriptionInfo getVisibleSubscriptionInfoForSimSlotIndex(int slotId) {
716         final List<SubscriptionInfo> subInfoList =
717                 mProxySubscriptionMgr.getActiveSubscriptionsInfo();
718         if (subInfoList == null) {
719             return null;
720         }
721         final CarrierConfigManager carrierConfigManager = getContext().getSystemService(
722                 CarrierConfigManager.class);
723         for (SubscriptionInfo subInfo : subInfoList) {
724             if ((isSubscriptionVisible(carrierConfigManager, subInfo)
725                     && (subInfo.getSimSlotIndex() == slotId))) {
726                 return subInfo;
727             }
728         }
729         return null;
730     }
731 
isSubscriptionVisible(CarrierConfigManager carrierConfigManager, SubscriptionInfo subInfo)732     private boolean isSubscriptionVisible(CarrierConfigManager carrierConfigManager,
733             SubscriptionInfo subInfo) {
734         final PersistableBundle bundle = carrierConfigManager
735                 .getConfigForSubId(subInfo.getSubscriptionId());
736         if (bundle == null) {
737             return false;
738         }
739         return !bundle.getBoolean(CarrierConfigManager.KEY_HIDE_SIM_LOCK_SETTINGS_BOOL);
740     }
741 
742     private OnTabChangeListener mTabListener = new OnTabChangeListener() {
743         @Override
744         public void onTabChanged(String tabId) {
745             mSlotId = getSlotIndexFromTag(tabId);
746 
747             // The User has changed tab; update the body.
748             updatePreferences();
749         }
750     };
751 
752     private TabContentFactory mEmptyTabContent = new TabContentFactory() {
753         @Override
754         public View createTabContent(String tag) {
755             return new View(mTabHost.getContext());
756         }
757     };
758 
buildTabSpec(String tag, String title)759     private TabSpec buildTabSpec(String tag, String title) {
760         return mTabHost.newTabSpec(tag).setIndicator(title).setContent(
761                 mEmptyTabContent);
762     }
763 }
764