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