| /* |
| * Copyright (C) 2015 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.systemui.statusbar.policy; |
| |
| import android.content.Context; |
| import android.content.Intent; |
| import android.database.ContentObserver; |
| import android.net.NetworkCapabilities; |
| import android.os.Handler; |
| import android.os.Looper; |
| import android.provider.Settings.Global; |
| import android.telephony.Annotation; |
| import android.telephony.CdmaEriInformation; |
| import android.telephony.CellSignalStrength; |
| import android.telephony.CellSignalStrengthCdma; |
| import android.telephony.DisplayInfo; |
| 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.Html; |
| import android.text.TextUtils; |
| import android.util.Log; |
| |
| import com.android.internal.annotations.VisibleForTesting; |
| import com.android.settingslib.Utils; |
| import com.android.settingslib.graph.SignalDrawable; |
| import com.android.settingslib.net.SignalStrengthUtil; |
| import com.android.systemui.R; |
| import com.android.systemui.statusbar.policy.NetworkController.IconState; |
| import com.android.systemui.statusbar.policy.NetworkController.SignalCallback; |
| import com.android.systemui.statusbar.policy.NetworkControllerImpl.Config; |
| import com.android.systemui.statusbar.policy.NetworkControllerImpl.SubscriptionDefaults; |
| |
| import java.io.PrintWriter; |
| import java.util.BitSet; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Objects; |
| import java.util.concurrent.Executor; |
| |
| |
| public class MobileSignalController extends SignalController< |
| MobileSignalController.MobileState, MobileSignalController.MobileIconGroup> { |
| private final TelephonyManager mPhone; |
| private final SubscriptionDefaults mDefaults; |
| private final String mNetworkNameDefault; |
| private final String mNetworkNameSeparator; |
| private final ContentObserver mObserver; |
| @VisibleForTesting |
| final PhoneStateListener mPhoneStateListener; |
| // Save entire info for logging, we only use the id. |
| final SubscriptionInfo mSubscriptionInfo; |
| |
| // @VisibleForDemoMode |
| final Map<String, MobileIconGroup> mNetworkToIconLookup; |
| |
| // Since some pieces of the phone state are interdependent we store it locally, |
| // this could potentially become part of MobileState for simplification/complication |
| // of code. |
| private int mDataState = TelephonyManager.DATA_DISCONNECTED; |
| private DisplayInfo mDisplayInfo = new DisplayInfo(TelephonyManager.NETWORK_TYPE_UNKNOWN, |
| DisplayInfo.OVERRIDE_NETWORK_TYPE_NONE); |
| private ServiceState mServiceState; |
| private SignalStrength mSignalStrength; |
| private MobileIconGroup mDefaultIcons; |
| private Config mConfig; |
| @VisibleForTesting |
| boolean mInflateSignalStrengths = false; |
| |
| // TODO: Reduce number of vars passed in, if we have the NetworkController, probably don't |
| // need listener lists anymore. |
| public MobileSignalController(Context context, Config config, boolean hasMobileData, |
| TelephonyManager phone, CallbackHandler callbackHandler, |
| NetworkControllerImpl networkController, SubscriptionInfo info, |
| SubscriptionDefaults defaults, Looper receiverLooper) { |
| super("MobileSignalController(" + info.getSubscriptionId() + ")", context, |
| NetworkCapabilities.TRANSPORT_CELLULAR, callbackHandler, |
| networkController); |
| mNetworkToIconLookup = new HashMap<>(); |
| mConfig = config; |
| mPhone = phone; |
| mDefaults = defaults; |
| mSubscriptionInfo = info; |
| mPhoneStateListener = new MobilePhoneStateListener((new Handler(receiverLooper))::post); |
| mNetworkNameSeparator = getTextIfExists(R.string.status_bar_network_name_separator) |
| .toString(); |
| mNetworkNameDefault = getTextIfExists( |
| com.android.internal.R.string.lockscreen_carrier_default).toString(); |
| |
| mapIconSets(); |
| |
| String networkName = info.getCarrierName() != null ? info.getCarrierName().toString() |
| : mNetworkNameDefault; |
| mLastState.networkName = mCurrentState.networkName = networkName; |
| mLastState.networkNameData = mCurrentState.networkNameData = networkName; |
| mLastState.enabled = mCurrentState.enabled = hasMobileData; |
| mLastState.iconGroup = mCurrentState.iconGroup = mDefaultIcons; |
| // Get initial data sim state. |
| updateDataSim(); |
| mObserver = new ContentObserver(new Handler(receiverLooper)) { |
| @Override |
| public void onChange(boolean selfChange) { |
| updateTelephony(); |
| } |
| }; |
| } |
| |
| public void setConfiguration(Config config) { |
| mConfig = config; |
| updateInflateSignalStrength(); |
| mapIconSets(); |
| updateTelephony(); |
| } |
| |
| public void setAirplaneMode(boolean airplaneMode) { |
| mCurrentState.airplaneMode = airplaneMode; |
| notifyListenersIfNecessary(); |
| } |
| |
| public void setUserSetupComplete(boolean userSetup) { |
| mCurrentState.userSetup = userSetup; |
| notifyListenersIfNecessary(); |
| } |
| |
| @Override |
| public void updateConnectivity(BitSet connectedTransports, BitSet validatedTransports) { |
| boolean isValidated = validatedTransports.get(mTransportType); |
| mCurrentState.isDefault = connectedTransports.get(mTransportType); |
| // Only show this as not having connectivity if we are default. |
| mCurrentState.inetCondition = (isValidated || !mCurrentState.isDefault) ? 1 : 0; |
| notifyListenersIfNecessary(); |
| } |
| |
| public void setCarrierNetworkChangeMode(boolean carrierNetworkChangeMode) { |
| mCurrentState.carrierNetworkChangeMode = carrierNetworkChangeMode; |
| updateTelephony(); |
| } |
| |
| /** |
| * Start listening for phone state changes. |
| */ |
| public void registerListener() { |
| mPhone.listen(mPhoneStateListener, |
| PhoneStateListener.LISTEN_SERVICE_STATE |
| | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS |
| | PhoneStateListener.LISTEN_CALL_STATE |
| | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE |
| | PhoneStateListener.LISTEN_DATA_ACTIVITY |
| | PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE |
| | PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE |
| | PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED); |
| mContext.getContentResolver().registerContentObserver(Global.getUriFor(Global.MOBILE_DATA), |
| true, mObserver); |
| mContext.getContentResolver().registerContentObserver(Global.getUriFor( |
| Global.MOBILE_DATA + mSubscriptionInfo.getSubscriptionId()), |
| true, mObserver); |
| } |
| |
| /** |
| * Stop listening for phone state changes. |
| */ |
| public void unregisterListener() { |
| mPhone.listen(mPhoneStateListener, 0); |
| mContext.getContentResolver().unregisterContentObserver(mObserver); |
| } |
| |
| /** |
| * Produce a mapping of data network types to icon groups for simple and quick use in |
| * updateTelephony. |
| */ |
| private void mapIconSets() { |
| mNetworkToIconLookup.clear(); |
| |
| mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_EVDO_0), |
| TelephonyIcons.THREE_G); |
| mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_EVDO_A), |
| TelephonyIcons.THREE_G); |
| mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_EVDO_B), |
| TelephonyIcons.THREE_G); |
| mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_EHRPD), |
| TelephonyIcons.THREE_G); |
| mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_UMTS), |
| TelephonyIcons.THREE_G); |
| mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_TD_SCDMA), |
| TelephonyIcons.THREE_G); |
| |
| if (!mConfig.showAtLeast3G) { |
| mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_UNKNOWN), |
| TelephonyIcons.UNKNOWN); |
| mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_EDGE), |
| TelephonyIcons.E); |
| mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_CDMA), |
| TelephonyIcons.ONE_X); |
| mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_1xRTT), |
| TelephonyIcons.ONE_X); |
| |
| mDefaultIcons = TelephonyIcons.G; |
| } else { |
| mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_UNKNOWN), |
| TelephonyIcons.THREE_G); |
| mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_EDGE), |
| TelephonyIcons.THREE_G); |
| mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_CDMA), |
| TelephonyIcons.THREE_G); |
| mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_1xRTT), |
| TelephonyIcons.THREE_G); |
| mDefaultIcons = TelephonyIcons.THREE_G; |
| } |
| |
| MobileIconGroup hGroup = TelephonyIcons.THREE_G; |
| MobileIconGroup hPlusGroup = TelephonyIcons.THREE_G; |
| if (mConfig.show4gFor3g) { |
| hGroup = TelephonyIcons.FOUR_G; |
| hPlusGroup = TelephonyIcons.FOUR_G; |
| } else if (mConfig.hspaDataDistinguishable) { |
| hGroup = TelephonyIcons.H; |
| hPlusGroup = TelephonyIcons.H_PLUS; |
| } |
| |
| mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_HSDPA), hGroup); |
| mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_HSUPA), hGroup); |
| mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_HSPA), hGroup); |
| mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_HSPAP), hPlusGroup); |
| |
| if (mConfig.show4gForLte) { |
| mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_LTE), |
| TelephonyIcons.FOUR_G); |
| if (mConfig.hideLtePlus) { |
| mNetworkToIconLookup.put(toDisplayIconKey( |
| DisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA), TelephonyIcons.FOUR_G); |
| } else { |
| mNetworkToIconLookup.put(toDisplayIconKey( |
| DisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA), TelephonyIcons.FOUR_G_PLUS); |
| } |
| } else { |
| mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_LTE), |
| TelephonyIcons.LTE); |
| if (mConfig.hideLtePlus) { |
| mNetworkToIconLookup.put(toDisplayIconKey( |
| DisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA), TelephonyIcons.LTE); |
| } else { |
| mNetworkToIconLookup.put(toDisplayIconKey( |
| DisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA), TelephonyIcons.LTE_PLUS); |
| } |
| } |
| mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_IWLAN), |
| TelephonyIcons.WFC); |
| mNetworkToIconLookup.put(toDisplayIconKey( |
| DisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO), TelephonyIcons.LTE_CA_5G_E); |
| mNetworkToIconLookup.put(toDisplayIconKey( |
| DisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA), TelephonyIcons.NR_5G); |
| mNetworkToIconLookup.put(toDisplayIconKey( |
| DisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE), TelephonyIcons.NR_5G_PLUS); |
| } |
| |
| private String getIconKey() { |
| if (mDisplayInfo.getOverrideNetworkType() == DisplayInfo.OVERRIDE_NETWORK_TYPE_NONE) { |
| return toIconKey(mDisplayInfo.getNetworkType()); |
| } else { |
| return toDisplayIconKey(mDisplayInfo.getOverrideNetworkType()); |
| } |
| } |
| |
| private String toIconKey(@Annotation.NetworkType int networkType) { |
| return Integer.toString(networkType); |
| } |
| |
| private String toDisplayIconKey(@Annotation.OverrideNetworkType int displayNetworkType) { |
| switch (displayNetworkType) { |
| case DisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA: |
| return toIconKey(TelephonyManager.NETWORK_TYPE_LTE) + "_CA"; |
| case DisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO: |
| return toIconKey(TelephonyManager.NETWORK_TYPE_LTE) + "_CA_Plus"; |
| case DisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA: |
| return "5G"; |
| case DisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE: |
| return "5G_Plus"; |
| default: |
| return "unsupported"; |
| } |
| } |
| |
| private void updateInflateSignalStrength() { |
| mInflateSignalStrengths = SignalStrengthUtil.shouldInflateSignalStrength(mContext, |
| mSubscriptionInfo.getSubscriptionId()); |
| } |
| |
| private int getNumLevels() { |
| if (mInflateSignalStrengths) { |
| return CellSignalStrength.getNumSignalStrengthLevels() + 1; |
| } |
| return CellSignalStrength.getNumSignalStrengthLevels(); |
| } |
| |
| @Override |
| public int getCurrentIconId() { |
| if (mCurrentState.iconGroup == TelephonyIcons.CARRIER_NETWORK_CHANGE) { |
| return SignalDrawable.getCarrierChangeState(getNumLevels()); |
| } else if (mCurrentState.connected) { |
| int level = mCurrentState.level; |
| if (mInflateSignalStrengths) { |
| level++; |
| } |
| boolean dataDisabled = mCurrentState.userSetup |
| && (mCurrentState.iconGroup == TelephonyIcons.DATA_DISABLED |
| || (mCurrentState.iconGroup == TelephonyIcons.NOT_DEFAULT_DATA |
| && mCurrentState.defaultDataOff)); |
| boolean noInternet = mCurrentState.inetCondition == 0; |
| boolean cutOut = dataDisabled || noInternet; |
| return SignalDrawable.getState(level, getNumLevels(), cutOut); |
| } else if (mCurrentState.enabled) { |
| return SignalDrawable.getEmptyState(getNumLevels()); |
| } else { |
| return 0; |
| } |
| } |
| |
| @Override |
| public int getQsCurrentIconId() { |
| return getCurrentIconId(); |
| } |
| |
| @Override |
| public void notifyListeners(SignalCallback callback) { |
| MobileIconGroup icons = getIcons(); |
| |
| String contentDescription = getTextIfExists(getContentDescription()).toString(); |
| CharSequence dataContentDescriptionHtml = getTextIfExists(icons.mDataContentDescription); |
| |
| //TODO: Hacky |
| // The data content description can sometimes be shown in a text view and might come to us |
| // as HTML. Strip any styling here so that listeners don't have to care |
| CharSequence dataContentDescription = Html.fromHtml( |
| dataContentDescriptionHtml.toString(), 0).toString(); |
| if (mCurrentState.inetCondition == 0) { |
| dataContentDescription = mContext.getString(R.string.data_connection_no_internet); |
| } |
| final boolean dataDisabled = (mCurrentState.iconGroup == TelephonyIcons.DATA_DISABLED |
| || (mCurrentState.iconGroup == TelephonyIcons.NOT_DEFAULT_DATA)) |
| && mCurrentState.userSetup; |
| |
| // Show icon in QS when we are connected or data is disabled. |
| boolean showDataIcon = mCurrentState.dataConnected || dataDisabled; |
| IconState statusIcon = new IconState(mCurrentState.enabled && !mCurrentState.airplaneMode, |
| getCurrentIconId(), contentDescription); |
| |
| int qsTypeIcon = 0; |
| IconState qsIcon = null; |
| CharSequence description = null; |
| // Only send data sim callbacks to QS. |
| if (mCurrentState.dataSim) { |
| qsTypeIcon = (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.mQsDataType : 0; |
| qsIcon = new IconState(mCurrentState.enabled |
| && !mCurrentState.isEmergency, getQsCurrentIconId(), contentDescription); |
| description = mCurrentState.isEmergency ? null : mCurrentState.networkName; |
| } |
| boolean activityIn = mCurrentState.dataConnected |
| && !mCurrentState.carrierNetworkChangeMode |
| && mCurrentState.activityIn; |
| boolean activityOut = mCurrentState.dataConnected |
| && !mCurrentState.carrierNetworkChangeMode |
| && mCurrentState.activityOut; |
| showDataIcon &= mCurrentState.isDefault || dataDisabled; |
| int typeIcon = (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.mDataType : 0; |
| callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon, |
| activityIn, activityOut, dataContentDescription, dataContentDescriptionHtml, |
| description, icons.mIsWide, mSubscriptionInfo.getSubscriptionId(), |
| mCurrentState.roaming); |
| } |
| |
| @Override |
| protected MobileState cleanState() { |
| return new MobileState(); |
| } |
| |
| private boolean isCdma() { |
| return (mSignalStrength != null) && !mSignalStrength.isGsm(); |
| } |
| |
| public boolean isEmergencyOnly() { |
| return (mServiceState != null && mServiceState.isEmergencyOnly()); |
| } |
| |
| private boolean isRoaming() { |
| // During a carrier change, roaming indications need to be supressed. |
| if (isCarrierNetworkChangeActive()) { |
| return false; |
| } |
| if (isCdma() && mServiceState != null) { |
| final int iconMode = mPhone.getCdmaEriInformation().getEriIconMode(); |
| return mPhone.getCdmaEriInformation().getEriIconIndex() != CdmaEriInformation.ERI_OFF |
| && (iconMode == CdmaEriInformation.ERI_ICON_MODE_NORMAL |
| || iconMode == CdmaEriInformation.ERI_ICON_MODE_FLASH); |
| } else { |
| return mServiceState != null && mServiceState.getRoaming(); |
| } |
| } |
| |
| private boolean isCarrierNetworkChangeActive() { |
| return mCurrentState.carrierNetworkChangeMode; |
| } |
| |
| public void handleBroadcast(Intent intent) { |
| String action = intent.getAction(); |
| if (action.equals(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED)) { |
| updateNetworkName(intent.getBooleanExtra(TelephonyManager.EXTRA_SHOW_SPN, false), |
| intent.getStringExtra(TelephonyManager.EXTRA_SPN), |
| intent.getStringExtra(TelephonyManager.EXTRA_DATA_SPN), |
| intent.getBooleanExtra(TelephonyManager.EXTRA_SHOW_PLMN, false), |
| intent.getStringExtra(TelephonyManager.EXTRA_PLMN)); |
| notifyListenersIfNecessary(); |
| } else if (action.equals(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)) { |
| updateDataSim(); |
| notifyListenersIfNecessary(); |
| } |
| } |
| |
| private void updateDataSim() { |
| int activeDataSubId = mDefaults.getActiveDataSubId(); |
| if (SubscriptionManager.isValidSubscriptionId(activeDataSubId)) { |
| mCurrentState.dataSim = activeDataSubId == mSubscriptionInfo.getSubscriptionId(); |
| } else { |
| // There doesn't seem to be a data sim selected, however if |
| // there isn't a MobileSignalController with dataSim set, then |
| // QS won't get any callbacks and will be blank. Instead |
| // lets just assume we are the data sim (which will basically |
| // show one at random) in QS until one is selected. The user |
| // should pick one soon after, so we shouldn't be in this state |
| // for long. |
| mCurrentState.dataSim = true; |
| } |
| } |
| |
| /** |
| * Updates the network's name based on incoming spn and plmn. |
| */ |
| void updateNetworkName(boolean showSpn, String spn, String dataSpn, |
| boolean showPlmn, String plmn) { |
| if (CHATTY) { |
| Log.d("CarrierLabel", "updateNetworkName showSpn=" + showSpn |
| + " spn=" + spn + " dataSpn=" + dataSpn |
| + " showPlmn=" + showPlmn + " plmn=" + plmn); |
| } |
| StringBuilder str = new StringBuilder(); |
| StringBuilder strData = new StringBuilder(); |
| if (showPlmn && plmn != null) { |
| str.append(plmn); |
| strData.append(plmn); |
| } |
| if (showSpn && spn != null) { |
| if (str.length() != 0) { |
| str.append(mNetworkNameSeparator); |
| } |
| str.append(spn); |
| } |
| if (str.length() != 0) { |
| mCurrentState.networkName = str.toString(); |
| } else { |
| mCurrentState.networkName = mNetworkNameDefault; |
| } |
| if (showSpn && dataSpn != null) { |
| if (strData.length() != 0) { |
| strData.append(mNetworkNameSeparator); |
| } |
| strData.append(dataSpn); |
| } |
| if (strData.length() != 0) { |
| mCurrentState.networkNameData = strData.toString(); |
| } else { |
| mCurrentState.networkNameData = mNetworkNameDefault; |
| } |
| } |
| |
| /** |
| * Extracts the CellSignalStrengthCdma from SignalStrength then returns the level |
| */ |
| private final int getCdmaLevel() { |
| List<CellSignalStrengthCdma> signalStrengthCdma = |
| mSignalStrength.getCellSignalStrengths(CellSignalStrengthCdma.class); |
| if (!signalStrengthCdma.isEmpty()) { |
| return signalStrengthCdma.get(0).getLevel(); |
| } |
| return CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN; |
| } |
| |
| /** |
| * Updates the current state based on mServiceState, mSignalStrength, mDataState, |
| * mDisplayInfo, and mSimState. It should be called any time one of these is updated. |
| * This will call listeners if necessary. |
| */ |
| private final void updateTelephony() { |
| if (DEBUG) { |
| Log.d(mTag, "updateTelephonySignalStrength: hasService=" + |
| Utils.isInService(mServiceState) + " ss=" + mSignalStrength |
| + " displayInfo=" + mDisplayInfo); |
| } |
| checkDefaultData(); |
| mCurrentState.connected = Utils.isInService(mServiceState) && mSignalStrength != null; |
| if (mCurrentState.connected) { |
| if (!mSignalStrength.isGsm() && mConfig.alwaysShowCdmaRssi) { |
| mCurrentState.level = getCdmaLevel(); |
| } else { |
| mCurrentState.level = mSignalStrength.getLevel(); |
| } |
| } |
| |
| String iconKey = getIconKey(); |
| if (mNetworkToIconLookup.get(iconKey) != null) { |
| mCurrentState.iconGroup = mNetworkToIconLookup.get(iconKey); |
| } else { |
| mCurrentState.iconGroup = mDefaultIcons; |
| } |
| mCurrentState.dataConnected = mCurrentState.connected |
| && mDataState == TelephonyManager.DATA_CONNECTED; |
| |
| mCurrentState.roaming = isRoaming(); |
| if (isCarrierNetworkChangeActive()) { |
| mCurrentState.iconGroup = TelephonyIcons.CARRIER_NETWORK_CHANGE; |
| } else if (isDataDisabled() && !mConfig.alwaysShowDataRatIcon) { |
| if (mSubscriptionInfo.getSubscriptionId() != mDefaults.getDefaultDataSubId()) { |
| mCurrentState.iconGroup = TelephonyIcons.NOT_DEFAULT_DATA; |
| } else { |
| mCurrentState.iconGroup = TelephonyIcons.DATA_DISABLED; |
| } |
| } |
| if (isEmergencyOnly() != mCurrentState.isEmergency) { |
| mCurrentState.isEmergency = isEmergencyOnly(); |
| mNetworkController.recalculateEmergency(); |
| } |
| // Fill in the network name if we think we have it. |
| if (mCurrentState.networkName.equals(mNetworkNameDefault) && mServiceState != null |
| && !TextUtils.isEmpty(mServiceState.getOperatorAlphaShort())) { |
| mCurrentState.networkName = mServiceState.getOperatorAlphaShort(); |
| } |
| // If this is the data subscription, update the currentState data name |
| if (mCurrentState.networkNameData.equals(mNetworkNameDefault) && mServiceState != null |
| && mCurrentState.dataSim |
| && !TextUtils.isEmpty(mServiceState.getOperatorAlphaShort())) { |
| mCurrentState.networkNameData = mServiceState.getOperatorAlphaShort(); |
| } |
| |
| notifyListenersIfNecessary(); |
| } |
| |
| /** |
| * If we are controlling the NOT_DEFAULT_DATA icon, check the status of the other one |
| */ |
| private void checkDefaultData() { |
| if (mCurrentState.iconGroup != TelephonyIcons.NOT_DEFAULT_DATA) { |
| mCurrentState.defaultDataOff = false; |
| return; |
| } |
| |
| mCurrentState.defaultDataOff = mNetworkController.isDataControllerDisabled(); |
| } |
| |
| void onMobileDataChanged() { |
| checkDefaultData(); |
| notifyListenersIfNecessary(); |
| } |
| |
| boolean isDataDisabled() { |
| return !mPhone.isDataConnectionAllowed(); |
| } |
| |
| @VisibleForTesting |
| void setActivity(int activity) { |
| mCurrentState.activityIn = activity == TelephonyManager.DATA_ACTIVITY_INOUT |
| || activity == TelephonyManager.DATA_ACTIVITY_IN; |
| mCurrentState.activityOut = activity == TelephonyManager.DATA_ACTIVITY_INOUT |
| || activity == TelephonyManager.DATA_ACTIVITY_OUT; |
| notifyListenersIfNecessary(); |
| } |
| |
| @Override |
| public void dump(PrintWriter pw) { |
| super.dump(pw); |
| pw.println(" mSubscription=" + mSubscriptionInfo + ","); |
| pw.println(" mServiceState=" + mServiceState + ","); |
| pw.println(" mSignalStrength=" + mSignalStrength + ","); |
| pw.println(" mDisplayInfo=" + mDisplayInfo + ","); |
| pw.println(" mDataState=" + mDataState + ","); |
| pw.println(" mInflateSignalStrengths=" + mInflateSignalStrengths + ","); |
| pw.println(" isDataDisabled=" + isDataDisabled() + ","); |
| } |
| |
| class MobilePhoneStateListener extends PhoneStateListener { |
| public MobilePhoneStateListener(Executor executor) { |
| super(executor); |
| } |
| |
| @Override |
| public void onSignalStrengthsChanged(SignalStrength signalStrength) { |
| if (DEBUG) { |
| Log.d(mTag, "onSignalStrengthsChanged signalStrength=" + signalStrength + |
| ((signalStrength == null) ? "" : (" level=" + signalStrength.getLevel()))); |
| } |
| mSignalStrength = signalStrength; |
| updateTelephony(); |
| } |
| |
| @Override |
| public void onServiceStateChanged(ServiceState state) { |
| if (DEBUG) { |
| Log.d(mTag, "onServiceStateChanged voiceState=" + state.getState() |
| + " dataState=" + state.getDataRegistrationState()); |
| } |
| mServiceState = state; |
| // onDisplayInfoChanged is invoked directly after onServiceStateChanged, so not calling |
| // updateTelephony() to prevent icon flickering in case of overrides. |
| } |
| |
| @Override |
| public void onDataConnectionStateChanged(int state, int networkType) { |
| if (DEBUG) { |
| Log.d(mTag, "onDataConnectionStateChanged: state=" + state |
| + " type=" + networkType); |
| } |
| mDataState = state; |
| if (networkType != mDisplayInfo.getNetworkType()) { |
| mDisplayInfo = new DisplayInfo(networkType, DisplayInfo.OVERRIDE_NETWORK_TYPE_NONE); |
| } |
| updateTelephony(); |
| } |
| |
| @Override |
| public void onDataActivity(int direction) { |
| if (DEBUG) { |
| Log.d(mTag, "onDataActivity: direction=" + direction); |
| } |
| setActivity(direction); |
| } |
| |
| @Override |
| public void onCarrierNetworkChange(boolean active) { |
| if (DEBUG) { |
| Log.d(mTag, "onCarrierNetworkChange: active=" + active); |
| } |
| mCurrentState.carrierNetworkChangeMode = active; |
| updateTelephony(); |
| } |
| |
| @Override |
| public void onActiveDataSubscriptionIdChanged(int subId) { |
| if (DEBUG) Log.d(mTag, "onActiveDataSubscriptionIdChanged: subId=" + subId); |
| updateDataSim(); |
| updateTelephony(); |
| } |
| |
| @Override |
| public void onDisplayInfoChanged(DisplayInfo displayInfo) { |
| if (DEBUG) { |
| Log.d(mTag, "onDisplayInfoChanged: displayInfo=" + displayInfo); |
| } |
| mDisplayInfo = displayInfo; |
| updateTelephony(); |
| } |
| } |
| |
| static class MobileIconGroup extends SignalController.IconGroup { |
| final int mDataContentDescription; // mContentDescriptionDataType |
| final int mDataType; |
| final boolean mIsWide; |
| final int mQsDataType; |
| |
| public MobileIconGroup(String name, int[][] sbIcons, int[][] qsIcons, int[] contentDesc, |
| int sbNullState, int qsNullState, int sbDiscState, int qsDiscState, |
| int discContentDesc, int dataContentDesc, int dataType, boolean isWide) { |
| super(name, sbIcons, qsIcons, contentDesc, sbNullState, qsNullState, sbDiscState, |
| qsDiscState, discContentDesc); |
| mDataContentDescription = dataContentDesc; |
| mDataType = dataType; |
| mIsWide = isWide; |
| mQsDataType = dataType; // TODO: remove this field |
| } |
| } |
| |
| static class MobileState extends SignalController.State { |
| String networkName; |
| String networkNameData; |
| boolean dataSim; |
| boolean dataConnected; |
| boolean isEmergency; |
| boolean airplaneMode; |
| boolean carrierNetworkChangeMode; |
| boolean isDefault; |
| boolean userSetup; |
| boolean roaming; |
| boolean defaultDataOff; // Tracks the on/off state of the defaultDataSubscription |
| |
| @Override |
| public void copyFrom(State s) { |
| super.copyFrom(s); |
| MobileState state = (MobileState) s; |
| dataSim = state.dataSim; |
| networkName = state.networkName; |
| networkNameData = state.networkNameData; |
| dataConnected = state.dataConnected; |
| isDefault = state.isDefault; |
| isEmergency = state.isEmergency; |
| airplaneMode = state.airplaneMode; |
| carrierNetworkChangeMode = state.carrierNetworkChangeMode; |
| userSetup = state.userSetup; |
| roaming = state.roaming; |
| defaultDataOff = state.defaultDataOff; |
| } |
| |
| @Override |
| protected void toString(StringBuilder builder) { |
| super.toString(builder); |
| builder.append(','); |
| builder.append("dataSim=").append(dataSim).append(','); |
| builder.append("networkName=").append(networkName).append(','); |
| builder.append("networkNameData=").append(networkNameData).append(','); |
| builder.append("dataConnected=").append(dataConnected).append(','); |
| builder.append("roaming=").append(roaming).append(','); |
| builder.append("isDefault=").append(isDefault).append(','); |
| builder.append("isEmergency=").append(isEmergency).append(','); |
| builder.append("airplaneMode=").append(airplaneMode).append(','); |
| builder.append("carrierNetworkChangeMode=").append(carrierNetworkChangeMode) |
| .append(','); |
| builder.append("userSetup=").append(userSetup).append(','); |
| builder.append("defaultDataOff=").append(defaultDataOff); |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| return super.equals(o) |
| && Objects.equals(((MobileState) o).networkName, networkName) |
| && Objects.equals(((MobileState) o).networkNameData, networkNameData) |
| && ((MobileState) o).dataSim == dataSim |
| && ((MobileState) o).dataConnected == dataConnected |
| && ((MobileState) o).isEmergency == isEmergency |
| && ((MobileState) o).airplaneMode == airplaneMode |
| && ((MobileState) o).carrierNetworkChangeMode == carrierNetworkChangeMode |
| && ((MobileState) o).userSetup == userSetup |
| && ((MobileState) o).isDefault == isDefault |
| && ((MobileState) o).roaming == roaming |
| && ((MobileState) o).defaultDataOff == defaultDataOff; |
| } |
| } |
| } |