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