/* * Copyright (C) 2014 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.sim; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.app.ProgressDialog; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.content.res.Resources; import android.graphics.drawable.BitmapDrawable; import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemProperties; import android.preference.Preference; import android.preference.PreferenceScreen; import android.preference.PreferenceCategory; import android.provider.SearchIndexableResource; import android.provider.Settings; import android.telephony.PhoneStateListener; import android.telephony.SmsManager; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.telecom.PhoneAccount; import android.telecom.PhoneAccountHandle; import android.telecom.TelecomManager; import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.CompoundButton; import android.widget.CompoundButton.OnCheckedChangeListener; import com.android.internal.logging.MetricsLogger; import com.android.internal.telephony.PhoneConstants; import com.android.settings.RestrictedSettingsFragment; import com.android.settings.Utils; import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.search.Indexable; import com.android.settings.R; import com.android.internal.telephony.IExtTelephony; import com.android.internal.telephony.TelephonyProperties; import java.util.ArrayList; import java.util.List; public class SimSettings extends RestrictedSettingsFragment implements Indexable { private static final String TAG = "SimSettings"; private static final boolean DBG = false; // These are the list of possible values that // IExtTelephony.getCurrentUiccCardProvisioningStatus() can return private static final int PROVISIONED = 1; private static final int NOT_PROVISIONED = 0; private static final int INVALID_STATE = -1; private static final int CARD_NOT_PRESENT = -2; private static final String DISALLOW_CONFIG_SIM = "no_config_sim"; private static final String SIM_CARD_CATEGORY = "sim_cards"; private static final String KEY_CELLULAR_DATA = "sim_cellular_data"; private static final String KEY_CALLS = "sim_calls"; private static final String KEY_SMS = "sim_sms"; public static final String EXTRA_SLOT_ID = "slot_id"; private static final String SIM_ACTIVITIES_CATEGORY = "sim_activities"; private static final String MOBILE_NETWORK_CATEGORY = "mobile_network"; private static final String KEY_PRIMARY_SUB_SELECT = "select_primary_sub"; private IExtTelephony mExtTelephony = IExtTelephony.Stub. asInterface(ServiceManager.getService("extphone")); /** * By UX design we use only one Subscription Information(SubInfo) record per SIM slot. * mAvalableSubInfos is the list of SubInfos we present to the user. * mSubInfoList is the list of all SubInfos. * mSelectableSubInfos is the list of SubInfos that a user can select for data, calls, and SMS. */ private List mAvailableSubInfos = null; private List mSubInfoList = null; private List mSelectableSubInfos = null; private PreferenceCategory mSimCards = null; private PreferenceCategory mMobileNetwork; private SubscriptionManager mSubscriptionManager; private int mNumSlots; private Context mContext; private static AlertDialog sAlertDialog = null; private static ProgressDialog sProgressDialog = null; private boolean needUpdate = false; private int mPhoneCount = TelephonyManager.getDefault().getPhoneCount(); private int[] mUiccProvisionStatus = new int[mPhoneCount]; private Preference mPrimarySubSelect = null; private int[] mCallState = new int[mPhoneCount]; private PhoneStateListener[] mPhoneStateListener = new PhoneStateListener[mPhoneCount]; private static final String ACTION_UICC_MANUAL_PROVISION_STATUS_CHANGED = "org.codeaurora.intent.action.ACTION_UICC_MANUAL_PROVISION_STATUS_CHANGED"; private static final String EXTRA_NEW_PROVISION_STATE = "newProvisionState"; private static final String CONFIG_LTE_SUB_SELECT_MODE = "config_lte_sub_select_mode"; private static final String CONFIG_PRIMARY_SUB_SETABLE = "config_primary_sub_setable"; private static final String CONFIG_CURRENT_PRIMARY_SUB = "config_current_primary_sub"; // If this config set to '1' DDS option would be greyed out on UI. // For more info pls refere framework code. private static final String CONFIG_DISABLE_DDS_PREFERENCE = "config_disable_dds_preference"; public SimSettings() { super(DISALLOW_CONFIG_SIM); } @Override protected int getMetricsCategory() { return MetricsLogger.SIM; } @Override public void onCreate(final Bundle bundle) { super.onCreate(bundle); mContext = getActivity(); mSubscriptionManager = SubscriptionManager.from(getActivity()); final TelephonyManager tm = (TelephonyManager) getActivity().getSystemService(Context.TELEPHONY_SERVICE); addPreferencesFromResource(R.xml.sim_settings); mPrimarySubSelect = (Preference) findPreference(KEY_PRIMARY_SUB_SELECT); mNumSlots = tm.getSimCount(); mSimCards = (PreferenceCategory)findPreference(SIM_CARD_CATEGORY); mMobileNetwork = (PreferenceCategory) findPreference(MOBILE_NETWORK_CATEGORY); mAvailableSubInfos = new ArrayList(mNumSlots); mSelectableSubInfos = new ArrayList(); SimSelectNotification.cancelNotification(getActivity()); IntentFilter intentFilter = new IntentFilter(ACTION_UICC_MANUAL_PROVISION_STATUS_CHANGED); mContext.registerReceiver(mReceiver, intentFilter); } @Override public void onDestroy() { mContext.unregisterReceiver(mReceiver); Log.d(TAG,"on onDestroy"); super.onDestroy(); } private final SubscriptionManager.OnSubscriptionsChangedListener mOnSubscriptionsChangeListener = new SubscriptionManager.OnSubscriptionsChangedListener() { @Override public void onSubscriptionsChanged() { if (DBG) log("onSubscriptionsChanged:"); Activity activity = getActivity(); if (activity != null && !activity.isFinishing()) { updateSubscriptions(); } } }; private void updateSubscriptions() { mSubInfoList = mSubscriptionManager.getActiveSubscriptionInfoList(); for (int i = 0; i < mNumSlots; ++i) { Preference pref = mSimCards.findPreference("sim" + i); if (pref instanceof SimPreference) { mSimCards.removePreference(pref); } } mMobileNetwork.removeAll(); mAvailableSubInfos.clear(); mSelectableSubInfos.clear(); for (int i = 0; i < mNumSlots; ++i) { final SubscriptionInfo sir = mSubscriptionManager .getActiveSubscriptionInfoForSimSlotIndex(i); int subscriptionId = sir != null ? sir.getSubscriptionId() : SubscriptionManager.INVALID_SUBSCRIPTION_ID; SimPreference simPreference = new SimEnablerPreference(mContext, sir, i); simPreference.setOrder(i-mNumSlots); mSimCards.addPreference(simPreference); mAvailableSubInfos.add(sir); if (sir != null && (isSubProvisioned(i))) { mSelectableSubInfos.add(sir); } Intent mobileNetworkIntent = new Intent(); mobileNetworkIntent.setComponent(new ComponentName( "com.android.phone", "com.android.phone.MobileNetworkSettings")); SubscriptionManager.putPhoneIdAndSubIdExtra(mobileNetworkIntent, i, subscriptionId); Preference mobileNetworkPref = new Preference(getActivity()); mobileNetworkPref.setTitle( getString(R.string.sim_mobile_network_settings_title, (i + 1))); mobileNetworkPref.setIntent(mobileNetworkIntent); mobileNetworkPref.setEnabled( subscriptionId != SubscriptionManager.INVALID_SUBSCRIPTION_ID); mMobileNetwork.addPreference(mobileNetworkPref); } updateAllOptions(); } private void updateAllOptions() { updateSimSlotValues(); updateActivitesCategory(); } private void updateSimSlotValues() { final int prefSize = mSimCards.getPreferenceCount(); for (int i = 0; i < prefSize; ++i) { Preference pref = mSimCards.getPreference(i); if (pref instanceof SimPreference) { ((SimPreference)pref).update(); } } } private void updateActivitesCategory() { updateCellularDataValues(); updateCallValues(); updateSmsValues(); } private void updateSmsValues() { final Preference simPref = findPreference(KEY_SMS); simPref.setTitle(R.string.sms_messages_title); boolean isSMSPrompt = false; SubscriptionInfo sir = mSubscriptionManager.getActiveSubscriptionInfo( mSubscriptionManager.getDefaultSmsSubId()); try { isSMSPrompt = mExtTelephony.isSMSPromptEnabled(); } catch (RemoteException ex) { loge("RemoteException @isSMSPromptEnabled" + ex); } catch (NullPointerException ex) { loge("NullPointerException @isSMSPromptEnabled" + ex); } // External telephony interfaces may not exist, fall back to our impl if (mExtTelephony == null) { isSMSPrompt = SmsManager.getDefault().isSMSPromptEnabled(); } log("[updateSmsValues] isSMSPrompt: " + isSMSPrompt); if (isSMSPrompt || sir == null) { simPref.setSummary(mContext.getResources().getString( R.string.sim_calls_ask_first_prefs_title)); } else { simPref.setSummary(sir.getDisplayName()); } simPref.setEnabled(mSelectableSubInfos.size() > 1); } private void updateCellularDataValues() { final Preference simPref = findPreference(KEY_CELLULAR_DATA); final SubscriptionInfo sir = mSubscriptionManager.getDefaultDataSubscriptionInfo(); simPref.setTitle(R.string.cellular_data_title); if (DBG) log("[updateCellularDataValues] mSubInfoList=" + mSubInfoList); if (sir != null) { simPref.setSummary(sir.getDisplayName()); } else if (sir == null) { simPref.setSummary(R.string.sim_selection_required_pref); } boolean callStateIdle = isCallStateIdle(); final boolean ecbMode = SystemProperties.getBoolean( TelephonyProperties.PROPERTY_INECM_MODE, false); // Enable data preference in msim mode and call state idle simPref.setEnabled((mSelectableSubInfos.size() > 1) && !disableDds() && callStateIdle && !ecbMode); } private void updateCallValues() { final Preference simPref = findPreference(KEY_CALLS); final TelecomManager telecomManager = TelecomManager.from(mContext); final PhoneAccountHandle phoneAccountHandle = telecomManager.getUserSelectedOutgoingPhoneAccount(); final List allPhoneAccounts = telecomManager.getCallCapablePhoneAccounts(); simPref.setTitle(R.string.calls_title); PhoneAccount phoneAccount = null; if (phoneAccountHandle != null) { phoneAccount = telecomManager.getPhoneAccount(phoneAccountHandle); } simPref.setSummary(phoneAccount == null ? mContext.getResources().getString(R.string.sim_calls_ask_first_prefs_title) : (String)phoneAccount.getLabel()); simPref.setEnabled(allPhoneAccounts.size() > 1); } @Override public void onResume() { super.onResume(); mSubscriptionManager.addOnSubscriptionsChangedListener(mOnSubscriptionsChangeListener); initLTEPreference(); updateSubscriptions(); listen(); } @Override public void onPause() { super.onPause(); mSubscriptionManager.removeOnSubscriptionsChangedListener(mOnSubscriptionsChangeListener); unRegisterPhoneStateListener(); for (int i = 0; i < mSimCards.getPreferenceCount(); ++i) { Preference pref = mSimCards.getPreference(i); if (pref instanceof SimEnablerPreference) { // Calling cleanUp() here to dismiss/cleanup any pending dialog exists. ((SimEnablerPreference)pref).cleanUpPendingDialogs(); } } } @Override public boolean onPreferenceTreeClick(final PreferenceScreen preferenceScreen, final Preference preference) { final Context context = mContext; Intent intent = new Intent(context, SimDialogActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); if (preference instanceof SimPreference) { Intent newIntent = new Intent(context, SimPreferenceDialog.class); newIntent.putExtra(EXTRA_SLOT_ID, ((SimPreference)preference).getSlotId()); startActivity(newIntent); return true; } else if (findPreference(KEY_CELLULAR_DATA) == preference) { intent.putExtra(SimDialogActivity.DIALOG_TYPE_KEY, SimDialogActivity.DATA_PICK); context.startActivity(intent); return true; } else if (findPreference(KEY_CALLS) == preference) { intent.putExtra(SimDialogActivity.DIALOG_TYPE_KEY, SimDialogActivity.CALLS_PICK); context.startActivity(intent); return true; } else if (findPreference(KEY_SMS) == preference) { intent.putExtra(SimDialogActivity.DIALOG_TYPE_KEY, SimDialogActivity.SMS_PICK); context.startActivity(intent); return true; } return false; } private void loge(String msg) { if (DBG) Log.e(TAG + "message", msg); } private void simEnablerUpdate() { if (isAdded()) { updateAllOptions(); } else { needUpdate = true; } } private class SimPreference extends Preference { SubscriptionInfo mSubInfoRecord; int mSlotId; Context mContext; public SimPreference(Context context, SubscriptionInfo subInfoRecord, int slotId) { super(context); mContext = context; mSubInfoRecord = subInfoRecord; mSlotId = slotId; setKey("sim" + mSlotId); update(); } public SimPreference (Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public void update() { final Resources res = mContext.getResources(); setTitle(String.format(mContext.getResources() .getString(R.string.sim_editor_title), (mSlotId + 1))); if (mSubInfoRecord != null) { if (TextUtils.isEmpty(getPhoneNumber(mSubInfoRecord))) { setSummary(mSubInfoRecord.getDisplayName()); } else { setSummary(mSubInfoRecord.getDisplayName() + " - " + getPhoneNumber(mSubInfoRecord)); setEnabled(true); } setIcon(new BitmapDrawable(res, (mSubInfoRecord.createIconBitmap(mContext)))); } else { setSummary(R.string.sim_slot_empty); setFragment(null); setEnabled(false); } } private int getSlotId() { return mSlotId; } @Override protected void onAttachedToActivity() { super.onAttachedToActivity(); if (needUpdate) { needUpdate = false; updateAllOptions(); } } } // This is to show SIM Enable options on/off on UI for user selection. // User can activate/de-activate through SIM on/off options. private class SimEnablerPreference extends SimPreference implements OnCheckedChangeListener { private String TAG = "SimEnablerPreference"; private static final boolean DBG = true; private static final int EVT_UPDATE = 1; private static final int EVT_SHOW_RESULT_DLG = 2; private static final int EVT_SHOW_PROGRESS_DLG = 3; private static final int EVT_PROGRESS_DLG_TIME_OUT = 4; private static final int CONFIRM_ALERT_DLG_ID = 1; private static final int ERROR_ALERT_DLG_ID = 2; private static final int RESULT_ALERT_DLG_ID = 3; private static final int REQUEST_SUCCESS = 0; private static final int GENERIC_FAILURE = -1; private static final int INVALID_INPUT = -2; private static final int REQUEST_IN_PROGRESS = -3; private static final String DISPLAY_NUMBERS_TYPE = "display_numbers_type"; private SubscriptionInfo mSir; private boolean mCurrentUiccProvisionState; private boolean mIsChecked; private boolean mCmdInProgress = false; private int mSwitchVisibility = View.VISIBLE; private CompoundButton mSwitch; //Delay for progress dialog to dismiss private static final int PROGRESS_DLG_TIME_OUT = 30000; private static final int MSG_DELAY_TIME = 2000; private IExtTelephony mExtTelephony; public SimEnablerPreference(Context context, SubscriptionInfo sir, int slotId) { super(context, (AttributeSet)null, com.android.internal.R.attr.checkBoxPreferenceStyle); logd("Contructor..Enter"); mContext = context; mSlotId = slotId; mSir = sir; mSubInfoRecord = sir; if (mContext.getResources().getBoolean(R.bool.config_custom_multi_sim_checkbox)) { setWidgetLayoutResource(R.layout.custom_sim_checkbox); } else { setWidgetLayoutResource(R.layout.custom_sim_switch); } mExtTelephony = IExtTelephony.Stub.asInterface(ServiceManager.getService("extphone")); setSwitchVisibility(View.VISIBLE); setKey("sim" + mSlotId); update(); } private void sendMessage(int event, Handler handler, int delay) { Message message = handler.obtainMessage(event); handler.sendMessageDelayed(message, delay); } private void sendMessage(int event, Handler handler, int delay, int arg1, int arg2) { Message message = handler.obtainMessage(event, arg1, arg2); handler.sendMessageDelayed(message, delay); } private boolean hasCard() { return TelephonyManager.getDefault().hasIccCard(mSlotId); } private boolean isAirplaneModeOn() { return (Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0) != 0); } private int getProvisionStatus(int slotId) { return mUiccProvisionStatus[slotId]; } @Override protected void onBindView(View view) { super.onBindView(view); logd("onBindView...."); mSwitch = (CompoundButton) view.findViewById(R.id.sub_switch_widget); mSwitch.setOnCheckedChangeListener(this); update(); // now use other config screen to active/deactive sim card\ mSwitch.setVisibility(mSwitchVisibility); // Disable manual provisioning option to user when // device is in Airplane mode. Hide it if the extphone framework // is not present, as the operation relies on said framework. if (mExtTelephony == null || !mContext.getResources().getBoolean(R.bool.config_enableManualSubProvisioning)) { mSwitch.setVisibility(View.GONE); } else { mSwitch.setVisibility(View.VISIBLE); mSwitch.setEnabled(!isAirplaneModeOn() && isCurrentSubValid()); } } @Override public void update() { final Resources res = mContext.getResources(); logd("update()" + mSir); try { //get current provision state of the SIM. mUiccProvisionStatus[mSlotId] = mExtTelephony.getCurrentUiccCardProvisioningStatus(mSlotId); } catch (RemoteException ex) { mUiccProvisionStatus[mSlotId] = INVALID_STATE; loge("Failed to get pref, slotId: "+ mSlotId +" Exception: " + ex); } catch (NullPointerException ex) { mUiccProvisionStatus[mSlotId] = INVALID_STATE; loge("Failed to get pref, slotId: "+ mSlotId +" Exception: " + ex); } if (mUiccProvisionStatus[mSlotId] == INVALID_STATE) { mUiccProvisionStatus[mSlotId] = PROVISIONED; } boolean isSubValid = isCurrentSubValid(); setEnabled(isSubValid); logd("update: isSubValid " + isSubValid + " provision status[" + mSlotId + "] = " + mUiccProvisionStatus[mSlotId]); setTitle(res.getString(R.string.sim_card_number_title, mSlotId + 1)); if (isSubValid) { updateSummary(); setIcon(new BitmapDrawable(res, (mSir.createIconBitmap(mContext)))); } else { setSummary(res.getString(R.string.sim_slot_empty)); } } // This method returns true if SubScription record corresponds to this // Preference screen has a valid SIM and slot index/SubId. private boolean isCurrentSubValid() { boolean isSubValid = false; if (hasCard()) { List sirList = mSubscriptionManager.getActiveSubscriptionInfoList(); if (sirList != null ) { for (SubscriptionInfo sir : sirList) { if (sir != null && mSlotId == sir.getSimSlotIndex()) { mSir = sir; break; } } if (mSir != null && SubscriptionManager.isValidSubscriptionId(mSir.getSubscriptionId()) && mSir.getSimSlotIndex() >= 0 && getProvisionStatus(mSir.getSimSlotIndex()) >= 0) { isSubValid = true; } } } return isSubValid; } public void setSwitchVisibility (int visibility) { mSwitchVisibility = visibility; } // Based on the received SIM provision state this method // sets the check box on Sim Preference UI and updates new // state to mCurrentUiccProvisionState. private void setChecked(boolean uiccProvisionState) { logd("setChecked: uiccProvisionState " + uiccProvisionState + "sir:" + mSir); if (mSwitch != null) { mSwitch.setOnCheckedChangeListener(null); // Do not update update checkstatus again in progress if (!mCmdInProgress) { mSwitch.setChecked(uiccProvisionState); } mSwitch.setOnCheckedChangeListener(this); mCurrentUiccProvisionState = uiccProvisionState; } } private void updateSummary() { Resources res = mContext.getResources(); String summary; boolean isActivated = (getProvisionStatus(mSir.getSimSlotIndex()) == PROVISIONED); logd("updateSummary: subId " + mSir.getSubscriptionId() + " isActivated = " + isActivated + " slot id = " + mSlotId); String displayName = mSir == null ? "SIM" : (String)mSir.getDisplayName(); if (isActivated) { summary = displayName; if (!TextUtils.isEmpty(mSir.getNumber())) { summary = displayName + " - " + mSir.getNumber(); } } else { summary = mContext.getString(R.string.sim_enabler_summary, displayName, res.getString(hasCard() ? R.string.sim_disabled : R.string.sim_missing)); } setSummary(summary); setChecked(isActivated); } /** * get number of Subs provisioned on the device * @param context * @return */ public int getNumOfSubsProvisioned() { int activeSubInfoCount = 0; List subInfoLists = mSubscriptionManager.getActiveSubscriptionInfoList(); if (subInfoLists != null) { for (SubscriptionInfo subInfo : subInfoLists) { if (getProvisionStatus(subInfo.getSimSlotIndex()) == PROVISIONED) activeSubInfoCount++; } } return activeSubInfoCount; } public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { mIsChecked = isChecked; logd("onClick: " + isChecked); handleUserRequest(); } // This internal method called when user changes preference from UI // 1. For activation/deactivation request from User, if device in APM mode // OR if voice call active on any SIM it dispay error dialog and returns. // 2. For deactivation request it returns error dialog if only one SUB in // active state. // 3. In other cases it sends user request to framework. synchronized private void handleUserRequest() { if (isAirplaneModeOn()) { // do nothing but warning logd("APM is on, EXIT!"); showAlertDialog(ERROR_ALERT_DLG_ID, R.string.sim_enabler_airplane_on); return; } for (int i = 0; i < mPhoneCount; i++) { int[] subId = SubscriptionManager.getSubId(i); //when voice call in progress, subscription can't be activate/deactivate. if (TelephonyManager.getDefault().getCallState(subId[0]) != TelephonyManager.CALL_STATE_IDLE) { logd("Call state for phoneId: " + i + " is not idle, EXIT!"); showAlertDialog(ERROR_ALERT_DLG_ID, R.string.sim_enabler_in_call); return; } } if (!mIsChecked) { if (getNumOfSubsProvisioned() > 1) { logd("More than one sub is active, Deactivation possible."); showAlertDialog(CONFIRM_ALERT_DLG_ID, 0); } else { logd("Only one sub is active. Deactivation not possible."); showAlertDialog(ERROR_ALERT_DLG_ID, R.string.sim_enabler_both_inactive); return; } } else { logd("Activate the sub"); sendUiccProvisioningRequest(); } } private void sendUiccProvisioningRequest() { if (!mSwitch.isEnabled()) { return; } new SimEnablerDisabler().execute(); } private class SimEnablerDisabler extends AsyncTask { int newProvisionedState = NOT_PROVISIONED; @Override protected void onPreExecute() { super.onPreExecute(); mCmdInProgress = true; showProgressDialog(); setEnabled(false); } @Override protected Integer doInBackground(Void... params) { int result = -1; newProvisionedState = NOT_PROVISIONED; try { if (mIsChecked) { result = mExtTelephony.activateUiccCard(mSir.getSimSlotIndex()); newProvisionedState = PROVISIONED; } else { result = mExtTelephony.deactivateUiccCard(mSir.getSimSlotIndex()); } } catch (RemoteException ex) { loge("Activate sub failed " + result + " phoneId " + mSir.getSimSlotIndex()); } catch (NullPointerException ex) { loge("Failed to activate sub Exception: " + ex); } return result; } @Override protected void onPostExecute(Integer result) { processSetUiccDone(result.intValue(), newProvisionedState); } } private void processSetUiccDone(int result, int newProvisionedState) { sendMessage(EVT_UPDATE, mHandler, MSG_DELAY_TIME); sendMessage(EVT_SHOW_RESULT_DLG, mHandler, MSG_DELAY_TIME, result, newProvisionedState); mCmdInProgress = false; } private void showAlertDialog(int dialogId, int msgId) { String title = mSir == null ? "SUB" : mSir.getDisplayName().toString(); // Confirm only one AlertDialog instance to show. dismissDialog(sAlertDialog); dismissDialog(sProgressDialog); AlertDialog.Builder builder = new AlertDialog.Builder(mContext) .setTitle(title); switch(dialogId) { case CONFIRM_ALERT_DLG_ID: String message; if (mContext.getResources().getBoolean( R.bool.confirm_to_switch_data_service)) { if (SubscriptionManager.getDefaultDataSubId() == mSir.getSubscriptionId()) { message = mContext.getString( R.string.sim_enabler_need_switch_data_service, getProvisionedSlotId(mContext)); } else { message = mContext.getString(R.string.sim_enabler_need_disable_sim); } builder.setTitle(R.string.sim_enabler_will_disable_sim_title); } else { message = mContext.getString(R.string.sim_enabler_need_disable_sim); } builder.setMessage(message); builder.setPositiveButton(android.R.string.ok, mDialogClickListener); builder.setNegativeButton(android.R.string.no, mDialogClickListener); builder.setOnCancelListener(mDialogCanceListener); break; case ERROR_ALERT_DLG_ID: builder.setMessage(mContext.getString(msgId)); builder.setNeutralButton(android.R.string.ok, mDialogClickListener); builder.setCancelable(false); break; case RESULT_ALERT_DLG_ID: String msg = mCurrentUiccProvisionState ? mContext.getString(R.string.sub_activate_success) : mContext.getString(R.string.sub_deactivate_success); builder.setMessage(msg); builder.setNeutralButton(android.R.string.ok, null); break; default: break; } sAlertDialog = builder.create(); sAlertDialog.setCanceledOnTouchOutside(false); sAlertDialog.show(); } private int getProvisionedSlotId(Context context) { int activeSlotId = -1; List subInfoLists = mSubscriptionManager.getActiveSubscriptionInfoList(); if (subInfoLists != null) { for (SubscriptionInfo subInfo : subInfoLists) { if (getProvisionStatus(subInfo.getSimSlotIndex()) == PROVISIONED && subInfo.getSubscriptionId() != mSir.getSubscriptionId()) activeSlotId = subInfo.getSimSlotIndex() + 1; } } return activeSlotId; } private void showProgressDialog() { String title = mSir == null ? "SUB" : mSir.getDisplayName().toString(); String msg = mContext.getString(mIsChecked ? R.string.sim_enabler_enabling : R.string.sim_enabler_disabling); dismissDialog(sProgressDialog); sProgressDialog = new ProgressDialog(mContext); sProgressDialog.setIndeterminate(true); sProgressDialog.setTitle(title); sProgressDialog.setMessage(msg); sProgressDialog.setCancelable(false); sProgressDialog.setCanceledOnTouchOutside(false); sProgressDialog.show(); sendMessage(EVT_PROGRESS_DLG_TIME_OUT, mHandler, PROGRESS_DLG_TIME_OUT); } private void dismissDialog(Dialog dialog) { if((dialog != null) && (dialog.isShowing())) { dialog.dismiss(); dialog = null; } } public void cleanUpPendingDialogs() { dismissDialog(sProgressDialog); dismissDialog(sAlertDialog); } private DialogInterface.OnClickListener mDialogClickListener = new DialogInterface .OnClickListener() { public void onClick(DialogInterface dialog, int which) { if (which == DialogInterface.BUTTON_POSITIVE) { dismissDialog(sAlertDialog); sendUiccProvisioningRequest(); } else if (which == DialogInterface.BUTTON_NEGATIVE) { update(); } else if (which == DialogInterface.BUTTON_NEUTRAL) { update(); } } }; private DialogInterface.OnCancelListener mDialogCanceListener = new DialogInterface .OnCancelListener() { public void onCancel(DialogInterface dialog) { update(); } }; private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch(msg.what) { case EVT_UPDATE: simEnablerUpdate(); case EVT_SHOW_RESULT_DLG: int result = msg.arg1; int newProvisionedState = msg.arg2; logd("EVT_SHOW_RESULT_DLG result: " + result + " new provisioned state " + newProvisionedState); update(); if (result != REQUEST_SUCCESS) { int msgId = (newProvisionedState == PROVISIONED) ? R.string.sub_activate_failed : R.string.sub_deactivate_failed; showAlertDialog(ERROR_ALERT_DLG_ID, msgId); } else { showAlertDialog(RESULT_ALERT_DLG_ID, 0); } mHandler.removeMessages(EVT_PROGRESS_DLG_TIME_OUT); break; case EVT_SHOW_PROGRESS_DLG: logd("EVT_SHOW_PROGRESS_DLG"); showProgressDialog(); break; case EVT_PROGRESS_DLG_TIME_OUT: logd("EVT_PROGRESS_DLG_TIME_OUT"); dismissDialog(sProgressDialog); // Must update UI when time out update(); break; default: break; } } }; private void logd(String msg) { if (DBG) Log.d(TAG + "(" + mSlotId + ")", msg); } private void loge(String msg) { Log.e(TAG + "(" + mSlotId + ")", msg); } } // Returns the line1Number. Line1number should always be read from TelephonyManager since it can // be overridden for display purposes. private String getPhoneNumber(SubscriptionInfo info) { final TelephonyManager tm = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); return tm.getLine1NumberForSubscriber(info.getSubscriptionId()); } private void log(String s) { Log.d(TAG, s); } /** * For search */ public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = new BaseSearchIndexProvider() { @Override public List getXmlResourcesToIndex(Context context, boolean enabled) { ArrayList result = new ArrayList(); if (Utils.showSimCardTile(context)) { SearchIndexableResource sir = new SearchIndexableResource(context); sir.xmlResId = R.xml.sim_settings; result.add(sir); } return result; } }; // Internal utility, returns true if Uicc card // corresponds to given slotId is provisioned. private boolean isSubProvisioned(int slotId) { boolean retVal = false; if (mUiccProvisionStatus[slotId] == PROVISIONED) retVal = true; return retVal; } private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); Log.d(TAG, "Intent received: " + action); if (ACTION_UICC_MANUAL_PROVISION_STATUS_CHANGED.equals(action)) { int phoneId = intent.getIntExtra(PhoneConstants.PHONE_KEY, SubscriptionManager.INVALID_SUBSCRIPTION_ID); int newProvisionedState = intent.getIntExtra(EXTRA_NEW_PROVISION_STATE, NOT_PROVISIONED); updateSubscriptions(); Log.d(TAG, "Received ACTION_UICC_MANUAL_PROVISION_STATUS_CHANGED on phoneId: " + phoneId + " new sub state " + newProvisionedState); } } }; // When primarycard feature enabled this provides menu option for user // to view/select current primary slot. private void initLTEPreference() { boolean isPrimarySubFeatureEnable = SystemProperties.getBoolean("persist.radio.primarycard", false); boolean primarySetable = Settings.Global.getInt(mContext.getContentResolver(), CONFIG_PRIMARY_SUB_SETABLE, 0) == 1; log("isPrimarySubFeatureEnable :" + isPrimarySubFeatureEnable + " primarySetable :" + primarySetable); if (!isPrimarySubFeatureEnable || !primarySetable) { final PreferenceCategory simActivities = (PreferenceCategory) findPreference(SIM_ACTIVITIES_CATEGORY); simActivities.removePreference(mPrimarySubSelect); return; } int currentPrimarySlot = Settings.Global.getInt(mContext.getContentResolver(), CONFIG_CURRENT_PRIMARY_SUB, SubscriptionManager.INVALID_SIM_SLOT_INDEX); boolean isManualMode = Settings.Global.getInt(mContext.getContentResolver(), CONFIG_LTE_SUB_SELECT_MODE, 1) == 0; log("init LTE primary slot : " + currentPrimarySlot + " isManualMode :" + isManualMode); if (SubscriptionManager.isValidSlotId(currentPrimarySlot)) { final SubscriptionInfo subInfo = mSubscriptionManager .getActiveSubscriptionInfoForSimSlotIndex(currentPrimarySlot); CharSequence lteSummary = (subInfo == null ) ? null : subInfo.getDisplayName(); mPrimarySubSelect.setSummary(lteSummary); } else { mPrimarySubSelect.setSummary(""); } mPrimarySubSelect.setEnabled(isManualMode); } private boolean disableDds() { boolean disableDds = Settings.Global.getInt(mContext.getContentResolver(), CONFIG_DISABLE_DDS_PREFERENCE, 0) == 1; log(" config disable dds = " + disableDds); return disableDds; } private void listen() { TelephonyManager tm = (TelephonyManager) getActivity().getSystemService(Context.TELEPHONY_SERVICE); if (mSelectableSubInfos.size() > 1) { Log.d(TAG, "Register for call state change"); for (int i = 0; i < mPhoneCount; i++) { int subId = mSelectableSubInfos.get(i).getSubscriptionId(); tm.listen(getPhoneStateListener(i, subId), PhoneStateListener.LISTEN_CALL_STATE); } } } private void unRegisterPhoneStateListener() { TelephonyManager tm = (TelephonyManager) getActivity().getSystemService(Context.TELEPHONY_SERVICE); for (int i = 0; i < mPhoneCount; i++) { if (mPhoneStateListener[i] != null) { tm.listen(mPhoneStateListener[i], PhoneStateListener.LISTEN_NONE); mPhoneStateListener[i] = null; } } } private PhoneStateListener getPhoneStateListener(int phoneId, int subId) { // Disable Sim selection for Data when voice call is going on as changing the default data // sim causes a modem reset currently and call gets disconnected // ToDo : Add subtext on disabled preference to let user know that default data sim cannot // be changed while call is going on final int i = phoneId; mPhoneStateListener[phoneId] = new PhoneStateListener(subId) { @Override public void onCallStateChanged(int state, String incomingNumber) { if (DBG) log("PhoneStateListener.onCallStateChanged: state=" + state); mCallState[i] = state; updateCellularDataValues(); } }; return mPhoneStateListener[phoneId]; } private boolean isCallStateIdle() { boolean callStateIdle = true; for (int i = 0; i < mCallState.length; i++) { if (TelephonyManager.CALL_STATE_IDLE != mCallState[i]) { callStateIdle = false; } } Log.d(TAG, "isCallStateIdle " + callStateIdle); return callStateIdle; } }