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 android.content.BroadcastReceiver;
20import android.content.Context;
21import android.content.Intent;
22import android.content.IntentFilter;
23import android.content.pm.PackageManager.NameNotFoundException;
24import android.content.res.Resources;
25import android.os.Bundle;
26import android.os.PersistableBundle;
27import android.os.UserHandle;
28import android.os.UserManager;
29import android.support.v7.preference.Preference;
30import android.telephony.CarrierConfigManager;
31import android.telephony.CellBroadcastMessage;
32import android.telephony.PhoneStateListener;
33import android.telephony.ServiceState;
34import android.telephony.SignalStrength;
35import android.telephony.SubscriptionInfo;
36import android.telephony.SubscriptionManager;
37import android.telephony.TelephonyManager;
38import android.text.TextUtils;
39import android.util.Log;
40import android.view.LayoutInflater;
41import android.view.View;
42import android.view.ViewGroup;
43import android.widget.ListView;
44import android.widget.TabHost;
45import android.widget.TabHost.OnTabChangeListener;
46import android.widget.TabHost.TabContentFactory;
47import android.widget.TabHost.TabSpec;
48import android.widget.TabWidget;
49
50import com.android.internal.logging.MetricsProto.MetricsEvent;
51import com.android.internal.telephony.DefaultPhoneNotifier;
52import com.android.internal.telephony.Phone;
53import com.android.internal.telephony.PhoneFactory;
54import com.android.settings.R;
55import com.android.settings.SettingsPreferenceFragment;
56import com.android.settings.Utils;
57import com.android.settingslib.DeviceInfoUtils;
58
59import java.util.List;
60
61import static android.content.Context.CARRIER_CONFIG_SERVICE;
62import static android.content.Context.TELEPHONY_SERVICE;
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                        && mSir.getSubscriptionId() == cbMessage.getSubId()) {
133                    String latestAreaInfo = cbMessage.getMessageBody();
134                    updateAreaInfo(latestAreaInfo);
135                }
136            }
137        }
138    };
139
140    @Override
141    public void onCreate(Bundle icicle) {
142        super.onCreate(icicle);
143        mTelephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
144        mCarrierConfigManager = (CarrierConfigManager) getSystemService(CARRIER_CONFIG_SERVICE);
145
146        mSelectableSubInfos = SubscriptionManager.from(getContext())
147                .getActiveSubscriptionInfoList();
148
149        addPreferencesFromResource(R.xml.device_info_sim_status);
150
151        mRes = getResources();
152        mDefaultText = mRes.getString(R.string.device_info_default);
153        // Note - missing in zaku build, be careful later...
154        mSignalStrength = findPreference(KEY_SIGNAL_STRENGTH);
155    }
156
157    @Override
158    public View onCreateView(LayoutInflater inflater, ViewGroup container,
159            Bundle savedInstanceState) {
160        if (mSelectableSubInfos == null) {
161            mSir = null;
162        } else {
163            mSir = mSelectableSubInfos.size() > 0 ? mSelectableSubInfos.get(0) : null;
164
165            if (mSelectableSubInfos.size() > 1) {
166                View view = inflater.inflate(R.layout.icc_lock_tabs, container, false);
167                final ViewGroup prefs_container = (ViewGroup) view.findViewById(
168                        R.id.prefs_container);
169                Utils.prepareCustomPreferencesList(container, view, prefs_container, false);
170                View prefs = super.onCreateView(inflater, prefs_container, savedInstanceState);
171                prefs_container.addView(prefs);
172
173                mTabHost = (TabHost) view.findViewById(android.R.id.tabhost);
174                mTabWidget = (TabWidget) view.findViewById(android.R.id.tabs);
175                mListView = (ListView) view.findViewById(android.R.id.list);
176
177                mTabHost.setup();
178                mTabHost.setOnTabChangedListener(mTabListener);
179                mTabHost.clearAllTabs();
180
181                for (int i = 0; i < mSelectableSubInfos.size(); i++) {
182                    mTabHost.addTab(buildTabSpec(String.valueOf(i),
183                            String.valueOf(mSelectableSubInfos.get(i).getDisplayName())));
184                }
185                return view;
186            }
187        }
188        return super.onCreateView(inflater, container, savedInstanceState);
189    }
190
191    @Override
192    public void onViewCreated(View view, Bundle savedInstanceState) {
193        super.onViewCreated(view, savedInstanceState);
194
195        updatePhoneInfos();
196    }
197
198    @Override
199    protected int getMetricsCategory() {
200        return MetricsEvent.DEVICEINFO_SIM_STATUS;
201    }
202
203    @Override
204    public void onResume() {
205        super.onResume();
206        if (mPhone != null) {
207            updatePreference();
208
209            updateSignalStrength(mPhone.getSignalStrength());
210            updateServiceState(mPhone.getServiceState());
211            updateDataState();
212            mTelephonyManager.listen(mPhoneStateListener,
213                    PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
214                    | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS
215                    | PhoneStateListener.LISTEN_SERVICE_STATE);
216            if (mShowLatestAreaInfo) {
217                getContext().registerReceiver(mAreaInfoReceiver,
218                        new IntentFilter(CB_AREA_INFO_RECEIVED_ACTION),
219                        CB_AREA_INFO_SENDER_PERMISSION, null);
220                // Ask CellBroadcastReceiver to broadcast the latest area info received
221                Intent getLatestIntent = new Intent(GET_LATEST_CB_AREA_INFO_ACTION);
222                getContext().sendBroadcastAsUser(getLatestIntent, UserHandle.ALL,
223                        CB_AREA_INFO_SENDER_PERMISSION);
224            }
225        }
226    }
227
228    @Override
229    public void onPause() {
230        super.onPause();
231
232        if (mPhone != null) {
233            mTelephonyManager.listen(mPhoneStateListener,
234                    PhoneStateListener.LISTEN_NONE);
235        }
236        if (mShowLatestAreaInfo) {
237            getContext().unregisterReceiver(mAreaInfoReceiver);
238        }
239    }
240
241    /**
242     * Removes the specified preference, if it exists.
243     * @param key the key for the Preference item
244     */
245    private void removePreferenceFromScreen(String key) {
246        Preference pref = findPreference(key);
247        if (pref != null) {
248            getPreferenceScreen().removePreference(pref);
249        }
250    }
251
252    private void setSummaryText(String key, String text) {
253        if (TextUtils.isEmpty(text)) {
254            text = mDefaultText;
255        }
256        // some preferences may be missing
257        final Preference preference = findPreference(key);
258        if (preference != null) {
259            preference.setSummary(text);
260        }
261    }
262
263    private void updateNetworkType() {
264        // Whether EDGE, UMTS, etc...
265        String networktype = null;
266        final int subId = mSir.getSubscriptionId();
267        final int actualDataNetworkType = mTelephonyManager.getDataNetworkType(
268                mSir.getSubscriptionId());
269        final int actualVoiceNetworkType = mTelephonyManager.getVoiceNetworkType(
270                mSir.getSubscriptionId());
271        if (TelephonyManager.NETWORK_TYPE_UNKNOWN != actualDataNetworkType) {
272            networktype = mTelephonyManager.getNetworkTypeName(actualDataNetworkType);
273        } else if (TelephonyManager.NETWORK_TYPE_UNKNOWN != actualVoiceNetworkType) {
274            networktype = mTelephonyManager.getNetworkTypeName(actualVoiceNetworkType);
275        }
276
277        boolean show4GForLTE = false;
278        try {
279            Context con = getActivity().createPackageContext("com.android.systemui", 0);
280            int id = con.getResources().getIdentifier("config_show4GForLTE",
281                    "bool", "com.android.systemui");
282            show4GForLTE = con.getResources().getBoolean(id);
283        } catch (NameNotFoundException e) {
284            Log.e(TAG, "NameNotFoundException for show4GFotLTE");
285        }
286
287        if (networktype != null && networktype.equals("LTE") && show4GForLTE) {
288            networktype = "4G";
289        }
290        setSummaryText(KEY_NETWORK_TYPE, networktype);
291    }
292
293    private void updateDataState() {
294        final int state =
295                DefaultPhoneNotifier.convertDataState(mPhone.getDataConnectionState());
296
297        String display = mRes.getString(R.string.radioInfo_unknown);
298
299        switch (state) {
300            case TelephonyManager.DATA_CONNECTED:
301                display = mRes.getString(R.string.radioInfo_data_connected);
302                break;
303            case TelephonyManager.DATA_SUSPENDED:
304                display = mRes.getString(R.string.radioInfo_data_suspended);
305                break;
306            case TelephonyManager.DATA_CONNECTING:
307                display = mRes.getString(R.string.radioInfo_data_connecting);
308                break;
309            case TelephonyManager.DATA_DISCONNECTED:
310                display = mRes.getString(R.string.radioInfo_data_disconnected);
311                break;
312        }
313
314        setSummaryText(KEY_DATA_STATE, display);
315    }
316
317    private void updateServiceState(ServiceState serviceState) {
318        final int state = serviceState.getState();
319        String display = mRes.getString(R.string.radioInfo_unknown);
320
321        switch (state) {
322            case ServiceState.STATE_IN_SERVICE:
323                display = mRes.getString(R.string.radioInfo_service_in);
324                break;
325            case ServiceState.STATE_OUT_OF_SERVICE:
326                // Set signal strength to 0 when service state is STATE_OUT_OF_SERVICE
327                mSignalStrength.setSummary("0");
328            case ServiceState.STATE_EMERGENCY_ONLY:
329                // Set summary string of service state to radioInfo_service_out when
330                // service state is both STATE_OUT_OF_SERVICE & STATE_EMERGENCY_ONLY
331                display = mRes.getString(R.string.radioInfo_service_out);
332                break;
333            case ServiceState.STATE_POWER_OFF:
334                display = mRes.getString(R.string.radioInfo_service_off);
335                // Also set signal strength to 0
336                mSignalStrength.setSummary("0");
337                break;
338        }
339
340        setSummaryText(KEY_SERVICE_STATE, display);
341
342        if (serviceState.getRoaming()) {
343            setSummaryText(KEY_ROAMING_STATE, mRes.getString(R.string.radioInfo_roaming_in));
344        } else {
345            setSummaryText(KEY_ROAMING_STATE, mRes.getString(R.string.radioInfo_roaming_not));
346        }
347        setSummaryText(KEY_OPERATOR_NAME, serviceState.getOperatorAlphaLong());
348    }
349
350    private void updateAreaInfo(String areaInfo) {
351        if (areaInfo != null) {
352            setSummaryText(KEY_LATEST_AREA_INFO, areaInfo);
353        }
354    }
355
356    void updateSignalStrength(SignalStrength signalStrength) {
357        if (mSignalStrength != null) {
358            final int state = mPhone.getServiceState().getState();
359            Resources r = getResources();
360
361            if ((ServiceState.STATE_OUT_OF_SERVICE == state) ||
362                    (ServiceState.STATE_POWER_OFF == state)) {
363                mSignalStrength.setSummary("0");
364                return;
365            }
366
367            int signalDbm = signalStrength.getDbm();
368            int signalAsu = signalStrength.getAsuLevel();
369
370            if (-1 == signalDbm) {
371                signalDbm = 0;
372            }
373
374            if (-1 == signalAsu) {
375                signalAsu = 0;
376            }
377
378            mSignalStrength.setSummary(r.getString(R.string.sim_signal_strength,
379                        signalDbm, signalAsu));
380        }
381    }
382
383    private void updatePreference() {
384        if (mPhone.getPhoneType() != TelephonyManager.PHONE_TYPE_CDMA) {
385            // only show area info when SIM country is Brazil
386            if (COUNTRY_ABBREVIATION_BRAZIL.equals(mTelephonyManager.getSimCountryIso(
387                            mSir.getSubscriptionId()))) {
388                mShowLatestAreaInfo = true;
389            }
390        }
391        PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(
392                mSir.getSubscriptionId());
393        mShowICCID = carrierConfig.getBoolean(
394                CarrierConfigManager.KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL);
395
396
397        // If formattedNumber is null or empty, it'll display as "Unknown".
398        setSummaryText(KEY_PHONE_NUMBER,
399                DeviceInfoUtils.getFormattedPhoneNumber(getContext(), mSir));
400        setSummaryText(KEY_IMEI, mPhone.getImei());
401        setSummaryText(KEY_IMEI_SV, mPhone.getDeviceSvn());
402
403        if (!mShowICCID) {
404            removePreferenceFromScreen(KEY_ICCID);
405        } else {
406            // Get ICCID, which is SIM serial number
407            String iccid = mTelephonyManager.getSimSerialNumber(mSir.getSubscriptionId());
408            setSummaryText(KEY_ICCID, iccid);
409        }
410
411        if (!mShowLatestAreaInfo) {
412            removePreferenceFromScreen(KEY_LATEST_AREA_INFO);
413        }
414    }
415
416    private void updatePhoneInfos() {
417        if (mSir != null) {
418            // TODO: http://b/23763013
419            final Phone phone = PhoneFactory.getPhone(SubscriptionManager.getPhoneId(
420                        mSir.getSubscriptionId()));
421            if (UserManager.get(getContext()).isAdminUser()
422                    && SubscriptionManager.isValidSubscriptionId(mSir.getSubscriptionId())) {
423                if (phone == null) {
424                    Log.e(TAG, "Unable to locate a phone object for the given Subscription ID.");
425                    return;
426                }
427
428                mPhone = phone;
429                mPhoneStateListener = new PhoneStateListener(mSir.getSubscriptionId()) {
430                    @Override
431                    public void onDataConnectionStateChanged(int state) {
432                        updateDataState();
433                        updateNetworkType();
434                    }
435
436                    @Override
437                    public void onSignalStrengthsChanged(SignalStrength signalStrength) {
438                        updateSignalStrength(signalStrength);
439                    }
440
441                    @Override
442                    public void onServiceStateChanged(ServiceState serviceState) {
443                        updateServiceState(serviceState);
444                    }
445                };
446            }
447        }
448    }
449    private OnTabChangeListener mTabListener = new OnTabChangeListener() {
450        @Override
451        public void onTabChanged(String tabId) {
452            final int slotId = Integer.parseInt(tabId);
453            mSir = mSelectableSubInfos.get(slotId);
454
455            // The User has changed tab; update the SIM information.
456            updatePhoneInfos();
457            mTelephonyManager.listen(mPhoneStateListener,
458                    PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
459                    | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS
460                    | PhoneStateListener.LISTEN_SERVICE_STATE);
461            updateDataState();
462            updateNetworkType();
463            updatePreference();
464        }
465    };
466
467    private TabContentFactory mEmptyTabContent = new TabContentFactory() {
468        @Override
469        public View createTabContent(String tag) {
470            return new View(mTabHost.getContext());
471        }
472    };
473
474    private TabSpec buildTabSpec(String tag, String title) {
475        return mTabHost.newTabSpec(tag).setIndicator(title).setContent(
476                mEmptyTabContent);
477    }
478}
479