diff --git a/AndroidManifest.xml b/AndroidManifest.xml index b4841dc254d..a800b4bcf71 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -1178,6 +1178,18 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index e7effb68634..f6f4d5af059 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -5546,6 +5546,8 @@ Disable Network Name Broadcast protects from third parties getting access to your network information. Disabling Network Name Broadcast will prevent automatic connection to hidden networks. + + %1$d dBm %2$d asu Preferred SIM for diff --git a/res/xml/device_info_sim_status.xml b/res/xml/device_info_sim_status.xml new file mode 100644 index 00000000000..9e9444df4d2 --- /dev/null +++ b/res/xml/device_info_sim_status.xml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/com/android/settings/deviceinfo/SimStatus.java b/src/com/android/settings/deviceinfo/SimStatus.java new file mode 100644 index 00000000000..46addf3f3f9 --- /dev/null +++ b/src/com/android/settings/deviceinfo/SimStatus.java @@ -0,0 +1,413 @@ +/* + * 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.res.Resources; +import android.os.Bundle; +import android.os.SystemProperties; +import android.os.UserHandle; +import android.preference.Preference; +import android.preference.PreferenceActivity; +import android.telephony.CellBroadcastMessage; +import android.telephony.PhoneNumberUtils; +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 com.android.internal.telephony.DefaultPhoneNotifier; +import com.android.internal.telephony.Phone; +import com.android.internal.telephony.PhoneFactory; +import com.android.settings.R; +import com.android.settings.Utils; + +import android.view.View; +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 java.util.ArrayList; +import java.util.List; + + +/** + * 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 PreferenceActivity { + 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 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 Phone mPhone = null; + private Resources mRes; + private Preference mSignalStrength; + private SubscriptionInfo mSir; + private boolean mShowLatestAreaInfo; + + // Default summary for items + private String mDefaultText; + + private TabHost mTabHost; + private TabWidget mTabWidget; + private ListView mListView; + private List 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 + protected void onCreate(Bundle icicle) { + super.onCreate(icicle); + + mSelectableSubInfos = new ArrayList(); + mTelephonyManager = (TelephonyManager)getSystemService(TELEPHONY_SERVICE); + + 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); + + for (int i = 0; i < mTelephonyManager.getSimCount(); i++) { + final SubscriptionInfo sir = Utils.findRecordBySlotId(i); + if (sir != null) { + mSelectableSubInfos.add(sir); + } + } + + mSir = mSelectableSubInfos.get(0); + if (mSelectableSubInfos.size() > 1) { + setContentView(R.layout.sim_information); + + mTabHost = (TabHost) findViewById(android.R.id.tabhost); + mTabWidget = (TabWidget) findViewById(android.R.id.tabs); + mListView = (ListView) 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()))); + } + } + + updatePhoneInfos(); + } + + @Override + protected 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) { + 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); + 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) { + 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; + if (TelephonyManager.NETWORK_TYPE_UNKNOWN + != mTelephonyManager.getNetworkType(mSir.getSubscriptionId())) { + networktype = mTelephonyManager.getNetworkTypeName( + mTelephonyManager.getNetworkType(mSir.getSubscriptionId())); + } + setSummaryText(KEY_NETWORK_TYPE, networktype); + } + + private void updateDataState() { + final int state = + DefaultPhoneNotifier.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: + case ServiceState.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); + 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"); + } + + 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; + } + } + + String rawNumber = mPhone.getLine1Number(); // may be null or empty + String formattedNumber = null; + if (!TextUtils.isEmpty(rawNumber)) { + formattedNumber = PhoneNumberUtils.formatNumber(rawNumber); + } + // If formattedNumber is null or empty, it'll display as "Unknown". + setSummaryText(KEY_PHONE_NUMBER, formattedNumber); + final String imei = mPhone.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA + ? mPhone.getImei() : mPhone.getDeviceId(); + setSummaryText(KEY_IMEI, imei); + setSummaryText(KEY_IMEI_SV, + ((TelephonyManager) getSystemService(TELEPHONY_SERVICE)) + .getDeviceSoftwareVersion(/*slotId*/)); + + if (!mShowLatestAreaInfo) { + removePreferenceFromScreen(KEY_LATEST_AREA_INFO); + } + } + + private void updatePhoneInfos() { + final Phone phone = PhoneFactory.getPhone(SubscriptionManager.getPhoneId( + mSir.getSubscriptionId())); + if (UserHandle.myUserId() == UserHandle.USER_OWNER + && mSir.getSubscriptionId() != SubscriptionManager.INVALID_SUB_ID) { + if (phone == null) { + Log.e(TAG, "Unable to locate a phone object for the given Subscription ID."); + return; + } + + mPhone = phone; + 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); + } +}