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