/* * 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.deviceinfo; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; import android.os.Bundle; import android.os.PersistableBundle; import android.os.UserHandle; import android.os.UserManager; import android.support.v7.preference.Preference; import android.telephony.CarrierConfigManager; import android.telephony.CellBroadcastMessage; import android.telephony.PhoneStateListener; import android.telephony.ServiceState; import android.telephony.SignalStrength; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; 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 com.android.internal.logging.MetricsProto.MetricsEvent; import com.android.internal.telephony.DefaultPhoneNotifier; import com.android.internal.telephony.Phone; import com.android.internal.telephony.PhoneConstantConversions; import com.android.internal.telephony.PhoneFactory; import com.android.settings.R; import com.android.settings.SettingsPreferenceFragment; import com.android.settings.Utils; import com.android.settingslib.DeviceInfoUtils; import java.util.List; import static android.content.Context.CARRIER_CONFIG_SERVICE; import static android.content.Context.TELEPHONY_SERVICE; /** * Display the following information * # Phone Number * # Network * # Roaming * # Device Id (IMEI in GSM and MEID in CDMA) * # Network type * # Operator info (area info cell broadcast for Brazil) * # Signal Strength * */ public class SimStatus extends SettingsPreferenceFragment { private static final String TAG = "SimStatus"; private static final String KEY_DATA_STATE = "data_state"; private static final String KEY_SERVICE_STATE = "service_state"; private static final String KEY_OPERATOR_NAME = "operator_name"; private static final String KEY_ROAMING_STATE = "roaming_state"; private static final String KEY_NETWORK_TYPE = "network_type"; private static final String KEY_LATEST_AREA_INFO = "latest_area_info"; private static final String KEY_PHONE_NUMBER = "number"; private static final String KEY_SIGNAL_STRENGTH = "signal_strength"; private static final String KEY_IMEI = "imei"; private static final String KEY_IMEI_SV = "imei_sv"; private static final String KEY_ICCID = "iccid"; private static final String COUNTRY_ABBREVIATION_BRAZIL = "br"; static final String CB_AREA_INFO_RECEIVED_ACTION = "android.cellbroadcastreceiver.CB_AREA_INFO_RECEIVED"; static final String GET_LATEST_CB_AREA_INFO_ACTION = "android.cellbroadcastreceiver.GET_LATEST_CB_AREA_INFO"; // Require the sender to have this permission to prevent third-party spoofing. static final String CB_AREA_INFO_SENDER_PERMISSION = "android.permission.RECEIVE_EMERGENCY_BROADCAST"; private TelephonyManager mTelephonyManager; private CarrierConfigManager mCarrierConfigManager; private Phone mPhone = null; private Resources mRes; private Preference mSignalStrength; private SubscriptionInfo mSir; private boolean mShowLatestAreaInfo; private boolean mShowICCID; // Default summary for items private String mDefaultText; private TabHost mTabHost; private TabWidget mTabWidget; private ListView mListView; private List<SubscriptionInfo> mSelectableSubInfos; private PhoneStateListener mPhoneStateListener; private BroadcastReceiver mAreaInfoReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (CB_AREA_INFO_RECEIVED_ACTION.equals(action)) { Bundle extras = intent.getExtras(); if (extras == null) { return; } CellBroadcastMessage cbMessage = (CellBroadcastMessage) extras.get("message"); if (cbMessage != null && cbMessage.getServiceCategory() == 50 && mSir.getSubscriptionId() == cbMessage.getSubId()) { String latestAreaInfo = cbMessage.getMessageBody(); updateAreaInfo(latestAreaInfo); } } } }; @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); mTelephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE); mCarrierConfigManager = (CarrierConfigManager) getSystemService(CARRIER_CONFIG_SERVICE); mSelectableSubInfos = SubscriptionManager.from(getContext()) .getActiveSubscriptionInfoList(); addPreferencesFromResource(R.xml.device_info_sim_status); mRes = getResources(); mDefaultText = mRes.getString(R.string.device_info_default); // Note - missing in zaku build, be careful later... mSignalStrength = findPreference(KEY_SIGNAL_STRENGTH); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (mSelectableSubInfos == null) { mSir = null; } else { mSir = mSelectableSubInfos.size() > 0 ? mSelectableSubInfos.get(0) : null; if (mSelectableSubInfos.size() > 1) { 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); 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.setOnTabChangedListener(mTabListener); mTabHost.clearAllTabs(); for (int i = 0; i < mSelectableSubInfos.size(); i++) { mTabHost.addTab(buildTabSpec(String.valueOf(i), String.valueOf(mSelectableSubInfos.get(i).getDisplayName()))); } return view; } } return super.onCreateView(inflater, container, savedInstanceState); } @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); updatePhoneInfos(); } @Override protected int getMetricsCategory() { return MetricsEvent.DEVICEINFO_SIM_STATUS; } @Override public void onResume() { super.onResume(); if (mPhone != null) { updatePreference(); updateSignalStrength(mPhone.getSignalStrength()); updateServiceState(mPhone.getServiceState()); updateDataState(); mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_DATA_CONNECTION_STATE | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS | PhoneStateListener.LISTEN_SERVICE_STATE); if (mShowLatestAreaInfo) { getContext().registerReceiver(mAreaInfoReceiver, new IntentFilter(CB_AREA_INFO_RECEIVED_ACTION), CB_AREA_INFO_SENDER_PERMISSION, null); // Ask CellBroadcastReceiver to broadcast the latest area info received Intent getLatestIntent = new Intent(GET_LATEST_CB_AREA_INFO_ACTION); getContext().sendBroadcastAsUser(getLatestIntent, UserHandle.ALL, CB_AREA_INFO_SENDER_PERMISSION); } } } @Override public void onPause() { super.onPause(); if (mPhone != null) { mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE); } if (mShowLatestAreaInfo) { getContext().unregisterReceiver(mAreaInfoReceiver); } } /** * Removes the specified preference, if it exists. * @param key the key for the Preference item */ private void removePreferenceFromScreen(String key) { Preference pref = findPreference(key); if (pref != null) { getPreferenceScreen().removePreference(pref); } } private void setSummaryText(String key, String text) { if (TextUtils.isEmpty(text)) { text = mDefaultText; } // some preferences may be missing final Preference preference = findPreference(key); if (preference != null) { preference.setSummary(text); } } private void updateNetworkType() { // Whether EDGE, UMTS, etc... String networktype = null; final int subId = mSir.getSubscriptionId(); final int actualDataNetworkType = mTelephonyManager.getDataNetworkType( mSir.getSubscriptionId()); final int actualVoiceNetworkType = mTelephonyManager.getVoiceNetworkType( mSir.getSubscriptionId()); if (TelephonyManager.NETWORK_TYPE_UNKNOWN != actualDataNetworkType) { networktype = mTelephonyManager.getNetworkTypeName(actualDataNetworkType); } else if (TelephonyManager.NETWORK_TYPE_UNKNOWN != actualVoiceNetworkType) { networktype = mTelephonyManager.getNetworkTypeName(actualVoiceNetworkType); } boolean show4GForLTE = false; try { Context con = getActivity().createPackageContext("com.android.systemui", 0); int id = con.getResources().getIdentifier("config_show4GForLTE", "bool", "com.android.systemui"); show4GForLTE = con.getResources().getBoolean(id); } catch (NameNotFoundException e) { Log.e(TAG, "NameNotFoundException for show4GFotLTE"); } if (networktype != null && networktype.equals("LTE") && show4GForLTE) { networktype = "4G"; } setSummaryText(KEY_NETWORK_TYPE, networktype); } private void updateDataState() { final int state = PhoneConstantConversions.convertDataState(mPhone.getDataConnectionState()); String display = mRes.getString(R.string.radioInfo_unknown); switch (state) { case TelephonyManager.DATA_CONNECTED: display = mRes.getString(R.string.radioInfo_data_connected); break; case TelephonyManager.DATA_SUSPENDED: display = mRes.getString(R.string.radioInfo_data_suspended); break; case TelephonyManager.DATA_CONNECTING: display = mRes.getString(R.string.radioInfo_data_connecting); break; case TelephonyManager.DATA_DISCONNECTED: display = mRes.getString(R.string.radioInfo_data_disconnected); break; } setSummaryText(KEY_DATA_STATE, display); } private void updateServiceState(ServiceState serviceState) { final int state = serviceState.getState(); String display = mRes.getString(R.string.radioInfo_unknown); switch (state) { case ServiceState.STATE_IN_SERVICE: display = mRes.getString(R.string.radioInfo_service_in); break; case ServiceState.STATE_OUT_OF_SERVICE: // Set signal strength to 0 when service state is STATE_OUT_OF_SERVICE mSignalStrength.setSummary("0"); case ServiceState.STATE_EMERGENCY_ONLY: // Set summary string of service state to radioInfo_service_out when // service state is both STATE_OUT_OF_SERVICE & STATE_EMERGENCY_ONLY display = mRes.getString(R.string.radioInfo_service_out); break; case ServiceState.STATE_POWER_OFF: display = mRes.getString(R.string.radioInfo_service_off); // Also set signal strength to 0 mSignalStrength.setSummary("0"); break; } setSummaryText(KEY_SERVICE_STATE, display); if (serviceState.getRoaming()) { setSummaryText(KEY_ROAMING_STATE, mRes.getString(R.string.radioInfo_roaming_in)); } else { setSummaryText(KEY_ROAMING_STATE, mRes.getString(R.string.radioInfo_roaming_not)); } setSummaryText(KEY_OPERATOR_NAME, serviceState.getOperatorAlphaLong()); } private void updateAreaInfo(String areaInfo) { if (areaInfo != null) { setSummaryText(KEY_LATEST_AREA_INFO, areaInfo); } } void updateSignalStrength(SignalStrength signalStrength) { if (mSignalStrength != null) { final int state = mPhone.getServiceState().getState(); Resources r = getResources(); if ((ServiceState.STATE_OUT_OF_SERVICE == state) || (ServiceState.STATE_POWER_OFF == state)) { mSignalStrength.setSummary("0"); return; } int signalDbm = signalStrength.getDbm(); int signalAsu = signalStrength.getAsuLevel(); if (-1 == signalDbm) { signalDbm = 0; } if (-1 == signalAsu) { signalAsu = 0; } mSignalStrength.setSummary(r.getString(R.string.sim_signal_strength, signalDbm, signalAsu)); } } private void updatePreference() { if (mPhone.getPhoneType() != TelephonyManager.PHONE_TYPE_CDMA) { // only show area info when SIM country is Brazil if (COUNTRY_ABBREVIATION_BRAZIL.equals(mTelephonyManager.getSimCountryIso( mSir.getSubscriptionId()))) { mShowLatestAreaInfo = true; } } PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId( mSir.getSubscriptionId()); mShowICCID = carrierConfig.getBoolean( CarrierConfigManager.KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL); // If formattedNumber is null or empty, it'll display as "Unknown". setSummaryText(KEY_PHONE_NUMBER, DeviceInfoUtils.getFormattedPhoneNumber(getContext(), mSir)); setSummaryText(KEY_IMEI, mPhone.getImei()); setSummaryText(KEY_IMEI_SV, mPhone.getDeviceSvn()); if (!mShowICCID) { removePreferenceFromScreen(KEY_ICCID); } else { // Get ICCID, which is SIM serial number String iccid = mTelephonyManager.getSimSerialNumber(mSir.getSubscriptionId()); setSummaryText(KEY_ICCID, iccid); } if (!mShowLatestAreaInfo) { removePreferenceFromScreen(KEY_LATEST_AREA_INFO); } } private void updatePhoneInfos() { if (mSir != null) { // TODO: http://b/23763013 final Phone phone = PhoneFactory.getPhone(SubscriptionManager.getPhoneId( mSir.getSubscriptionId())); if (UserManager.get(getContext()).isAdminUser() && SubscriptionManager.isValidSubscriptionId(mSir.getSubscriptionId())) { if (phone == null) { Log.e(TAG, "Unable to locate a phone object for the given Subscription ID."); return; } mPhone = phone; // To avoid register multiple listeners when user changes the tab. if (mPhoneStateListener != null && mTelephonyManager != null) { mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE); mPhoneStateListener = null; } mPhoneStateListener = new PhoneStateListener(mSir.getSubscriptionId()) { @Override public void onDataConnectionStateChanged(int state) { updateDataState(); updateNetworkType(); } @Override public void onSignalStrengthsChanged(SignalStrength signalStrength) { updateSignalStrength(signalStrength); } @Override public void onServiceStateChanged(ServiceState serviceState) { updateServiceState(serviceState); } }; } } } private OnTabChangeListener mTabListener = new OnTabChangeListener() { @Override public void onTabChanged(String tabId) { final int slotId = Integer.parseInt(tabId); mSir = mSelectableSubInfos.get(slotId); // The User has changed tab; update the SIM information. updatePhoneInfos(); mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_DATA_CONNECTION_STATE | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS | PhoneStateListener.LISTEN_SERVICE_STATE); updateDataState(); updateNetworkType(); updatePreference(); } }; 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); } }