1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.settings.deviceinfo;
18
19import static android.content.Context.CARRIER_CONFIG_SERVICE;
20import static android.content.Context.TELEPHONY_SERVICE;
21
22import android.content.BroadcastReceiver;
23import android.content.Context;
24import android.content.Intent;
25import android.content.IntentFilter;
26import android.content.pm.PackageManager.NameNotFoundException;
27import android.content.res.Resources;
28import android.os.Bundle;
29import android.os.PersistableBundle;
30import android.os.UserHandle;
31import android.os.UserManager;
32import android.support.v7.preference.Preference;
33import android.telephony.CarrierConfigManager;
34import android.telephony.CellBroadcastMessage;
35import android.telephony.PhoneNumberUtils;
36import android.telephony.PhoneStateListener;
37import android.telephony.ServiceState;
38import android.telephony.SignalStrength;
39import android.telephony.SubscriptionInfo;
40import android.telephony.SubscriptionManager;
41import android.telephony.TelephonyManager;
42import android.text.TextUtils;
43import android.util.Log;
44import android.view.LayoutInflater;
45import android.view.View;
46import android.view.ViewGroup;
47import android.widget.ListView;
48import android.widget.TabHost;
49import android.widget.TabHost.OnTabChangeListener;
50import android.widget.TabHost.TabContentFactory;
51import android.widget.TabHost.TabSpec;
52import android.widget.TabWidget;
53
54import com.android.internal.logging.MetricsProto.MetricsEvent;
55import com.android.internal.telephony.DefaultPhoneNotifier;
56import com.android.internal.telephony.Phone;
57import com.android.internal.telephony.PhoneFactory;
58import com.android.settings.R;
59import com.android.settings.SettingsPreferenceFragment;
60import com.android.settings.Utils;
61
62import java.util.List;
63
64
65/**
66 * Display the following information
67 * # Phone Number
68 * # Network
69 * # Roaming
70 * # Device Id (IMEI in GSM and MEID in CDMA)
71 * # Network type
72 * # Operator info (area info cell broadcast for Brazil)
73 * # Signal Strength
74 *
75 */
76public class SimStatus extends SettingsPreferenceFragment {
77    private static final String TAG = "SimStatus";
78
79    private static final String KEY_DATA_STATE = "data_state";
80    private static final String KEY_SERVICE_STATE = "service_state";
81    private static final String KEY_OPERATOR_NAME = "operator_name";
82    private static final String KEY_ROAMING_STATE = "roaming_state";
83    private static final String KEY_NETWORK_TYPE = "network_type";
84    private static final String KEY_LATEST_AREA_INFO = "latest_area_info";
85    private static final String KEY_PHONE_NUMBER = "number";
86    private static final String KEY_SIGNAL_STRENGTH = "signal_strength";
87    private static final String KEY_IMEI = "imei";
88    private static final String KEY_IMEI_SV = "imei_sv";
89    private static final String KEY_ICCID = "iccid";
90    private static final String COUNTRY_ABBREVIATION_BRAZIL = "br";
91
92    static final String CB_AREA_INFO_RECEIVED_ACTION =
93            "android.cellbroadcastreceiver.CB_AREA_INFO_RECEIVED";
94
95    static final String GET_LATEST_CB_AREA_INFO_ACTION =
96            "android.cellbroadcastreceiver.GET_LATEST_CB_AREA_INFO";
97
98    // Require the sender to have this permission to prevent third-party spoofing.
99    static final String CB_AREA_INFO_SENDER_PERMISSION =
100            "android.permission.RECEIVE_EMERGENCY_BROADCAST";
101
102
103    private TelephonyManager mTelephonyManager;
104    private CarrierConfigManager mCarrierConfigManager;
105    private Phone mPhone = null;
106    private Resources mRes;
107    private Preference mSignalStrength;
108    private SubscriptionInfo mSir;
109    private boolean mShowLatestAreaInfo;
110    private boolean mShowICCID;
111
112    // Default summary for items
113    private String mDefaultText;
114
115    private TabHost mTabHost;
116    private TabWidget mTabWidget;
117    private ListView mListView;
118    private List<SubscriptionInfo> mSelectableSubInfos;
119
120    private PhoneStateListener mPhoneStateListener;
121    private BroadcastReceiver mAreaInfoReceiver = new BroadcastReceiver() {
122        @Override
123        public void onReceive(Context context, Intent intent) {
124            String action = intent.getAction();
125            if (CB_AREA_INFO_RECEIVED_ACTION.equals(action)) {
126                Bundle extras = intent.getExtras();
127                if (extras == null) {
128                    return;
129                }
130                CellBroadcastMessage cbMessage = (CellBroadcastMessage) extras.get("message");
131                if (cbMessage != null && cbMessage.getServiceCategory() == 50) {
132                    String latestAreaInfo = cbMessage.getMessageBody();
133                    updateAreaInfo(latestAreaInfo);
134                }
135            }
136        }
137    };
138
139    @Override
140    public void onCreate(Bundle icicle) {
141        super.onCreate(icicle);
142        mTelephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
143        mCarrierConfigManager = (CarrierConfigManager) getSystemService(CARRIER_CONFIG_SERVICE);
144
145        mSelectableSubInfos = SubscriptionManager.from(getContext())
146                .getActiveSubscriptionInfoList();
147
148        addPreferencesFromResource(R.xml.device_info_sim_status);
149
150        mRes = getResources();
151        mDefaultText = mRes.getString(R.string.device_info_default);
152        // Note - missing in zaku build, be careful later...
153        mSignalStrength = findPreference(KEY_SIGNAL_STRENGTH);
154    }
155
156    @Override
157    public View onCreateView(LayoutInflater inflater, ViewGroup container,
158            Bundle savedInstanceState) {
159        if (mSelectableSubInfos == null) {
160            mSir = null;
161        } else {
162            mSir = mSelectableSubInfos.size() > 0 ? mSelectableSubInfos.get(0) : null;
163
164            if (mSelectableSubInfos.size() > 1) {
165                View view = inflater.inflate(R.layout.icc_lock_tabs, container, false);
166                final ViewGroup prefs_container = (ViewGroup) view.findViewById(
167                        R.id.prefs_container);
168                Utils.prepareCustomPreferencesList(container, view, prefs_container, false);
169                View prefs = super.onCreateView(inflater, prefs_container, savedInstanceState);
170                prefs_container.addView(prefs);
171
172                mTabHost = (TabHost) view.findViewById(android.R.id.tabhost);
173                mTabWidget = (TabWidget) view.findViewById(android.R.id.tabs);
174                mListView = (ListView) view.findViewById(android.R.id.list);
175
176                mTabHost.setup();
177                mTabHost.setOnTabChangedListener(mTabListener);
178                mTabHost.clearAllTabs();
179
180                for (int i = 0; i < mSelectableSubInfos.size(); i++) {
181                    mTabHost.addTab(buildTabSpec(String.valueOf(i),
182                            String.valueOf(mSelectableSubInfos.get(i).getDisplayName())));
183                }
184                return view;
185            }
186        }
187        return super.onCreateView(inflater, container, savedInstanceState);
188    }
189
190    @Override
191    public void onViewCreated(View view, Bundle savedInstanceState) {
192        super.onViewCreated(view, savedInstanceState);
193
194        updatePhoneInfos();
195    }
196
197    @Override
198    protected int getMetricsCategory() {
199        return MetricsEvent.DEVICEINFO_SIM_STATUS;
200    }
201
202    @Override
203    public void onResume() {
204        super.onResume();
205        if (mPhone != null) {
206            updatePreference();
207
208            updateSignalStrength(mPhone.getSignalStrength());
209            updateServiceState(mPhone.getServiceState());
210            updateDataState();
211            mTelephonyManager.listen(mPhoneStateListener,
212                    PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
213                    | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS
214                    | PhoneStateListener.LISTEN_SERVICE_STATE);
215            if (mShowLatestAreaInfo) {
216                getContext().registerReceiver(mAreaInfoReceiver,
217                        new IntentFilter(CB_AREA_INFO_RECEIVED_ACTION),
218                        CB_AREA_INFO_SENDER_PERMISSION, null);
219                // Ask CellBroadcastReceiver to broadcast the latest area info received
220                Intent getLatestIntent = new Intent(GET_LATEST_CB_AREA_INFO_ACTION);
221                getContext().sendBroadcastAsUser(getLatestIntent, UserHandle.ALL,
222                        CB_AREA_INFO_SENDER_PERMISSION);
223            }
224        }
225    }
226
227    @Override
228    public void onPause() {
229        super.onPause();
230
231        if (mPhone != null) {
232            mTelephonyManager.listen(mPhoneStateListener,
233                    PhoneStateListener.LISTEN_NONE);
234        }
235        if (mShowLatestAreaInfo) {
236            getContext().unregisterReceiver(mAreaInfoReceiver);
237        }
238    }
239
240    /**
241     * Removes the specified preference, if it exists.
242     * @param key the key for the Preference item
243     */
244    private void removePreferenceFromScreen(String key) {
245        Preference pref = findPreference(key);
246        if (pref != null) {
247            getPreferenceScreen().removePreference(pref);
248        }
249    }
250
251    private void setSummaryText(String key, String text) {
252        if (TextUtils.isEmpty(text)) {
253            text = mDefaultText;
254        }
255        // some preferences may be missing
256        final Preference preference = findPreference(key);
257        if (preference != null) {
258            preference.setSummary(text);
259        }
260    }
261
262    private void updateNetworkType() {
263        // Whether EDGE, UMTS, etc...
264        String networktype = null;
265        final int subId = mSir.getSubscriptionId();
266        final int actualDataNetworkType = mTelephonyManager.getDataNetworkType(
267                mSir.getSubscriptionId());
268        final int actualVoiceNetworkType = mTelephonyManager.getVoiceNetworkType(
269                mSir.getSubscriptionId());
270        if (TelephonyManager.NETWORK_TYPE_UNKNOWN != actualDataNetworkType) {
271            networktype = mTelephonyManager.getNetworkTypeName(actualDataNetworkType);
272        } else if (TelephonyManager.NETWORK_TYPE_UNKNOWN != actualVoiceNetworkType) {
273            networktype = mTelephonyManager.getNetworkTypeName(actualVoiceNetworkType);
274        }
275
276        boolean show4GForLTE = false;
277        try {
278            Context con = getActivity().createPackageContext("com.android.systemui", 0);
279            int id = con.getResources().getIdentifier("config_show4GForLTE",
280                    "bool", "com.android.systemui");
281            show4GForLTE = con.getResources().getBoolean(id);
282        } catch (NameNotFoundException e) {
283            Log.e(TAG, "NameNotFoundException for show4GFotLTE");
284        }
285
286        if (networktype != null && networktype.equals("LTE") && show4GForLTE) {
287            networktype = "4G";
288        }
289        setSummaryText(KEY_NETWORK_TYPE, networktype);
290    }
291
292    private void updateDataState() {
293        final int state =
294                DefaultPhoneNotifier.convertDataState(mPhone.getDataConnectionState());
295
296        String display = mRes.getString(R.string.radioInfo_unknown);
297
298        switch (state) {
299            case TelephonyManager.DATA_CONNECTED:
300                display = mRes.getString(R.string.radioInfo_data_connected);
301                break;
302            case TelephonyManager.DATA_SUSPENDED:
303                display = mRes.getString(R.string.radioInfo_data_suspended);
304                break;
305            case TelephonyManager.DATA_CONNECTING:
306                display = mRes.getString(R.string.radioInfo_data_connecting);
307                break;
308            case TelephonyManager.DATA_DISCONNECTED:
309                display = mRes.getString(R.string.radioInfo_data_disconnected);
310                break;
311        }
312
313        setSummaryText(KEY_DATA_STATE, display);
314    }
315
316    private void updateServiceState(ServiceState serviceState) {
317        final int state = serviceState.getState();
318        String display = mRes.getString(R.string.radioInfo_unknown);
319
320        switch (state) {
321            case ServiceState.STATE_IN_SERVICE:
322                display = mRes.getString(R.string.radioInfo_service_in);
323                break;
324            case ServiceState.STATE_OUT_OF_SERVICE:
325                // Set signal strength to 0 when service state is STATE_OUT_OF_SERVICE
326                mSignalStrength.setSummary("0");
327            case ServiceState.STATE_EMERGENCY_ONLY:
328                // Set summary string of service state to radioInfo_service_out when
329                // service state is both STATE_OUT_OF_SERVICE & STATE_EMERGENCY_ONLY
330                display = mRes.getString(R.string.radioInfo_service_out);
331                break;
332            case ServiceState.STATE_POWER_OFF:
333                display = mRes.getString(R.string.radioInfo_service_off);
334                // Also set signal strength to 0
335                mSignalStrength.setSummary("0");
336                break;
337        }
338
339        setSummaryText(KEY_SERVICE_STATE, display);
340
341        if (serviceState.getRoaming()) {
342            setSummaryText(KEY_ROAMING_STATE, mRes.getString(R.string.radioInfo_roaming_in));
343        } else {
344            setSummaryText(KEY_ROAMING_STATE, mRes.getString(R.string.radioInfo_roaming_not));
345        }
346        setSummaryText(KEY_OPERATOR_NAME, serviceState.getOperatorAlphaLong());
347    }
348
349    private void updateAreaInfo(String areaInfo) {
350        if (areaInfo != null) {
351            setSummaryText(KEY_LATEST_AREA_INFO, areaInfo);
352        }
353    }
354
355    void updateSignalStrength(SignalStrength signalStrength) {
356        if (mSignalStrength != null) {
357            final int state = mPhone.getServiceState().getState();
358            Resources r = getResources();
359
360            if ((ServiceState.STATE_OUT_OF_SERVICE == state) ||
361                    (ServiceState.STATE_POWER_OFF == state)) {
362                mSignalStrength.setSummary("0");
363                return;
364            }
365
366            int signalDbm = signalStrength.getDbm();
367            int signalAsu = signalStrength.getAsuLevel();
368
369            if (-1 == signalDbm) {
370                signalDbm = 0;
371            }
372
373            if (-1 == signalAsu) {
374                signalAsu = 0;
375            }
376
377            mSignalStrength.setSummary(r.getString(R.string.sim_signal_strength,
378                        signalDbm, signalAsu));
379        }
380    }
381
382    private void updatePreference() {
383        if (mPhone.getPhoneType() != TelephonyManager.PHONE_TYPE_CDMA) {
384            // only show area info when SIM country is Brazil
385            if (COUNTRY_ABBREVIATION_BRAZIL.equals(mTelephonyManager.getSimCountryIso(
386                            mSir.getSubscriptionId()))) {
387                mShowLatestAreaInfo = true;
388            }
389        }
390        PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(
391                mSir.getSubscriptionId());
392        mShowICCID = carrierConfig.getBoolean(
393                CarrierConfigManager.KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL);
394
395        String rawNumber = mTelephonyManager.getLine1Number(mSir.getSubscriptionId());
396        String formattedNumber = null;
397        if (!TextUtils.isEmpty(rawNumber)) {
398            formattedNumber = PhoneNumberUtils.formatNumber(rawNumber);
399        }
400        // If formattedNumber is null or empty, it'll display as "Unknown".
401        setSummaryText(KEY_PHONE_NUMBER, formattedNumber);
402        setSummaryText(KEY_IMEI, mPhone.getImei());
403        setSummaryText(KEY_IMEI_SV, mPhone.getDeviceSvn());
404
405        if (!mShowICCID) {
406            removePreferenceFromScreen(KEY_ICCID);
407        } else {
408            // Get ICCID, which is SIM serial number
409            String iccid = mTelephonyManager.getSimSerialNumber(mSir.getSubscriptionId());
410            setSummaryText(KEY_ICCID, iccid);
411        }
412
413        if (!mShowLatestAreaInfo) {
414            removePreferenceFromScreen(KEY_LATEST_AREA_INFO);
415        }
416    }
417
418    private void updatePhoneInfos() {
419        if (mSir != null) {
420            // TODO: http://b/23763013
421            final Phone phone = PhoneFactory.getPhone(SubscriptionManager.getPhoneId(
422                        mSir.getSubscriptionId()));
423            if (UserManager.get(getContext()).isAdminUser()
424                    && SubscriptionManager.isValidSubscriptionId(mSir.getSubscriptionId())) {
425                if (phone == null) {
426                    Log.e(TAG, "Unable to locate a phone object for the given Subscription ID.");
427                    return;
428                }
429
430                mPhone = phone;
431                mPhoneStateListener = new PhoneStateListener(mSir.getSubscriptionId()) {
432                    @Override
433                    public void onDataConnectionStateChanged(int state) {
434                        updateDataState();
435                        updateNetworkType();
436                    }
437
438                    @Override
439                    public void onSignalStrengthsChanged(SignalStrength signalStrength) {
440                        updateSignalStrength(signalStrength);
441                    }
442
443                    @Override
444                    public void onServiceStateChanged(ServiceState serviceState) {
445                        updateServiceState(serviceState);
446                    }
447                };
448            }
449        }
450    }
451    private OnTabChangeListener mTabListener = new OnTabChangeListener() {
452        @Override
453        public void onTabChanged(String tabId) {
454            final int slotId = Integer.parseInt(tabId);
455            mSir = mSelectableSubInfos.get(slotId);
456
457            // The User has changed tab; update the SIM information.
458            updatePhoneInfos();
459            mTelephonyManager.listen(mPhoneStateListener,
460                    PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
461                    | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS
462                    | PhoneStateListener.LISTEN_SERVICE_STATE);
463            updateDataState();
464            updateNetworkType();
465            updatePreference();
466        }
467    };
468
469    private TabContentFactory mEmptyTabContent = new TabContentFactory() {
470        @Override
471        public View createTabContent(String tag) {
472            return new View(mTabHost.getContext());
473        }
474    };
475
476    private TabSpec buildTabSpec(String tag, String title) {
477        return mTabHost.newTabSpec(tag).setIndicator(title).setContent(
478                mEmptyTabContent);
479    }
480}
481