| /* |
| * Copyright (C) 2017 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.simstatus; |
| |
| import static android.content.Context.CARRIER_CONFIG_SERVICE; |
| import static android.content.Context.EUICC_SERVICE; |
| import static android.content.Context.TELEPHONY_SERVICE; |
| |
| import android.Manifest; |
| import android.content.BroadcastReceiver; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.IntentFilter; |
| import android.content.pm.PackageManager; |
| import android.content.res.Resources; |
| import android.os.Bundle; |
| import android.os.PersistableBundle; |
| import android.os.UserHandle; |
| 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.telephony.euicc.EuiccManager; |
| import android.text.BidiFormatter; |
| import android.text.TextDirectionHeuristics; |
| import android.text.TextUtils; |
| import android.util.Log; |
| |
| import androidx.annotation.NonNull; |
| import androidx.annotation.VisibleForTesting; |
| |
| import com.android.settings.R; |
| import com.android.settingslib.DeviceInfoUtils; |
| import com.android.settingslib.core.lifecycle.Lifecycle; |
| import com.android.settingslib.core.lifecycle.LifecycleObserver; |
| import com.android.settingslib.core.lifecycle.events.OnPause; |
| import com.android.settingslib.core.lifecycle.events.OnResume; |
| |
| import java.util.List; |
| |
| public class SimStatusDialogController implements LifecycleObserver, OnResume, OnPause { |
| |
| private final static String TAG = "SimStatusDialogCtrl"; |
| |
| @VisibleForTesting |
| final static int NETWORK_PROVIDER_VALUE_ID = R.id.operator_name_value; |
| @VisibleForTesting |
| final static int PHONE_NUMBER_VALUE_ID = R.id.number_value; |
| @VisibleForTesting |
| final static int CELLULAR_NETWORK_STATE = R.id.data_state_value; |
| @VisibleForTesting |
| final static int OPERATOR_INFO_LABEL_ID = R.id.latest_area_info_label; |
| @VisibleForTesting |
| final static int OPERATOR_INFO_VALUE_ID = R.id.latest_area_info_value; |
| @VisibleForTesting |
| final static int SERVICE_STATE_VALUE_ID = R.id.service_state_value; |
| @VisibleForTesting |
| final static int SIGNAL_STRENGTH_LABEL_ID = R.id.signal_strength_label; |
| @VisibleForTesting |
| final static int SIGNAL_STRENGTH_VALUE_ID = R.id.signal_strength_value; |
| @VisibleForTesting |
| final static int CELL_VOICE_NETWORK_TYPE_VALUE_ID = R.id.voice_network_type_value; |
| @VisibleForTesting |
| final static int CELL_DATA_NETWORK_TYPE_VALUE_ID = R.id.data_network_type_value; |
| @VisibleForTesting |
| final static int ROAMING_INFO_VALUE_ID = R.id.roaming_state_value; |
| @VisibleForTesting |
| final static int ICCID_INFO_LABEL_ID = R.id.icc_id_label; |
| @VisibleForTesting |
| final static int ICCID_INFO_VALUE_ID = R.id.icc_id_value; |
| @VisibleForTesting |
| final static int EID_INFO_VALUE_ID = R.id.esim_id_value; |
| @VisibleForTesting |
| final static int IMS_REGISTRATION_STATE_LABEL_ID = R.id.ims_reg_state_label; |
| @VisibleForTesting |
| final static int IMS_REGISTRATION_STATE_VALUE_ID = R.id.ims_reg_state_value; |
| |
| private final static String CB_AREA_INFO_RECEIVED_ACTION = |
| "com.android.cellbroadcastreceiver.CB_AREA_INFO_RECEIVED"; |
| private final static String GET_LATEST_CB_AREA_INFO_ACTION = |
| "com.android.cellbroadcastreceiver.GET_LATEST_CB_AREA_INFO"; |
| private final static String CELL_BROADCAST_RECEIVER_APP = "com.android.cellbroadcastreceiver"; |
| |
| private final SimStatusDialogFragment mDialog; |
| private final SubscriptionInfo mSubscriptionInfo; |
| private final TelephonyManager mTelephonyManager; |
| private final CarrierConfigManager mCarrierConfigManager; |
| private final EuiccManager mEuiccManager; |
| private final Resources mRes; |
| private final Context mContext; |
| |
| private boolean mShowLatestAreaInfo; |
| |
| private final BroadcastReceiver mAreaInfoReceiver = new BroadcastReceiver() { |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| final String action = intent.getAction(); |
| if (TextUtils.equals(action, CB_AREA_INFO_RECEIVED_ACTION)) { |
| final Bundle extras = intent.getExtras(); |
| if (extras == null) { |
| return; |
| } |
| final CellBroadcastMessage cbMessage = (CellBroadcastMessage) extras.get("message"); |
| if (cbMessage != null |
| && mSubscriptionInfo.getSubscriptionId() == cbMessage.getSubId()) { |
| final String latestAreaInfo = cbMessage.getMessageBody(); |
| mDialog.setText(OPERATOR_INFO_VALUE_ID, latestAreaInfo); |
| } |
| } |
| } |
| }; |
| |
| private PhoneStateListener mPhoneStateListener; |
| |
| public SimStatusDialogController(@NonNull SimStatusDialogFragment dialog, Lifecycle lifecycle, |
| int slotId) { |
| mDialog = dialog; |
| mContext = dialog.getContext(); |
| mSubscriptionInfo = getPhoneSubscriptionInfo(slotId); |
| mTelephonyManager = (TelephonyManager) mContext.getSystemService( |
| TELEPHONY_SERVICE); |
| mCarrierConfigManager = (CarrierConfigManager) mContext.getSystemService( |
| CARRIER_CONFIG_SERVICE); |
| mEuiccManager = (EuiccManager) mContext.getSystemService(EUICC_SERVICE); |
| |
| mRes = mContext.getResources(); |
| |
| if (lifecycle != null) { |
| lifecycle.addObserver(this); |
| } |
| } |
| |
| public void initialize() { |
| updateEid(); |
| |
| if (mSubscriptionInfo == null) { |
| return; |
| } |
| |
| mPhoneStateListener = getPhoneStateListener(); |
| |
| final ServiceState serviceState = getCurrentServiceState(); |
| updateNetworkProvider(serviceState); |
| updatePhoneNumber(); |
| updateLatestAreaInfo(); |
| updateServiceState(serviceState); |
| updateSignalStrength(getSignalStrength()); |
| updateNetworkType(); |
| updateRoamingStatus(serviceState); |
| updateIccidNumber(); |
| updateImsRegistrationState(); |
| } |
| |
| @Override |
| public void onResume() { |
| if (mSubscriptionInfo == null) { |
| return; |
| } |
| |
| mTelephonyManager.listen(mPhoneStateListener, |
| PhoneStateListener.LISTEN_DATA_CONNECTION_STATE |
| | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS |
| | PhoneStateListener.LISTEN_SERVICE_STATE); |
| |
| if (mShowLatestAreaInfo) { |
| mContext.registerReceiver(mAreaInfoReceiver, |
| new IntentFilter(CB_AREA_INFO_RECEIVED_ACTION), |
| Manifest.permission.RECEIVE_EMERGENCY_BROADCAST, null /* scheduler */); |
| // Ask CellBroadcastReceiver to broadcast the latest area info received |
| final Intent getLatestIntent = new Intent(GET_LATEST_CB_AREA_INFO_ACTION); |
| getLatestIntent.setPackage(CELL_BROADCAST_RECEIVER_APP); |
| mContext.sendBroadcastAsUser(getLatestIntent, UserHandle.ALL, |
| Manifest.permission.RECEIVE_EMERGENCY_BROADCAST); |
| } |
| } |
| |
| @Override |
| public void onPause() { |
| if (mSubscriptionInfo == null) { |
| return; |
| } |
| |
| mTelephonyManager.listen(mPhoneStateListener, |
| PhoneStateListener.LISTEN_NONE); |
| |
| if (mShowLatestAreaInfo) { |
| mContext.unregisterReceiver(mAreaInfoReceiver); |
| } |
| } |
| |
| private void updateNetworkProvider(ServiceState serviceState) { |
| mDialog.setText(NETWORK_PROVIDER_VALUE_ID, serviceState.getOperatorAlphaLong()); |
| } |
| |
| private void updatePhoneNumber() { |
| // If formattedNumber is null or empty, it'll display as "Unknown". |
| mDialog.setText(PHONE_NUMBER_VALUE_ID, BidiFormatter.getInstance().unicodeWrap( |
| getPhoneNumber(), TextDirectionHeuristics.LTR)); |
| } |
| |
| private void updateDataState(int state) { |
| String networkStateValue; |
| |
| switch (state) { |
| case TelephonyManager.DATA_CONNECTED: |
| networkStateValue = mRes.getString(R.string.radioInfo_data_connected); |
| break; |
| case TelephonyManager.DATA_SUSPENDED: |
| networkStateValue = mRes.getString(R.string.radioInfo_data_suspended); |
| break; |
| case TelephonyManager.DATA_CONNECTING: |
| networkStateValue = mRes.getString(R.string.radioInfo_data_connecting); |
| break; |
| case TelephonyManager.DATA_DISCONNECTED: |
| networkStateValue = mRes.getString(R.string.radioInfo_data_disconnected); |
| break; |
| default: |
| networkStateValue = mRes.getString(R.string.radioInfo_unknown); |
| break; |
| } |
| |
| mDialog.setText(CELLULAR_NETWORK_STATE, networkStateValue); |
| } |
| |
| |
| private void updateLatestAreaInfo() { |
| mShowLatestAreaInfo = Resources.getSystem().getBoolean( |
| com.android.internal.R.bool.config_showAreaUpdateInfoSettings) |
| && mTelephonyManager.getPhoneType() != TelephonyManager.PHONE_TYPE_CDMA; |
| |
| if (!mShowLatestAreaInfo) { |
| mDialog.removeSettingFromScreen(OPERATOR_INFO_LABEL_ID); |
| mDialog.removeSettingFromScreen(OPERATOR_INFO_VALUE_ID); |
| } |
| } |
| |
| private void updateServiceState(ServiceState serviceState) { |
| final int state = serviceState.getState(); |
| if (state == ServiceState.STATE_OUT_OF_SERVICE || state == ServiceState.STATE_POWER_OFF) { |
| resetSignalStrength(); |
| } |
| |
| String serviceStateValue; |
| |
| switch (state) { |
| case ServiceState.STATE_IN_SERVICE: |
| serviceStateValue = mRes.getString(R.string.radioInfo_service_in); |
| break; |
| case ServiceState.STATE_OUT_OF_SERVICE: |
| 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 |
| serviceStateValue = mRes.getString(R.string.radioInfo_service_out); |
| break; |
| case ServiceState.STATE_POWER_OFF: |
| serviceStateValue = mRes.getString(R.string.radioInfo_service_off); |
| break; |
| default: |
| serviceStateValue = mRes.getString(R.string.radioInfo_unknown); |
| break; |
| } |
| |
| mDialog.setText(SERVICE_STATE_VALUE_ID, serviceStateValue); |
| } |
| |
| private void updateSignalStrength(SignalStrength signalStrength) { |
| final int subscriptionId = mSubscriptionInfo.getSubscriptionId(); |
| final PersistableBundle carrierConfig = |
| mCarrierConfigManager.getConfigForSubId(subscriptionId); |
| // by default we show the signal strength |
| boolean showSignalStrength = true; |
| if (carrierConfig != null) { |
| showSignalStrength = carrierConfig.getBoolean( |
| CarrierConfigManager.KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL); |
| } |
| if (!showSignalStrength) { |
| mDialog.removeSettingFromScreen(SIGNAL_STRENGTH_LABEL_ID); |
| mDialog.removeSettingFromScreen(SIGNAL_STRENGTH_VALUE_ID); |
| return; |
| } |
| |
| final int state = getCurrentServiceState().getState(); |
| |
| if ((ServiceState.STATE_OUT_OF_SERVICE == state) || |
| (ServiceState.STATE_POWER_OFF == state)) { |
| return; |
| } |
| |
| int signalDbm = getDbm(signalStrength); |
| int signalAsu = getAsuLevel(signalStrength); |
| |
| if (signalDbm == -1) { |
| signalDbm = 0; |
| } |
| |
| if (signalAsu == -1) { |
| signalAsu = 0; |
| } |
| |
| mDialog.setText(SIGNAL_STRENGTH_VALUE_ID, mRes.getString(R.string.sim_signal_strength, |
| signalDbm, signalAsu)); |
| } |
| |
| private void resetSignalStrength() { |
| mDialog.setText(SIGNAL_STRENGTH_VALUE_ID, "0"); |
| } |
| |
| private void updateNetworkType() { |
| // Whether EDGE, UMTS, etc... |
| String dataNetworkTypeName = null; |
| String voiceNetworkTypeName = null; |
| final int subId = mSubscriptionInfo.getSubscriptionId(); |
| final int actualDataNetworkType = mTelephonyManager.getDataNetworkType(subId); |
| final int actualVoiceNetworkType = mTelephonyManager.getVoiceNetworkType(subId); |
| if (TelephonyManager.NETWORK_TYPE_UNKNOWN != actualDataNetworkType) { |
| dataNetworkTypeName = mTelephonyManager.getNetworkTypeName(actualDataNetworkType); |
| } |
| if (TelephonyManager.NETWORK_TYPE_UNKNOWN != actualVoiceNetworkType) { |
| voiceNetworkTypeName = mTelephonyManager.getNetworkTypeName(actualVoiceNetworkType); |
| } |
| |
| boolean show4GForLTE = false; |
| try { |
| final Context con = mContext.createPackageContext( |
| "com.android.systemui", 0 /* flags */); |
| final int id = con.getResources().getIdentifier("config_show4GForLTE", |
| "bool" /* default type */, "com.android.systemui" /* default package */); |
| show4GForLTE = con.getResources().getBoolean(id); |
| } catch (PackageManager.NameNotFoundException e) { |
| Log.e(TAG, "NameNotFoundException for show4GForLTE"); |
| } |
| |
| if (show4GForLTE) { |
| if ("LTE".equals(dataNetworkTypeName)) { |
| dataNetworkTypeName = "4G"; |
| } |
| if ("LTE".equals(voiceNetworkTypeName)) { |
| voiceNetworkTypeName = "4G"; |
| } |
| } |
| |
| mDialog.setText(CELL_VOICE_NETWORK_TYPE_VALUE_ID, voiceNetworkTypeName); |
| mDialog.setText(CELL_DATA_NETWORK_TYPE_VALUE_ID, dataNetworkTypeName); |
| } |
| |
| private void updateRoamingStatus(ServiceState serviceState) { |
| if (serviceState.getRoaming()) { |
| mDialog.setText(ROAMING_INFO_VALUE_ID, mRes.getString(R.string.radioInfo_roaming_in)); |
| } else { |
| mDialog.setText(ROAMING_INFO_VALUE_ID, mRes.getString(R.string.radioInfo_roaming_not)); |
| } |
| } |
| |
| private void updateIccidNumber() { |
| final int subscriptionId = mSubscriptionInfo.getSubscriptionId(); |
| final PersistableBundle carrierConfig = |
| mCarrierConfigManager.getConfigForSubId(subscriptionId); |
| // do not show iccid by default |
| boolean showIccId = false; |
| if (carrierConfig != null) { |
| showIccId = carrierConfig.getBoolean( |
| CarrierConfigManager.KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL); |
| } |
| if (!showIccId) { |
| mDialog.removeSettingFromScreen(ICCID_INFO_LABEL_ID); |
| mDialog.removeSettingFromScreen(ICCID_INFO_VALUE_ID); |
| } else { |
| mDialog.setText(ICCID_INFO_VALUE_ID, getSimSerialNumber(subscriptionId)); |
| } |
| } |
| |
| private void updateEid() { |
| mDialog.setText(EID_INFO_VALUE_ID, mEuiccManager.getEid()); |
| } |
| |
| private void updateImsRegistrationState() { |
| final int subscriptionId = mSubscriptionInfo.getSubscriptionId(); |
| final PersistableBundle carrierConfig = |
| mCarrierConfigManager.getConfigForSubId(subscriptionId); |
| final boolean showImsRegState = carrierConfig == null ? false : |
| carrierConfig.getBoolean(CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL); |
| if (showImsRegState) { |
| final boolean isImsRegistered = mTelephonyManager.isImsRegistered(subscriptionId); |
| mDialog.setText(IMS_REGISTRATION_STATE_VALUE_ID, mRes.getString(isImsRegistered ? |
| R.string.ims_reg_status_registered : R.string.ims_reg_status_not_registered)); |
| } else { |
| mDialog.removeSettingFromScreen(IMS_REGISTRATION_STATE_LABEL_ID); |
| mDialog.removeSettingFromScreen(IMS_REGISTRATION_STATE_VALUE_ID); |
| } |
| } |
| |
| private SubscriptionInfo getPhoneSubscriptionInfo(int slotId) { |
| final List<SubscriptionInfo> subscriptionInfoList = SubscriptionManager.from( |
| mContext).getActiveSubscriptionInfoList(); |
| if (subscriptionInfoList != null && subscriptionInfoList.size() > slotId) { |
| return subscriptionInfoList.get(slotId); |
| } else { |
| return null; |
| } |
| } |
| |
| @VisibleForTesting |
| ServiceState getCurrentServiceState() { |
| return mTelephonyManager.getServiceStateForSubscriber( |
| mSubscriptionInfo.getSubscriptionId()); |
| } |
| |
| @VisibleForTesting |
| int getDbm(SignalStrength signalStrength) { |
| return signalStrength.getDbm(); |
| } |
| |
| @VisibleForTesting |
| int getAsuLevel(SignalStrength signalStrength) { |
| return signalStrength.getAsuLevel(); |
| } |
| |
| @VisibleForTesting |
| PhoneStateListener getPhoneStateListener() { |
| return new PhoneStateListener( |
| mSubscriptionInfo.getSubscriptionId()) { |
| @Override |
| public void onDataConnectionStateChanged(int state) { |
| updateDataState(state); |
| updateNetworkType(); |
| } |
| |
| @Override |
| public void onSignalStrengthsChanged(SignalStrength signalStrength) { |
| updateSignalStrength(signalStrength); |
| } |
| |
| @Override |
| public void onServiceStateChanged(ServiceState serviceState) { |
| updateNetworkProvider(serviceState); |
| updateServiceState(serviceState); |
| updateRoamingStatus(serviceState); |
| } |
| }; |
| } |
| |
| @VisibleForTesting |
| String getPhoneNumber() { |
| return DeviceInfoUtils.getFormattedPhoneNumber(mContext, mSubscriptionInfo); |
| } |
| |
| @VisibleForTesting |
| SignalStrength getSignalStrength() { |
| return mTelephonyManager.getSignalStrength(); |
| } |
| |
| @VisibleForTesting |
| String getSimSerialNumber(int subscriptionId) { |
| return mTelephonyManager.getSimSerialNumber(subscriptionId); |
| } |
| } |