Adding Sim Status menu to Settings.

+ Added tabs for Multi-SIM devices.
+ Most of the code was taken from MediaTek
(https://partner-android-review.googlesource.com/#/c/181417)

Bug: 18195254
Change-Id: If6f7d5cfa4d41adb52dfc1bd035b4ff79d926bd8
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index b4841dc..a800b4b 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -1179,6 +1179,18 @@
         </activity>
 
         <!-- Runs in the phone process since it needs access to the Phone object -->
+        <activity android:name=".deviceinfo.SimStatus"
+                android:label="@string/sim_status_title"
+                android:theme="@style/Theme.SubSettingsDialogWhenLarge"
+                android:process="com.android.phone">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.VOICE_LAUNCH" />
+            </intent-filter>
+        </activity>
+
+        <!-- Runs in the phone process since it needs access to the Phone object -->
         <activity android:name=".deviceinfo.ImeiInformation"
                 android:label="@string/imei_information_title"
                 android:theme="@style/Theme.SubSettingsDialogWhenLarge"
diff --git a/res/layout/sim_information.xml b/res/layout/sim_information.xml
new file mode 100644
index 0000000..d2a4acc
--- /dev/null
+++ b/res/layout/sim_information.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<TabHost xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@android:id/tabhost"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <LinearLayout
+        android:id="@+id/tabs_container"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical">
+
+        <HorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:scrollbars="none"
+            android:fillViewport="true">
+
+            <TabWidget
+                android:id="@android:id/tabs"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="horizontal"
+                style="?android:attr/tabWidgetStyle" />
+
+        </HorizontalScrollView>
+
+        <!-- give an empty content area to make tabhost happy -->
+        <FrameLayout
+            android:id="@android:id/tabcontent"
+            android:layout_width="0dip"
+            android:layout_height="0dip" />
+
+        <ListView
+            android:id="@android:id/list"
+            android:layout_width="match_parent"
+            android:layout_height="0dip"
+            android:layout_weight="1"
+            android:clipChildren="false"
+            android:clipToPadding="false"
+            android:smoothScrollbar="false" />
+
+    </LinearLayout>
+
+</TabHost>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index e7effb6..f6f4d5a 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -5546,6 +5546,8 @@
     <string name="preferred_network_offload_footer">Disable Network Name Broadcast protects from third parties getting access to your network information.</string>
     <!-- Preferred Network offload Popup.  [CHAR LIMIT=100] -->
     <string name="preferred_network_offload_popup">Disabling Network Name Broadcast will prevent automatic connection to hidden networks.</string>
+    <!-- Summary text describing signal strength to the user.  [CHAR LIMIT=60] -->
+    <string name="sim_signal_strength"><xliff:g id="dbm">%1$d</xliff:g> dBm <xliff:g id="asu">%2$d</xliff:g> asu</string>
 
     <!-- This is a divider in the SIM cards preferences that is the header of various settings where the user chooses which SIM to use for phone calls, data, and SMS messages [CHAR LIMIT=50] -->
     <string name="sim_pref_divider">Preferred SIM for</string>
diff --git a/res/xml/device_info_sim_status.xml b/res/xml/device_info_sim_status.xml
new file mode 100644
index 0000000..9e9444d
--- /dev/null
+++ b/res/xml/device_info_sim_status.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+        android:title="@string/sim_status_title">
+
+    <Preference android:key="operator_name"
+        style="?android:attr/preferenceInformationStyle"
+        android:title="@string/status_operator"
+        android:summary="@string/device_info_not_available"
+        android:persistent="false" />
+
+    <Preference android:key="signal_strength"
+        style="?android:attr/preferenceInformationStyle"
+        android:title="@string/status_signal_strength"
+        android:summary="@string/device_info_not_available"
+        android:persistent="false" />
+
+    <Preference android:key="network_type"
+        style="?android:attr/preferenceInformationStyle"
+        android:title="@string/status_network_type"
+        android:summary="@string/device_info_not_available"
+        android:persistent="false" />
+
+    <Preference android:key="latest_area_info"
+        style="?android:attr/preferenceInformationStyle"
+        android:title="@string/status_latest_area_info"
+        android:summary="@string/device_info_not_available"
+        android:persistent="false" />
+
+    <Preference android:key="service_state"
+        style="?android:attr/preferenceInformationStyle"
+        android:title="@string/status_service_state"
+        android:summary="@string/device_info_not_available"
+        android:persistent="false" />
+
+    <Preference android:key="roaming_state"
+        style="?android:attr/preferenceInformationStyle"
+        android:title="@string/status_roaming"
+        android:summary="@string/device_info_not_available"
+        android:persistent="false" />
+
+    <Preference android:key="data_state"
+        style="?android:attr/preferenceInformationStyle"
+        android:title="@string/status_data_state"
+        android:summary="@string/device_info_not_available"
+        android:persistent="false" />
+
+    <Preference android:key="number"
+        style="?android:attr/preferenceInformationStyle"
+        android:title="@string/status_number"
+        android:summary="@string/device_info_not_available"
+        android:persistent="false" />
+
+    <Preference android:key="imei"
+        style="?android:attr/preferenceInformationStyle"
+        android:title="@string/status_imei"
+        android:summary="@string/device_info_not_available"
+        android:persistent="false" />
+
+    <Preference android:key="imei_sv"
+        style="?android:attr/preferenceInformationStyle"
+        android:title="@string/status_imei_sv"
+        android:summary="@string/device_info_not_available"
+        android:persistent="false" />
+
+</PreferenceScreen>
diff --git a/src/com/android/settings/deviceinfo/SimStatus.java b/src/com/android/settings/deviceinfo/SimStatus.java
new file mode 100644
index 0000000..46addf3
--- /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<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
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        mSelectableSubInfos = new ArrayList<SubscriptionInfo>();
+        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);
+    }
+}