| /* | |
| * Copyright (C) 2008 The Android Open Source Project | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| package com.android.settings; | |
| import android.app.settings.SettingsEnums; | |
| import android.content.BroadcastReceiver; | |
| import android.content.Context; | |
| import android.content.Intent; | |
| import android.content.IntentFilter; | |
| import android.content.res.Configuration; | |
| import android.content.res.Resources; | |
| import android.graphics.PixelFormat; | |
| import android.os.AsyncTask; | |
| import android.os.Bundle; | |
| import android.os.Handler; | |
| import android.os.Message; | |
| import android.os.PersistableBundle; | |
| import android.telephony.CarrierConfigManager; | |
| import android.telephony.PinResult; | |
| import android.telephony.SubscriptionInfo; | |
| import android.telephony.SubscriptionManager; | |
| import android.telephony.TelephonyManager; | |
| import android.text.TextUtils; | |
| import android.util.Log; | |
| import android.view.Gravity; | |
| import android.view.LayoutInflater; | |
| import android.view.View; | |
| import android.view.ViewGroup; | |
| import android.view.WindowInsets.Type; | |
| import android.view.WindowManager; | |
| import android.widget.EditText; | |
| import android.widget.ListView; | |
| import android.widget.TabHost; | |
| import android.widget.TabHost.OnTabChangeListener; | |
| import android.widget.TabHost.TabContentFactory; | |
| import android.widget.TabHost.TabSpec; | |
| import android.widget.TabWidget; | |
| import android.widget.TextView; | |
| import android.widget.Toast; | |
| import androidx.annotation.Nullable; | |
| import androidx.preference.Preference; | |
| import androidx.preference.TwoStatePreference; | |
| import com.android.settings.network.ProxySubscriptionManager; | |
| import com.android.settings.network.SubscriptionUtil; | |
| import com.android.settings.network.telephony.MobileNetworkUtils; | |
| import com.android.settingslib.utils.StringUtil; | |
| import java.util.ArrayList; | |
| import java.util.List; | |
| /** | |
| * Implements the preference screen to enable/disable ICC lock and | |
| * also the dialogs to change the ICC PIN. In the former case, enabling/disabling | |
| * the ICC lock will prompt the user for the current PIN. | |
| * In the Change PIN case, it prompts the user for old pin, new pin and new pin | |
| * again before attempting to change it. Calls the SimCard interface to execute | |
| * these operations. | |
| * | |
| */ | |
| public class IccLockSettings extends SettingsPreferenceFragment | |
| implements EditPinPreference.OnPinEnteredListener { | |
| private static final String TAG = "IccLockSettings"; | |
| private static final boolean DBG = false; | |
| private static final int OFF_MODE = 0; | |
| // State when enabling/disabling ICC lock | |
| private static final int ICC_LOCK_MODE = 1; | |
| // State when entering the old pin | |
| private static final int ICC_OLD_MODE = 2; | |
| // State when entering the new pin - first time | |
| private static final int ICC_NEW_MODE = 3; | |
| // State when entering the new pin - second time | |
| private static final int ICC_REENTER_MODE = 4; | |
| // Keys in xml file | |
| private static final String PIN_DIALOG = "sim_pin"; | |
| private static final String PIN_TOGGLE = "sim_toggle"; | |
| // Keys in icicle | |
| private static final String DIALOG_SUB_ID = "dialogSubId"; | |
| private static final String DIALOG_STATE = "dialogState"; | |
| private static final String DIALOG_PIN = "dialogPin"; | |
| private static final String DIALOG_ERROR = "dialogError"; | |
| private static final String ENABLE_TO_STATE = "enableState"; | |
| private static final String CURRENT_TAB = "currentTab"; | |
| // Save and restore inputted PIN code when configuration changed | |
| // (ex. portrait<-->landscape) during change PIN code | |
| private static final String OLD_PINCODE = "oldPinCode"; | |
| private static final String NEW_PINCODE = "newPinCode"; | |
| private static final int MIN_PIN_LENGTH = 4; | |
| private static final int MAX_PIN_LENGTH = 8; | |
| // Which dialog to show next when popped up | |
| private int mDialogState = OFF_MODE; | |
| private String mPin; | |
| private String mOldPin; | |
| private String mNewPin; | |
| private String mError; | |
| // Are we trying to enable or disable ICC lock? | |
| private boolean mToState; | |
| private TabHost mTabHost; | |
| private TabWidget mTabWidget; | |
| private ListView mListView; | |
| private ProxySubscriptionManager mProxySubscriptionMgr; | |
| private EditPinPreference mPinDialog; | |
| private TwoStatePreference mPinToggle; | |
| private Resources mRes; | |
| // For async handler to identify request type | |
| private static final int MSG_SIM_STATE_CHANGED = 102; | |
| // @see android.widget.Toast$TN | |
| private static final long LONG_DURATION_TIMEOUT = 7000; | |
| private int mSlotId = -1; | |
| private int mSubId; | |
| private TelephonyManager mTelephonyManager; | |
| // For replies from IccCard interface | |
| private Handler mHandler = new Handler() { | |
| public void handleMessage(Message msg) { | |
| switch (msg.what) { | |
| case MSG_SIM_STATE_CHANGED: | |
| updatePreferences(); | |
| break; | |
| } | |
| return; | |
| } | |
| }; | |
| private final BroadcastReceiver mSimStateReceiver = new BroadcastReceiver() { | |
| public void onReceive(Context context, Intent intent) { | |
| final String action = intent.getAction(); | |
| if (Intent.ACTION_SIM_STATE_CHANGED.equals(action)) { | |
| mHandler.sendMessage(mHandler.obtainMessage(MSG_SIM_STATE_CHANGED)); | |
| } | |
| } | |
| }; | |
| // For top-level settings screen to query | |
| private boolean isIccLockEnabled() { | |
| mTelephonyManager = mTelephonyManager.createForSubscriptionId(mSubId); | |
| return mTelephonyManager.isIccLockEnabled(); | |
| } | |
| private String getSummary(Context context) { | |
| final Resources res = context.getResources(); | |
| final String summary = isIccLockEnabled() | |
| ? res.getString(R.string.sim_lock_on) | |
| : res.getString(R.string.sim_lock_off); | |
| return summary; | |
| } | |
| @Override | |
| public void onCreate(Bundle savedInstanceState) { | |
| super.onCreate(savedInstanceState); | |
| if (Utils.isMonkeyRunning() || | |
| !SubscriptionUtil.isSimHardwareVisible(getContext()) || | |
| MobileNetworkUtils.isMobileNetworkUserRestricted(getContext())) { | |
| finish(); | |
| return; | |
| } | |
| // enable ProxySubscriptionMgr with Lifecycle support for all controllers | |
| // live within this fragment | |
| mProxySubscriptionMgr = ProxySubscriptionManager.getInstance(getContext()); | |
| mProxySubscriptionMgr.setLifecycle(getLifecycle()); | |
| mTelephonyManager = getContext().getSystemService(TelephonyManager.class); | |
| addPreferencesFromResource(R.xml.sim_lock_settings); | |
| mPinDialog = (EditPinPreference) findPreference(PIN_DIALOG); | |
| mPinToggle = (TwoStatePreference) findPreference(PIN_TOGGLE); | |
| if (savedInstanceState != null) { | |
| if (savedInstanceState.containsKey(DIALOG_STATE) | |
| && restoreDialogStates(savedInstanceState)) { | |
| Log.d(TAG, "onCreate: restore dialog for slotId=" + mSlotId + ", subId=" + mSubId); | |
| } else if (savedInstanceState.containsKey(CURRENT_TAB) | |
| && restoreTabFocus(savedInstanceState)) { | |
| Log.d(TAG, "onCreate: restore focus on slotId=" + mSlotId + ", subId=" + mSubId); | |
| } | |
| } | |
| mPinDialog.setOnPinEnteredListener(this); | |
| // Don't need any changes to be remembered | |
| getPreferenceScreen().setPersistent(false); | |
| mRes = getResources(); | |
| } | |
| private boolean restoreDialogStates(Bundle savedInstanceState) { | |
| final SubscriptionInfo subInfo = mProxySubscriptionMgr | |
| .getActiveSubscriptionInfo(savedInstanceState.getInt(DIALOG_SUB_ID)); | |
| if (subInfo == null) { | |
| return false; | |
| } | |
| final SubscriptionInfo visibleSubInfo = getVisibleSubscriptionInfoForSimSlotIndex( | |
| subInfo.getSimSlotIndex()); | |
| if (visibleSubInfo == null) { | |
| return false; | |
| } | |
| if (visibleSubInfo.getSubscriptionId() != subInfo.getSubscriptionId()) { | |
| return false; | |
| } | |
| mSlotId = subInfo.getSimSlotIndex(); | |
| mSubId = subInfo.getSubscriptionId(); | |
| mDialogState = savedInstanceState.getInt(DIALOG_STATE); | |
| mPin = savedInstanceState.getString(DIALOG_PIN); | |
| mError = savedInstanceState.getString(DIALOG_ERROR); | |
| mToState = savedInstanceState.getBoolean(ENABLE_TO_STATE); | |
| // Restore inputted PIN code | |
| switch (mDialogState) { | |
| case ICC_NEW_MODE: | |
| mOldPin = savedInstanceState.getString(OLD_PINCODE); | |
| break; | |
| case ICC_REENTER_MODE: | |
| mOldPin = savedInstanceState.getString(OLD_PINCODE); | |
| mNewPin = savedInstanceState.getString(NEW_PINCODE); | |
| break; | |
| } | |
| return true; | |
| } | |
| private boolean restoreTabFocus(Bundle savedInstanceState) { | |
| int slotId = 0; | |
| try { | |
| slotId = Integer.parseInt(savedInstanceState.getString(CURRENT_TAB)); | |
| } catch (NumberFormatException exception) { | |
| return false; | |
| } | |
| final SubscriptionInfo subInfo = getVisibleSubscriptionInfoForSimSlotIndex(slotId); | |
| if (subInfo == null) { | |
| return false; | |
| } | |
| mSlotId = subInfo.getSimSlotIndex(); | |
| mSubId = subInfo.getSubscriptionId(); | |
| if (mTabHost != null) { | |
| mTabHost.setCurrentTabByTag(getTagForSlotId(mSlotId)); | |
| } | |
| return true; | |
| } | |
| @Override | |
| public View onCreateView(LayoutInflater inflater, ViewGroup container, | |
| Bundle savedInstanceState) { | |
| final int numSims = mProxySubscriptionMgr.getActiveSubscriptionInfoCountMax(); | |
| final List<SubscriptionInfo> componenterList = new ArrayList<>(); | |
| for (int i = 0; i < numSims; ++i) { | |
| final SubscriptionInfo subInfo = getVisibleSubscriptionInfoForSimSlotIndex(i); | |
| if (subInfo != null) { | |
| componenterList.add(subInfo); | |
| } | |
| } | |
| if (componenterList.size() == 0) { | |
| Log.e(TAG, "onCreateView: no sim info"); | |
| return super.onCreateView(inflater, container, savedInstanceState); | |
| } | |
| if (mSlotId < 0) { | |
| mSlotId = componenterList.get(0).getSimSlotIndex(); | |
| mSubId = componenterList.get(0).getSubscriptionId(); | |
| Log.d(TAG, "onCreateView: default slotId=" + mSlotId + ", subId=" + mSubId); | |
| } | |
| if (componenterList.size() > 1) { | |
| final View view = inflater.inflate(R.layout.icc_lock_tabs, container, false); | |
| final ViewGroup prefs_container = (ViewGroup) view.findViewById(R.id.prefs_container); | |
| Utils.prepareCustomPreferencesList(container, view, prefs_container, false); | |
| final View prefs = super.onCreateView(inflater, prefs_container, savedInstanceState); | |
| prefs_container.addView(prefs); | |
| mTabHost = (TabHost) view.findViewById(android.R.id.tabhost); | |
| mTabWidget = (TabWidget) view.findViewById(android.R.id.tabs); | |
| mListView = (ListView) view.findViewById(android.R.id.list); | |
| mTabHost.setup(); | |
| mTabHost.clearAllTabs(); | |
| for (SubscriptionInfo subInfo : componenterList) { | |
| final int slot = subInfo.getSimSlotIndex(); | |
| final String tag = getTagForSlotId(slot); | |
| mTabHost.addTab(buildTabSpec(tag, | |
| String.valueOf(subInfo == null | |
| ? getContext().getString(R.string.sim_editor_title, slot + 1) | |
| : SubscriptionUtil.getUniqueSubscriptionDisplayName( | |
| subInfo, getContext())))); | |
| } | |
| mTabHost.setCurrentTabByTag(getTagForSlotId(mSlotId)); | |
| mTabHost.setOnTabChangedListener(mTabListener); | |
| return view; | |
| } else { | |
| return super.onCreateView(inflater, container, savedInstanceState); | |
| } | |
| } | |
| @Override | |
| public void onViewCreated(View view, Bundle savedInstanceState) { | |
| super.onViewCreated(view, savedInstanceState); | |
| updatePreferences(); | |
| } | |
| private void updatePreferences() { | |
| final SubscriptionInfo sir = getVisibleSubscriptionInfoForSimSlotIndex(mSlotId); | |
| final int subId = (sir != null) ? sir.getSubscriptionId() | |
| : SubscriptionManager.INVALID_SUBSCRIPTION_ID; | |
| if (mSubId != subId) { | |
| mSubId = subId; | |
| resetDialogState(); | |
| if ((mPinDialog != null) && mPinDialog.isDialogOpen()) { | |
| mPinDialog.getDialog().dismiss(); | |
| } | |
| } | |
| if (mPinDialog != null) { | |
| mPinDialog.setEnabled(sir != null); | |
| } | |
| if (mPinToggle != null) { | |
| mPinToggle.setEnabled(sir != null); | |
| if (sir != null) { | |
| mPinToggle.setChecked(isIccLockEnabled()); | |
| } | |
| } | |
| } | |
| @Override | |
| public int getMetricsCategory() { | |
| return SettingsEnums.ICC_LOCK; | |
| } | |
| @Override | |
| public void onResume() { | |
| super.onResume(); | |
| // ACTION_SIM_STATE_CHANGED is sticky, so we'll receive current state after this call, | |
| // which will call updatePreferences(). | |
| final IntentFilter filter = new IntentFilter(Intent.ACTION_SIM_STATE_CHANGED); | |
| getContext().registerReceiver(mSimStateReceiver, filter); | |
| if (mDialogState != OFF_MODE) { | |
| showPinDialog(); | |
| } else { | |
| // Prep for standard click on "Change PIN" | |
| resetDialogState(); | |
| } | |
| } | |
| @Override | |
| public void onPause() { | |
| super.onPause(); | |
| getContext().unregisterReceiver(mSimStateReceiver); | |
| } | |
| @Override | |
| public int getHelpResource() { | |
| return R.string.help_url_icc_lock; | |
| } | |
| @Override | |
| public void onSaveInstanceState(Bundle out) { | |
| // Need to store this state for slider open/close | |
| // There is one case where the dialog is popped up by the preference | |
| // framework. In that case, let the preference framework store the | |
| // dialog state. In other cases, where this activity manually launches | |
| // the dialog, store the state of the dialog. | |
| if (mPinDialog.isDialogOpen()) { | |
| out.putInt(DIALOG_SUB_ID, mSubId); | |
| out.putInt(DIALOG_STATE, mDialogState); | |
| out.putString(DIALOG_PIN, mPinDialog.getEditText().getText().toString()); | |
| out.putString(DIALOG_ERROR, mError); | |
| out.putBoolean(ENABLE_TO_STATE, mToState); | |
| // Save inputted PIN code | |
| switch (mDialogState) { | |
| case ICC_NEW_MODE: | |
| out.putString(OLD_PINCODE, mOldPin); | |
| break; | |
| case ICC_REENTER_MODE: | |
| out.putString(OLD_PINCODE, mOldPin); | |
| out.putString(NEW_PINCODE, mNewPin); | |
| break; | |
| } | |
| } else { | |
| super.onSaveInstanceState(out); | |
| } | |
| if (mTabHost != null) { | |
| out.putString(CURRENT_TAB, mTabHost.getCurrentTabTag()); | |
| } | |
| } | |
| private void showPinDialog() { | |
| if (mDialogState == OFF_MODE) { | |
| return; | |
| } | |
| setDialogValues(); | |
| mPinDialog.showPinDialog(); | |
| final EditText editText = mPinDialog.getEditText(); | |
| if (!TextUtils.isEmpty(mPin) && editText != null) { | |
| editText.setSelection(mPin.length()); | |
| } | |
| } | |
| private void setDialogValues() { | |
| mPinDialog.setText(mPin); | |
| String message = ""; | |
| switch (mDialogState) { | |
| case ICC_LOCK_MODE: | |
| message = mRes.getString(R.string.sim_enter_pin); | |
| mPinDialog.setDialogTitle(mToState | |
| ? mRes.getString(R.string.sim_enable_sim_lock) | |
| : mRes.getString(R.string.sim_disable_sim_lock)); | |
| break; | |
| case ICC_OLD_MODE: | |
| message = mRes.getString(R.string.sim_enter_old); | |
| mPinDialog.setDialogTitle(mRes.getString(R.string.sim_change_pin)); | |
| break; | |
| case ICC_NEW_MODE: | |
| message = mRes.getString(R.string.sim_enter_new); | |
| mPinDialog.setDialogTitle(mRes.getString(R.string.sim_change_pin)); | |
| break; | |
| case ICC_REENTER_MODE: | |
| message = mRes.getString(R.string.sim_reenter_new); | |
| mPinDialog.setDialogTitle(mRes.getString(R.string.sim_change_pin)); | |
| break; | |
| } | |
| if (mError != null) { | |
| message = mError + "\n" + message; | |
| mError = null; | |
| } | |
| mPinDialog.setDialogMessage(message); | |
| } | |
| @Override | |
| public void onPinEntered(EditPinPreference preference, boolean positiveResult) { | |
| if (!positiveResult) { | |
| resetDialogState(); | |
| return; | |
| } | |
| mPin = preference.getText(); | |
| if (!reasonablePin(mPin)) { | |
| // inject error message and display dialog again | |
| mError = mRes.getString(R.string.sim_invalid_pin_hint); | |
| showPinDialog(); | |
| return; | |
| } | |
| switch (mDialogState) { | |
| case ICC_LOCK_MODE: | |
| tryChangeIccLockState(); | |
| break; | |
| case ICC_OLD_MODE: | |
| mOldPin = mPin; | |
| mDialogState = ICC_NEW_MODE; | |
| mError = null; | |
| mPin = null; | |
| showPinDialog(); | |
| break; | |
| case ICC_NEW_MODE: | |
| mNewPin = mPin; | |
| mDialogState = ICC_REENTER_MODE; | |
| mPin = null; | |
| showPinDialog(); | |
| break; | |
| case ICC_REENTER_MODE: | |
| if (!mPin.equals(mNewPin)) { | |
| mError = mRes.getString(R.string.sim_pins_dont_match); | |
| mDialogState = ICC_NEW_MODE; | |
| mPin = null; | |
| showPinDialog(); | |
| } else { | |
| mError = null; | |
| tryChangePin(); | |
| } | |
| break; | |
| } | |
| } | |
| @Override | |
| public boolean onPreferenceTreeClick(Preference preference) { | |
| if (preference == mPinToggle) { | |
| // Get the new, preferred state | |
| mToState = mPinToggle.isChecked(); | |
| // Flip it back and pop up pin dialog | |
| mPinToggle.setChecked(!mToState); | |
| mDialogState = ICC_LOCK_MODE; | |
| showPinDialog(); | |
| } else if (preference == mPinDialog) { | |
| mDialogState = ICC_OLD_MODE; | |
| return false; | |
| } | |
| return true; | |
| } | |
| private void tryChangeIccLockState() { | |
| // Try to change icc lock. If it succeeds, toggle the lock state and | |
| // reset dialog state. Else inject error message and show dialog again. | |
| new SetIccLockEnabled(mToState, mPin).execute(); | |
| // Disable the setting till the response is received. | |
| mPinToggle.setEnabled(false); | |
| } | |
| private class SetIccLockEnabled extends AsyncTask<Void, Void, PinResult> { | |
| private final boolean mState; | |
| private final String mPin; | |
| private SetIccLockEnabled(boolean state, String pin) { | |
| mState = state; | |
| mPin = pin; | |
| } | |
| @Override | |
| protected PinResult doInBackground(Void... params) { | |
| mTelephonyManager = mTelephonyManager.createForSubscriptionId(mSubId); | |
| return mTelephonyManager.setIccLockEnabled(mState, mPin); | |
| } | |
| @Override | |
| protected void onPostExecute(PinResult pinResult) { | |
| iccLockChanged(pinResult.getResult() == PinResult.PIN_RESULT_TYPE_SUCCESS /* success */, | |
| pinResult.getAttemptsRemaining() /* attemptsRemaining */); | |
| } | |
| } | |
| private void iccLockChanged(boolean success, int attemptsRemaining) { | |
| Log.d(TAG, "iccLockChanged: success = " + success); | |
| if (success) { | |
| mPinToggle.setChecked(mToState); | |
| } else { | |
| if (attemptsRemaining >= 0) { | |
| createCustomTextToast(getPinPasswordErrorMessage(attemptsRemaining)); | |
| } else { | |
| if (mToState) { | |
| Toast.makeText(getContext(), mRes.getString( | |
| R.string.sim_pin_enable_failed), Toast.LENGTH_LONG).show(); | |
| } else { | |
| Toast.makeText(getContext(), mRes.getString( | |
| R.string.sim_pin_disable_failed), Toast.LENGTH_LONG).show(); | |
| } | |
| } | |
| } | |
| mPinToggle.setEnabled(true); | |
| resetDialogState(); | |
| } | |
| private void createCustomTextToast(CharSequence errorMessage) { | |
| // Cannot overlay Toast on PUK unlock screen. | |
| // The window type of Toast is set by NotificationManagerService. | |
| // It can't be overwritten by LayoutParams.type. | |
| // Ovarlay a custom window with LayoutParams (TYPE_STATUS_BAR_PANEL) on PUK unlock screen. | |
| final View v = ((LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE)) | |
| .inflate(com.android.internal.R.layout.transient_notification, null); | |
| final TextView tv = (TextView) v.findViewById(com.android.internal.R.id.message); | |
| tv.setText(errorMessage); | |
| tv.setSingleLine(false); | |
| final WindowManager.LayoutParams params = new WindowManager.LayoutParams(); | |
| final Configuration config = v.getContext().getResources().getConfiguration(); | |
| final int gravity = Gravity.getAbsoluteGravity( | |
| getContext().getResources().getInteger( | |
| com.android.internal.R.integer.config_toastDefaultGravity), | |
| config.getLayoutDirection()); | |
| params.gravity = gravity; | |
| if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) { | |
| params.horizontalWeight = 1.0f; | |
| } | |
| if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) { | |
| params.verticalWeight = 1.0f; | |
| } | |
| params.y = getContext().getResources().getDimensionPixelSize( | |
| com.android.internal.R.dimen.toast_y_offset); | |
| params.height = WindowManager.LayoutParams.WRAP_CONTENT; | |
| params.width = WindowManager.LayoutParams.WRAP_CONTENT; | |
| params.format = PixelFormat.TRANSLUCENT; | |
| params.windowAnimations = com.android.internal.R.style.Animation_Toast; | |
| params.type = WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL; | |
| params.setFitInsetsTypes(params.getFitInsetsTypes() & ~Type.statusBars()); | |
| params.setTitle(errorMessage); | |
| params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | |
| | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | |
| | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; | |
| final WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE); | |
| wm.addView(v, params); | |
| mHandler.postDelayed(new Runnable() { | |
| @Override | |
| public void run() { | |
| wm.removeViewImmediate(v); | |
| } | |
| }, LONG_DURATION_TIMEOUT); | |
| } | |
| private void iccPinChanged(boolean success, int attemptsRemaining) { | |
| Log.d(TAG, "iccPinChanged: success = " + success); | |
| if (!success) { | |
| createCustomTextToast(getPinPasswordErrorMessage(attemptsRemaining)); | |
| } else { | |
| Toast.makeText(getContext(), mRes.getString(R.string.sim_change_succeeded), | |
| Toast.LENGTH_SHORT) | |
| .show(); | |
| } | |
| resetDialogState(); | |
| } | |
| private void tryChangePin() { | |
| new ChangeIccLockPin(mOldPin, mNewPin).execute(); | |
| } | |
| private class ChangeIccLockPin extends AsyncTask<Void, Void, PinResult> { | |
| private final String mOldPin; | |
| private final String mNewPin; | |
| private ChangeIccLockPin(String oldPin, String newPin) { | |
| mOldPin = oldPin; | |
| mNewPin = newPin; | |
| } | |
| @Override | |
| protected PinResult doInBackground(Void... params) { | |
| mTelephonyManager = mTelephonyManager.createForSubscriptionId(mSubId); | |
| return mTelephonyManager.changeIccLockPin(mOldPin, mNewPin); | |
| } | |
| @Override | |
| protected void onPostExecute(PinResult pinResult) { | |
| iccPinChanged(pinResult.getResult() == PinResult.PIN_RESULT_TYPE_SUCCESS /* success */, | |
| pinResult.getAttemptsRemaining() /* attemptsRemaining */); | |
| } | |
| } | |
| private String getPinPasswordErrorMessage(int attemptsRemaining) { | |
| String displayMessage; | |
| if (attemptsRemaining == 0) { | |
| displayMessage = mRes.getString(R.string.wrong_pin_code_pukked); | |
| } else if (attemptsRemaining == 1) { | |
| displayMessage = mRes.getString(R.string.wrong_pin_code_one, attemptsRemaining); | |
| } else if (attemptsRemaining > 1) { | |
| displayMessage = StringUtil.getIcuPluralsString(getPrefContext(), attemptsRemaining, | |
| R.string.wrong_pin_code); | |
| } else { | |
| displayMessage = mRes.getString(R.string.pin_failed); | |
| } | |
| if (DBG) Log.d(TAG, "getPinPasswordErrorMessage:" | |
| + " attemptsRemaining=" + attemptsRemaining + " displayMessage=" + displayMessage); | |
| return displayMessage; | |
| } | |
| private boolean reasonablePin(String pin) { | |
| if (pin == null || pin.length() < MIN_PIN_LENGTH || pin.length() > MAX_PIN_LENGTH) { | |
| return false; | |
| } else { | |
| return true; | |
| } | |
| } | |
| private void resetDialogState() { | |
| mError = null; | |
| mDialogState = ICC_OLD_MODE; // Default for when Change PIN is clicked | |
| mPin = ""; | |
| setDialogValues(); | |
| mDialogState = OFF_MODE; | |
| } | |
| private String getTagForSlotId(int slotId) { | |
| return String.valueOf(slotId); | |
| } | |
| private int getSlotIndexFromTag(String tag) { | |
| int slotId = -1; | |
| try { | |
| slotId = Integer.parseInt(tag); | |
| } catch (NumberFormatException exception) { | |
| } | |
| return slotId; | |
| } | |
| @Nullable | |
| private SubscriptionInfo getVisibleSubscriptionInfoForSimSlotIndex(int slotId) { | |
| final List<SubscriptionInfo> subInfoList = | |
| mProxySubscriptionMgr.getActiveSubscriptionsInfo(); | |
| if (subInfoList == null) { | |
| return null; | |
| } | |
| Context context = getContext(); | |
| if (context == null) { | |
| return null; | |
| } | |
| final CarrierConfigManager carrierConfigManager = context.getSystemService( | |
| CarrierConfigManager.class); | |
| for (SubscriptionInfo subInfo : subInfoList) { | |
| if ((isSubscriptionVisible(carrierConfigManager, subInfo) | |
| && (subInfo.getSimSlotIndex() == slotId))) { | |
| return subInfo; | |
| } | |
| } | |
| return null; | |
| } | |
| private boolean isSubscriptionVisible(CarrierConfigManager carrierConfigManager, | |
| SubscriptionInfo subInfo) { | |
| final PersistableBundle bundle = carrierConfigManager | |
| .getConfigForSubId(subInfo.getSubscriptionId()); | |
| if (bundle == null) { | |
| return false; | |
| } | |
| return !bundle.getBoolean(CarrierConfigManager.KEY_HIDE_SIM_LOCK_SETTINGS_BOOL); | |
| } | |
| private OnTabChangeListener mTabListener = new OnTabChangeListener() { | |
| @Override | |
| public void onTabChanged(String tabId) { | |
| mSlotId = getSlotIndexFromTag(tabId); | |
| // The User has changed tab; update the body. | |
| updatePreferences(); | |
| } | |
| }; | |
| private TabContentFactory mEmptyTabContent = new TabContentFactory() { | |
| @Override | |
| public View createTabContent(String tag) { | |
| return new View(mTabHost.getContext()); | |
| } | |
| }; | |
| private TabSpec buildTabSpec(String tag, String title) { | |
| return mTabHost.newTabSpec(tag).setIndicator(title).setContent( | |
| mEmptyTabContent); | |
| } | |
| } |