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