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