• 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                 !SubscriptionUtil.isSimHardwareVisible(getContext())) {
179             finishFragment();
180             return;
181         }
182 
183         // enable ProxySubscriptionMgr with Lifecycle support for all controllers
184         // live within this fragment
185         mProxySubscriptionMgr = ProxySubscriptionManager.getInstance(getContext());
186         mProxySubscriptionMgr.setLifecycle(getLifecycle());
187 
188         mTelephonyManager = getContext().getSystemService(TelephonyManager.class);
189 
190         addPreferencesFromResource(R.xml.sim_lock_settings);
191 
192         mPinDialog = (EditPinPreference) findPreference(PIN_DIALOG);
193         mPinToggle = (SwitchPreference) findPreference(PIN_TOGGLE);
194         if (savedInstanceState != null) {
195             if (savedInstanceState.containsKey(DIALOG_STATE)
196                     && restoreDialogStates(savedInstanceState)) {
197                 Log.d(TAG, "onCreate: restore dialog for slotId=" + mSlotId + ", subId=" + mSubId);
198             } else if (savedInstanceState.containsKey(CURRENT_TAB)
199                     && restoreTabFocus(savedInstanceState)) {
200                 Log.d(TAG, "onCreate: restore focus on slotId=" + mSlotId + ", subId=" + mSubId);
201             }
202         }
203 
204         mPinDialog.setOnPinEnteredListener(this);
205 
206         // Don't need any changes to be remembered
207         getPreferenceScreen().setPersistent(false);
208 
209         mRes = getResources();
210     }
211 
restoreDialogStates(Bundle savedInstanceState)212     private boolean restoreDialogStates(Bundle savedInstanceState) {
213         final SubscriptionInfo subInfo = mProxySubscriptionMgr
214                 .getActiveSubscriptionInfo(savedInstanceState.getInt(DIALOG_SUB_ID));
215         if (subInfo == null) {
216             return false;
217         }
218 
219         final SubscriptionInfo visibleSubInfo = getVisibleSubscriptionInfoForSimSlotIndex(
220                 subInfo.getSimSlotIndex());
221         if (visibleSubInfo == null) {
222             return false;
223         }
224         if (visibleSubInfo.getSubscriptionId() != subInfo.getSubscriptionId()) {
225             return false;
226         }
227 
228         mSlotId = subInfo.getSimSlotIndex();
229         mSubId = subInfo.getSubscriptionId();
230         mDialogState = savedInstanceState.getInt(DIALOG_STATE);
231         mPin = savedInstanceState.getString(DIALOG_PIN);
232         mError = savedInstanceState.getString(DIALOG_ERROR);
233         mToState = savedInstanceState.getBoolean(ENABLE_TO_STATE);
234 
235         // Restore inputted PIN code
236         switch (mDialogState) {
237             case ICC_NEW_MODE:
238                 mOldPin = savedInstanceState.getString(OLD_PINCODE);
239                 break;
240 
241             case ICC_REENTER_MODE:
242                 mOldPin = savedInstanceState.getString(OLD_PINCODE);
243                 mNewPin = savedInstanceState.getString(NEW_PINCODE);
244                 break;
245         }
246         return true;
247     }
248 
restoreTabFocus(Bundle savedInstanceState)249     private boolean restoreTabFocus(Bundle savedInstanceState) {
250         int slotId = 0;
251         try {
252             slotId = Integer.parseInt(savedInstanceState.getString(CURRENT_TAB));
253         } catch (NumberFormatException exception) {
254             return false;
255         }
256 
257         final SubscriptionInfo subInfo = getVisibleSubscriptionInfoForSimSlotIndex(slotId);
258         if (subInfo == null) {
259             return false;
260         }
261 
262         mSlotId = subInfo.getSimSlotIndex();
263         mSubId = subInfo.getSubscriptionId();
264         if (mTabHost != null) {
265             mTabHost.setCurrentTabByTag(getTagForSlotId(mSlotId));
266         }
267         return true;
268     }
269 
270     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)271     public View onCreateView(LayoutInflater inflater, ViewGroup container,
272             Bundle savedInstanceState) {
273 
274         final int numSims = mProxySubscriptionMgr.getActiveSubscriptionInfoCountMax();
275         final List<SubscriptionInfo> componenterList = new ArrayList<>();
276 
277         for (int i = 0; i < numSims; ++i) {
278             final SubscriptionInfo subInfo = getVisibleSubscriptionInfoForSimSlotIndex(i);
279             if (subInfo != null) {
280                 componenterList.add(subInfo);
281             }
282         }
283 
284         if (componenterList.size() == 0) {
285             Log.e(TAG, "onCreateView: no sim info");
286             return super.onCreateView(inflater, container, savedInstanceState);
287         }
288 
289         if (mSlotId < 0) {
290             mSlotId = componenterList.get(0).getSimSlotIndex();
291             mSubId = componenterList.get(0).getSubscriptionId();
292             Log.d(TAG, "onCreateView: default slotId=" + mSlotId + ", subId=" + mSubId);
293         }
294 
295         if (componenterList.size() > 1) {
296             final View view = inflater.inflate(R.layout.icc_lock_tabs, container, false);
297             final ViewGroup prefs_container = (ViewGroup) view.findViewById(R.id.prefs_container);
298             Utils.prepareCustomPreferencesList(container, view, prefs_container, false);
299             final View prefs = super.onCreateView(inflater, prefs_container, savedInstanceState);
300             prefs_container.addView(prefs);
301 
302             mTabHost = (TabHost) view.findViewById(android.R.id.tabhost);
303             mTabWidget = (TabWidget) view.findViewById(android.R.id.tabs);
304             mListView = (ListView) view.findViewById(android.R.id.list);
305 
306             mTabHost.setup();
307             mTabHost.clearAllTabs();
308 
309             for (SubscriptionInfo subInfo : componenterList) {
310                 final int slot = subInfo.getSimSlotIndex();
311                 final String tag = getTagForSlotId(slot);
312                 mTabHost.addTab(buildTabSpec(tag,
313                         String.valueOf(subInfo == null
314                                 ? getContext().getString(R.string.sim_editor_title, slot + 1)
315                                 : SubscriptionUtil.getUniqueSubscriptionDisplayName(
316                                         subInfo, getContext()))));
317             }
318 
319             mTabHost.setCurrentTabByTag(getTagForSlotId(mSlotId));
320             mTabHost.setOnTabChangedListener(mTabListener);
321             return view;
322         } else {
323             return super.onCreateView(inflater, container, savedInstanceState);
324         }
325     }
326 
327     @Override
onViewCreated(View view, Bundle savedInstanceState)328     public void onViewCreated(View view, Bundle savedInstanceState) {
329         super.onViewCreated(view, savedInstanceState);
330         updatePreferences();
331     }
332 
updatePreferences()333     private void updatePreferences() {
334 
335         final SubscriptionInfo sir = getVisibleSubscriptionInfoForSimSlotIndex(mSlotId);
336         final int subId = (sir != null) ? sir.getSubscriptionId()
337                 : SubscriptionManager.INVALID_SUBSCRIPTION_ID;
338 
339         if (mSubId != subId) {
340             mSubId = subId;
341             resetDialogState();
342             if ((mPinDialog != null) && mPinDialog.isDialogOpen()) {
343                 mPinDialog.getDialog().dismiss();
344             }
345         }
346 
347         if (mPinDialog != null) {
348             mPinDialog.setEnabled(sir != null);
349         }
350         if (mPinToggle != null) {
351             mPinToggle.setEnabled(sir != null);
352 
353             if (sir != null) {
354                 mPinToggle.setChecked(isIccLockEnabled());
355             }
356         }
357     }
358 
359     @Override
getMetricsCategory()360     public int getMetricsCategory() {
361         return SettingsEnums.ICC_LOCK;
362     }
363 
364     @Override
onResume()365     public void onResume() {
366         super.onResume();
367 
368         // ACTION_SIM_STATE_CHANGED is sticky, so we'll receive current state after this call,
369         // which will call updatePreferences().
370         final IntentFilter filter = new IntentFilter(Intent.ACTION_SIM_STATE_CHANGED);
371         getContext().registerReceiver(mSimStateReceiver, filter);
372 
373         if (mDialogState != OFF_MODE) {
374             showPinDialog();
375         } else {
376             // Prep for standard click on "Change PIN"
377             resetDialogState();
378         }
379     }
380 
381     @Override
onPause()382     public void onPause() {
383         super.onPause();
384         getContext().unregisterReceiver(mSimStateReceiver);
385     }
386 
387     @Override
getHelpResource()388     public int getHelpResource() {
389         return R.string.help_url_icc_lock;
390     }
391 
392     @Override
onSaveInstanceState(Bundle out)393     public void onSaveInstanceState(Bundle out) {
394         // Need to store this state for slider open/close
395         // There is one case where the dialog is popped up by the preference
396         // framework. In that case, let the preference framework store the
397         // dialog state. In other cases, where this activity manually launches
398         // the dialog, store the state of the dialog.
399         if (mPinDialog.isDialogOpen()) {
400             out.putInt(DIALOG_SUB_ID, mSubId);
401             out.putInt(DIALOG_STATE, mDialogState);
402             out.putString(DIALOG_PIN, mPinDialog.getEditText().getText().toString());
403             out.putString(DIALOG_ERROR, mError);
404             out.putBoolean(ENABLE_TO_STATE, mToState);
405 
406             // Save inputted PIN code
407             switch (mDialogState) {
408                 case ICC_NEW_MODE:
409                     out.putString(OLD_PINCODE, mOldPin);
410                     break;
411 
412                 case ICC_REENTER_MODE:
413                     out.putString(OLD_PINCODE, mOldPin);
414                     out.putString(NEW_PINCODE, mNewPin);
415                     break;
416             }
417         } else {
418             super.onSaveInstanceState(out);
419         }
420 
421         if (mTabHost != null) {
422             out.putString(CURRENT_TAB, mTabHost.getCurrentTabTag());
423         }
424     }
425 
showPinDialog()426     private void showPinDialog() {
427         if (mDialogState == OFF_MODE) {
428             return;
429         }
430         setDialogValues();
431 
432         mPinDialog.showPinDialog();
433 
434         final EditText editText = mPinDialog.getEditText();
435         if (!TextUtils.isEmpty(mPin) && editText != null) {
436             editText.setSelection(mPin.length());
437         }
438     }
439 
setDialogValues()440     private void setDialogValues() {
441         mPinDialog.setText(mPin);
442         String message = "";
443         switch (mDialogState) {
444             case ICC_LOCK_MODE:
445                 message = mRes.getString(R.string.sim_enter_pin);
446                 mPinDialog.setDialogTitle(mToState
447                         ? mRes.getString(R.string.sim_enable_sim_lock)
448                         : mRes.getString(R.string.sim_disable_sim_lock));
449                 break;
450             case ICC_OLD_MODE:
451                 message = mRes.getString(R.string.sim_enter_old);
452                 mPinDialog.setDialogTitle(mRes.getString(R.string.sim_change_pin));
453                 break;
454             case ICC_NEW_MODE:
455                 message = mRes.getString(R.string.sim_enter_new);
456                 mPinDialog.setDialogTitle(mRes.getString(R.string.sim_change_pin));
457                 break;
458             case ICC_REENTER_MODE:
459                 message = mRes.getString(R.string.sim_reenter_new);
460                 mPinDialog.setDialogTitle(mRes.getString(R.string.sim_change_pin));
461                 break;
462         }
463         if (mError != null) {
464             message = mError + "\n" + message;
465             mError = null;
466         }
467         mPinDialog.setDialogMessage(message);
468     }
469 
470     @Override
onPinEntered(EditPinPreference preference, boolean positiveResult)471     public void onPinEntered(EditPinPreference preference, boolean positiveResult) {
472         if (!positiveResult) {
473             resetDialogState();
474             return;
475         }
476 
477         mPin = preference.getText();
478         if (!reasonablePin(mPin)) {
479             // inject error message and display dialog again
480             mError = mRes.getString(R.string.sim_bad_pin);
481             showPinDialog();
482             return;
483         }
484         switch (mDialogState) {
485             case ICC_LOCK_MODE:
486                 tryChangeIccLockState();
487                 break;
488             case ICC_OLD_MODE:
489                 mOldPin = mPin;
490                 mDialogState = ICC_NEW_MODE;
491                 mError = null;
492                 mPin = null;
493                 showPinDialog();
494                 break;
495             case ICC_NEW_MODE:
496                 mNewPin = mPin;
497                 mDialogState = ICC_REENTER_MODE;
498                 mPin = null;
499                 showPinDialog();
500                 break;
501             case ICC_REENTER_MODE:
502                 if (!mPin.equals(mNewPin)) {
503                     mError = mRes.getString(R.string.sim_pins_dont_match);
504                     mDialogState = ICC_NEW_MODE;
505                     mPin = null;
506                     showPinDialog();
507                 } else {
508                     mError = null;
509                     tryChangePin();
510                 }
511                 break;
512         }
513     }
514 
515     @Override
onPreferenceTreeClick(Preference preference)516     public boolean onPreferenceTreeClick(Preference preference) {
517         if (preference == mPinToggle) {
518             // Get the new, preferred state
519             mToState = mPinToggle.isChecked();
520             // Flip it back and pop up pin dialog
521             mPinToggle.setChecked(!mToState);
522             mDialogState = ICC_LOCK_MODE;
523             showPinDialog();
524         } else if (preference == mPinDialog) {
525             mDialogState = ICC_OLD_MODE;
526             return false;
527         }
528         return true;
529     }
530 
tryChangeIccLockState()531     private void tryChangeIccLockState() {
532         // Try to change icc lock. If it succeeds, toggle the lock state and
533         // reset dialog state. Else inject error message and show dialog again.
534         new SetIccLockEnabled(mToState, mPin).execute();
535         // Disable the setting till the response is received.
536         mPinToggle.setEnabled(false);
537     }
538 
539     private class SetIccLockEnabled extends AsyncTask<Void, Void, PinResult> {
540         private final boolean mState;
541         private final String mPin;
542 
SetIccLockEnabled(boolean state, String pin)543         private SetIccLockEnabled(boolean state, String pin) {
544             mState = state;
545             mPin = pin;
546         }
547 
548         @Override
doInBackground(Void... params)549         protected PinResult doInBackground(Void... params) {
550             mTelephonyManager =  mTelephonyManager.createForSubscriptionId(mSubId);
551             return mTelephonyManager.setIccLockEnabled(mState, mPin);
552         }
553 
554         @Override
onPostExecute(PinResult pinResult)555         protected void onPostExecute(PinResult pinResult) {
556             iccLockChanged(pinResult.getResult() == PinResult.PIN_RESULT_TYPE_SUCCESS /* success */,
557                     pinResult.getAttemptsRemaining() /* attemptsRemaining */);
558         }
559     }
560 
iccLockChanged(boolean success, int attemptsRemaining)561     private void iccLockChanged(boolean success, int attemptsRemaining) {
562         Log.d(TAG, "iccLockChanged: success = " + success);
563         if (success) {
564             mPinToggle.setChecked(mToState);
565         } else {
566             if (attemptsRemaining >= 0) {
567                 createCustomTextToast(getPinPasswordErrorMessage(attemptsRemaining));
568             } else {
569                 if (mToState) {
570                     Toast.makeText(getContext(), mRes.getString(
571                             R.string.sim_pin_enable_failed), Toast.LENGTH_LONG).show();
572                 } else {
573                     Toast.makeText(getContext(), mRes.getString(
574                             R.string.sim_pin_disable_failed), Toast.LENGTH_LONG).show();
575                 }
576             }
577         }
578         mPinToggle.setEnabled(true);
579         resetDialogState();
580     }
581 
createCustomTextToast(CharSequence errorMessage)582     private void createCustomTextToast(CharSequence errorMessage) {
583         // Cannot overlay Toast on PUK unlock screen.
584         // The window type of Toast is set by NotificationManagerService.
585         // It can't be overwritten by LayoutParams.type.
586         // Ovarlay a custom window with LayoutParams (TYPE_STATUS_BAR_PANEL) on PUK unlock screen.
587         final View v = ((LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE))
588                 .inflate(com.android.internal.R.layout.transient_notification, null);
589         final TextView tv = (TextView) v.findViewById(com.android.internal.R.id.message);
590         tv.setText(errorMessage);
591         tv.setSingleLine(false);
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 ChangeIccLockPin(mOldPin, mNewPin).execute();
645     }
646 
647     private class ChangeIccLockPin extends AsyncTask<Void, Void, PinResult> {
648         private final String mOldPin;
649         private final String mNewPin;
650 
ChangeIccLockPin(String oldPin, String newPin)651         private ChangeIccLockPin(String oldPin, String newPin) {
652             mOldPin = oldPin;
653             mNewPin = newPin;
654         }
655 
656         @Override
doInBackground(Void... params)657         protected PinResult doInBackground(Void... params) {
658             mTelephonyManager = mTelephonyManager.createForSubscriptionId(mSubId);
659             return mTelephonyManager.changeIccLockPin(mOldPin, mNewPin);
660         }
661 
662         @Override
onPostExecute(PinResult pinResult)663         protected void onPostExecute(PinResult pinResult) {
664             iccPinChanged(pinResult.getResult() == PinResult.PIN_RESULT_TYPE_SUCCESS /* success */,
665                     pinResult.getAttemptsRemaining() /* attemptsRemaining */);
666         }
667     }
668 
getPinPasswordErrorMessage(int attemptsRemaining)669     private String getPinPasswordErrorMessage(int attemptsRemaining) {
670         String displayMessage;
671 
672         if (attemptsRemaining == 0) {
673             displayMessage = mRes.getString(R.string.wrong_pin_code_pukked);
674         } else if (attemptsRemaining == 1) {
675             displayMessage = mRes.getString(R.string.wrong_pin_code_one, attemptsRemaining);
676         } else if (attemptsRemaining > 1) {
677             displayMessage = mRes
678                     .getQuantityString(R.plurals.wrong_pin_code, attemptsRemaining,
679                             attemptsRemaining);
680         } else {
681             displayMessage = mRes.getString(R.string.pin_failed);
682         }
683         if (DBG) Log.d(TAG, "getPinPasswordErrorMessage:"
684                 + " attemptsRemaining=" + attemptsRemaining + " displayMessage=" + displayMessage);
685         return displayMessage;
686     }
687 
reasonablePin(String pin)688     private boolean reasonablePin(String pin) {
689         if (pin == null || pin.length() < MIN_PIN_LENGTH || pin.length() > MAX_PIN_LENGTH) {
690             return false;
691         } else {
692             return true;
693         }
694     }
695 
resetDialogState()696     private void resetDialogState() {
697         mError = null;
698         mDialogState = ICC_OLD_MODE; // Default for when Change PIN is clicked
699         mPin = "";
700         setDialogValues();
701         mDialogState = OFF_MODE;
702     }
703 
getTagForSlotId(int slotId)704     private String getTagForSlotId(int slotId) {
705         return String.valueOf(slotId);
706     }
707 
getSlotIndexFromTag(String tag)708     private int getSlotIndexFromTag(String tag) {
709         int slotId = -1;
710         try {
711             slotId = Integer.parseInt(tag);
712         } catch (NumberFormatException exception) {
713         }
714         return slotId;
715     }
716 
getVisibleSubscriptionInfoForSimSlotIndex(int slotId)717     private SubscriptionInfo getVisibleSubscriptionInfoForSimSlotIndex(int slotId) {
718         final List<SubscriptionInfo> subInfoList =
719                 mProxySubscriptionMgr.getActiveSubscriptionsInfo();
720         if (subInfoList == null) {
721             return null;
722         }
723         final CarrierConfigManager carrierConfigManager = getContext().getSystemService(
724                 CarrierConfigManager.class);
725         for (SubscriptionInfo subInfo : subInfoList) {
726             if ((isSubscriptionVisible(carrierConfigManager, subInfo)
727                     && (subInfo.getSimSlotIndex() == slotId))) {
728                 return subInfo;
729             }
730         }
731         return null;
732     }
733 
isSubscriptionVisible(CarrierConfigManager carrierConfigManager, SubscriptionInfo subInfo)734     private boolean isSubscriptionVisible(CarrierConfigManager carrierConfigManager,
735             SubscriptionInfo subInfo) {
736         final PersistableBundle bundle = carrierConfigManager
737                 .getConfigForSubId(subInfo.getSubscriptionId());
738         if (bundle == null) {
739             return false;
740         }
741         return !bundle.getBoolean(CarrierConfigManager.KEY_HIDE_SIM_LOCK_SETTINGS_BOOL);
742     }
743 
744     private OnTabChangeListener mTabListener = new OnTabChangeListener() {
745         @Override
746         public void onTabChanged(String tabId) {
747             mSlotId = getSlotIndexFromTag(tabId);
748 
749             // The User has changed tab; update the body.
750             updatePreferences();
751         }
752     };
753 
754     private TabContentFactory mEmptyTabContent = new TabContentFactory() {
755         @Override
756         public View createTabContent(String tag) {
757             return new View(mTabHost.getContext());
758         }
759     };
760 
buildTabSpec(String tag, String title)761     private TabSpec buildTabSpec(String tag, String title) {
762         return mTabHost.newTabSpec(tag).setIndicator(title).setContent(
763                 mEmptyTabContent);
764     }
765 }
766