Home | History | Annotate | Download | only in policy
      1 /*
      2  * Copyright (C) 2015 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 package com.android.systemui.statusbar.policy;
     17 
     18 import android.content.Context;
     19 import android.content.Intent;
     20 import android.net.NetworkCapabilities;
     21 import android.os.Looper;
     22 import android.telephony.PhoneStateListener;
     23 import android.telephony.ServiceState;
     24 import android.telephony.SignalStrength;
     25 import android.telephony.SubscriptionInfo;
     26 import android.telephony.SubscriptionManager;
     27 import android.telephony.TelephonyManager;
     28 import android.text.TextUtils;
     29 import android.util.Log;
     30 import android.util.SparseArray;
     31 
     32 import com.android.internal.annotations.VisibleForTesting;
     33 import com.android.internal.telephony.TelephonyIntents;
     34 import com.android.internal.telephony.cdma.EriInfo;
     35 import com.android.systemui.R;
     36 import com.android.systemui.statusbar.policy.NetworkController.IconState;
     37 import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
     38 import com.android.systemui.statusbar.policy.NetworkControllerImpl.Config;
     39 import com.android.systemui.statusbar.policy.NetworkControllerImpl.SubscriptionDefaults;
     40 
     41 import java.io.PrintWriter;
     42 import java.util.BitSet;
     43 import java.util.Objects;
     44 
     45 
     46 public class MobileSignalController extends SignalController<
     47         MobileSignalController.MobileState, MobileSignalController.MobileIconGroup> {
     48     private final TelephonyManager mPhone;
     49     private final SubscriptionDefaults mDefaults;
     50     private final String mNetworkNameDefault;
     51     private final String mNetworkNameSeparator;
     52     @VisibleForTesting
     53     final PhoneStateListener mPhoneStateListener;
     54     // Save entire info for logging, we only use the id.
     55     final SubscriptionInfo mSubscriptionInfo;
     56 
     57     // @VisibleForDemoMode
     58     final SparseArray<MobileIconGroup> mNetworkToIconLookup;
     59 
     60     // Since some pieces of the phone state are interdependent we store it locally,
     61     // this could potentially become part of MobileState for simplification/complication
     62     // of code.
     63     private int mDataNetType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
     64     private int mDataState = TelephonyManager.DATA_DISCONNECTED;
     65     private ServiceState mServiceState;
     66     private SignalStrength mSignalStrength;
     67     private MobileIconGroup mDefaultIcons;
     68     private Config mConfig;
     69 
     70     // TODO: Reduce number of vars passed in, if we have the NetworkController, probably don't
     71     // need listener lists anymore.
     72     public MobileSignalController(Context context, Config config, boolean hasMobileData,
     73             TelephonyManager phone, CallbackHandler callbackHandler,
     74             NetworkControllerImpl networkController, SubscriptionInfo info,
     75             SubscriptionDefaults defaults, Looper receiverLooper) {
     76         super("MobileSignalController(" + info.getSubscriptionId() + ")", context,
     77                 NetworkCapabilities.TRANSPORT_CELLULAR, callbackHandler,
     78                 networkController);
     79         mNetworkToIconLookup = new SparseArray<>();
     80         mConfig = config;
     81         mPhone = phone;
     82         mDefaults = defaults;
     83         mSubscriptionInfo = info;
     84         mPhoneStateListener = new MobilePhoneStateListener(info.getSubscriptionId(),
     85                 receiverLooper);
     86         mNetworkNameSeparator = getStringIfExists(R.string.status_bar_network_name_separator);
     87         mNetworkNameDefault = getStringIfExists(
     88                 com.android.internal.R.string.lockscreen_carrier_default);
     89 
     90         mapIconSets();
     91 
     92         String networkName = info.getCarrierName() != null ? info.getCarrierName().toString()
     93                 : mNetworkNameDefault;
     94         mLastState.networkName = mCurrentState.networkName = networkName;
     95         mLastState.networkNameData = mCurrentState.networkNameData = networkName;
     96         mLastState.enabled = mCurrentState.enabled = hasMobileData;
     97         mLastState.iconGroup = mCurrentState.iconGroup = mDefaultIcons;
     98         // Get initial data sim state.
     99         updateDataSim();
    100     }
    101 
    102     public void setConfiguration(Config config) {
    103         mConfig = config;
    104         mapIconSets();
    105         updateTelephony();
    106     }
    107 
    108     public int getDataContentDescription() {
    109         return getIcons().mDataContentDescription;
    110     }
    111 
    112     public void setAirplaneMode(boolean airplaneMode) {
    113         mCurrentState.airplaneMode = airplaneMode;
    114         notifyListenersIfNecessary();
    115     }
    116 
    117     public void setUserSetupComplete(boolean userSetup) {
    118         mCurrentState.userSetup = userSetup;
    119         notifyListenersIfNecessary();
    120     }
    121 
    122     @Override
    123     public void updateConnectivity(BitSet connectedTransports, BitSet validatedTransports) {
    124         boolean isValidated = validatedTransports.get(mTransportType);
    125         mCurrentState.isDefault = connectedTransports.get(mTransportType);
    126         // Only show this as not having connectivity if we are default.
    127         mCurrentState.inetCondition = (isValidated || !mCurrentState.isDefault) ? 1 : 0;
    128         notifyListenersIfNecessary();
    129     }
    130 
    131     public void setCarrierNetworkChangeMode(boolean carrierNetworkChangeMode) {
    132         mCurrentState.carrierNetworkChangeMode = carrierNetworkChangeMode;
    133         updateTelephony();
    134     }
    135 
    136     /**
    137      * Start listening for phone state changes.
    138      */
    139     public void registerListener() {
    140         mPhone.listen(mPhoneStateListener,
    141                 PhoneStateListener.LISTEN_SERVICE_STATE
    142                         | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS
    143                         | PhoneStateListener.LISTEN_CALL_STATE
    144                         | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
    145                         | PhoneStateListener.LISTEN_DATA_ACTIVITY
    146                         | PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE);
    147     }
    148 
    149     /**
    150      * Stop listening for phone state changes.
    151      */
    152     public void unregisterListener() {
    153         mPhone.listen(mPhoneStateListener, 0);
    154     }
    155 
    156     /**
    157      * Produce a mapping of data network types to icon groups for simple and quick use in
    158      * updateTelephony.
    159      */
    160     private void mapIconSets() {
    161         mNetworkToIconLookup.clear();
    162 
    163         mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EVDO_0, TelephonyIcons.THREE_G);
    164         mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EVDO_A, TelephonyIcons.THREE_G);
    165         mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EVDO_B, TelephonyIcons.THREE_G);
    166         mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EHRPD, TelephonyIcons.THREE_G);
    167         mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_UMTS, TelephonyIcons.THREE_G);
    168 
    169         if (!mConfig.showAtLeast3G) {
    170             mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_UNKNOWN,
    171                     TelephonyIcons.UNKNOWN);
    172             mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EDGE, TelephonyIcons.E);
    173             mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_CDMA, TelephonyIcons.ONE_X);
    174             mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_1xRTT, TelephonyIcons.ONE_X);
    175 
    176             mDefaultIcons = TelephonyIcons.G;
    177         } else {
    178             mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_UNKNOWN,
    179                     TelephonyIcons.THREE_G);
    180             mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EDGE,
    181                     TelephonyIcons.THREE_G);
    182             mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_CDMA,
    183                     TelephonyIcons.THREE_G);
    184             mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_1xRTT,
    185                     TelephonyIcons.THREE_G);
    186             mDefaultIcons = TelephonyIcons.THREE_G;
    187         }
    188 
    189         MobileIconGroup hGroup = TelephonyIcons.THREE_G;
    190         if (mConfig.hspaDataDistinguishable) {
    191             hGroup = TelephonyIcons.H;
    192         }
    193         mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSDPA, hGroup);
    194         mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSUPA, hGroup);
    195         mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSPA, hGroup);
    196         mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSPAP, hGroup);
    197 
    198         if (mConfig.show4gForLte) {
    199             mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE, TelephonyIcons.FOUR_G);
    200             if (mConfig.hideLtePlus) {
    201                 mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE_CA,
    202                         TelephonyIcons.FOUR_G);
    203             } else {
    204                 mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE_CA,
    205                         TelephonyIcons.FOUR_G_PLUS);
    206             }
    207         } else {
    208             mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE, TelephonyIcons.LTE);
    209             if (mConfig.hideLtePlus) {
    210                 mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE_CA,
    211                         TelephonyIcons.LTE);
    212             } else {
    213                 mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE_CA,
    214                         TelephonyIcons.LTE_PLUS);
    215             }
    216         }
    217         mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_IWLAN, TelephonyIcons.WFC);
    218     }
    219 
    220     @Override
    221     public void notifyListeners(SignalCallback callback) {
    222         MobileIconGroup icons = getIcons();
    223 
    224         String contentDescription = getStringIfExists(getContentDescription());
    225         String dataContentDescription = getStringIfExists(icons.mDataContentDescription);
    226         final boolean dataDisabled = mCurrentState.iconGroup == TelephonyIcons.DATA_DISABLED
    227                 && mCurrentState.userSetup;
    228 
    229         // Show icon in QS when we are connected or need to show roaming or data is disabled.
    230         boolean showDataIcon = mCurrentState.dataConnected
    231                 || mCurrentState.iconGroup == TelephonyIcons.ROAMING
    232                 || dataDisabled;
    233         IconState statusIcon = new IconState(mCurrentState.enabled && !mCurrentState.airplaneMode,
    234                 getCurrentIconId(), contentDescription);
    235 
    236         int qsTypeIcon = 0;
    237         IconState qsIcon = null;
    238         String description = null;
    239         // Only send data sim callbacks to QS.
    240         if (mCurrentState.dataSim) {
    241             qsTypeIcon = showDataIcon ? icons.mQsDataType : 0;
    242             qsIcon = new IconState(mCurrentState.enabled
    243                     && !mCurrentState.isEmergency, getQsCurrentIconId(), contentDescription);
    244             description = mCurrentState.isEmergency ? null : mCurrentState.networkName;
    245         }
    246         boolean activityIn = mCurrentState.dataConnected
    247                         && !mCurrentState.carrierNetworkChangeMode
    248                         && mCurrentState.activityIn;
    249         boolean activityOut = mCurrentState.dataConnected
    250                         && !mCurrentState.carrierNetworkChangeMode
    251                         && mCurrentState.activityOut;
    252         showDataIcon &= mCurrentState.isDefault
    253                 || mCurrentState.iconGroup == TelephonyIcons.ROAMING
    254                 || dataDisabled;
    255         int typeIcon = showDataIcon ? icons.mDataType : 0;
    256         callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon,
    257                 activityIn, activityOut, dataContentDescription, description, icons.mIsWide,
    258                 mSubscriptionInfo.getSubscriptionId());
    259     }
    260 
    261     @Override
    262     protected MobileState cleanState() {
    263         return new MobileState();
    264     }
    265 
    266     private boolean hasService() {
    267         if (mServiceState != null) {
    268             // Consider the device to be in service if either voice or data
    269             // service is available. Some SIM cards are marketed as data-only
    270             // and do not support voice service, and on these SIM cards, we
    271             // want to show signal bars for data service as well as the "no
    272             // service" or "emergency calls only" text that indicates that voice
    273             // is not available.
    274             switch (mServiceState.getVoiceRegState()) {
    275                 case ServiceState.STATE_POWER_OFF:
    276                     return false;
    277                 case ServiceState.STATE_OUT_OF_SERVICE:
    278                 case ServiceState.STATE_EMERGENCY_ONLY:
    279                     return mServiceState.getDataRegState() == ServiceState.STATE_IN_SERVICE;
    280                 default:
    281                     return true;
    282             }
    283         } else {
    284             return false;
    285         }
    286     }
    287 
    288     private boolean isCdma() {
    289         return (mSignalStrength != null) && !mSignalStrength.isGsm();
    290     }
    291 
    292     public boolean isEmergencyOnly() {
    293         return (mServiceState != null && mServiceState.isEmergencyOnly());
    294     }
    295 
    296     private boolean isRoaming() {
    297         if (isCdma()) {
    298             final int iconMode = mServiceState.getCdmaEriIconMode();
    299             return mServiceState.getCdmaEriIconIndex() != EriInfo.ROAMING_INDICATOR_OFF
    300                     && (iconMode == EriInfo.ROAMING_ICON_MODE_NORMAL
    301                         || iconMode == EriInfo.ROAMING_ICON_MODE_FLASH);
    302         } else {
    303             return mServiceState != null && mServiceState.getRoaming();
    304         }
    305     }
    306 
    307     private boolean isCarrierNetworkChangeActive() {
    308         return mCurrentState.carrierNetworkChangeMode;
    309     }
    310 
    311     public void handleBroadcast(Intent intent) {
    312         String action = intent.getAction();
    313         if (action.equals(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION)) {
    314             updateNetworkName(intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_SPN, false),
    315                     intent.getStringExtra(TelephonyIntents.EXTRA_SPN),
    316                     intent.getStringExtra(TelephonyIntents.EXTRA_DATA_SPN),
    317                     intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_PLMN, false),
    318                     intent.getStringExtra(TelephonyIntents.EXTRA_PLMN));
    319             notifyListenersIfNecessary();
    320         } else if (action.equals(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)) {
    321             updateDataSim();
    322             notifyListenersIfNecessary();
    323         }
    324     }
    325 
    326     private void updateDataSim() {
    327         int defaultDataSub = mDefaults.getDefaultDataSubId();
    328         if (SubscriptionManager.isValidSubscriptionId(defaultDataSub)) {
    329             mCurrentState.dataSim = defaultDataSub == mSubscriptionInfo.getSubscriptionId();
    330         } else {
    331             // There doesn't seem to be a data sim selected, however if
    332             // there isn't a MobileSignalController with dataSim set, then
    333             // QS won't get any callbacks and will be blank.  Instead
    334             // lets just assume we are the data sim (which will basically
    335             // show one at random) in QS until one is selected.  The user
    336             // should pick one soon after, so we shouldn't be in this state
    337             // for long.
    338             mCurrentState.dataSim = true;
    339         }
    340     }
    341 
    342     /**
    343      * Updates the network's name based on incoming spn and plmn.
    344      */
    345     void updateNetworkName(boolean showSpn, String spn, String dataSpn,
    346             boolean showPlmn, String plmn) {
    347         if (CHATTY) {
    348             Log.d("CarrierLabel", "updateNetworkName showSpn=" + showSpn
    349                     + " spn=" + spn + " dataSpn=" + dataSpn
    350                     + " showPlmn=" + showPlmn + " plmn=" + plmn);
    351         }
    352         StringBuilder str = new StringBuilder();
    353         StringBuilder strData = new StringBuilder();
    354         if (showPlmn && plmn != null) {
    355             str.append(plmn);
    356             strData.append(plmn);
    357         }
    358         if (showSpn && spn != null) {
    359             if (str.length() != 0) {
    360                 str.append(mNetworkNameSeparator);
    361             }
    362             str.append(spn);
    363         }
    364         if (str.length() != 0) {
    365             mCurrentState.networkName = str.toString();
    366         } else {
    367             mCurrentState.networkName = mNetworkNameDefault;
    368         }
    369         if (showSpn && dataSpn != null) {
    370             if (strData.length() != 0) {
    371                 strData.append(mNetworkNameSeparator);
    372             }
    373             strData.append(dataSpn);
    374         }
    375         if (strData.length() != 0) {
    376             mCurrentState.networkNameData = strData.toString();
    377         } else {
    378             mCurrentState.networkNameData = mNetworkNameDefault;
    379         }
    380     }
    381 
    382     /**
    383      * Updates the current state based on mServiceState, mSignalStrength, mDataNetType,
    384      * mDataState, and mSimState.  It should be called any time one of these is updated.
    385      * This will call listeners if necessary.
    386      */
    387     private final void updateTelephony() {
    388         if (DEBUG) {
    389             Log.d(mTag, "updateTelephonySignalStrength: hasService=" + hasService()
    390                     + " ss=" + mSignalStrength);
    391         }
    392         mCurrentState.connected = hasService() && mSignalStrength != null;
    393         if (mCurrentState.connected) {
    394             if (!mSignalStrength.isGsm() && mConfig.alwaysShowCdmaRssi) {
    395                 mCurrentState.level = mSignalStrength.getCdmaLevel();
    396             } else {
    397                 mCurrentState.level = mSignalStrength.getLevel();
    398             }
    399         }
    400         if (mNetworkToIconLookup.indexOfKey(mDataNetType) >= 0) {
    401             mCurrentState.iconGroup = mNetworkToIconLookup.get(mDataNetType);
    402         } else {
    403             mCurrentState.iconGroup = mDefaultIcons;
    404         }
    405         mCurrentState.dataConnected = mCurrentState.connected
    406                 && mDataState == TelephonyManager.DATA_CONNECTED;
    407 
    408         if (isCarrierNetworkChangeActive()) {
    409             mCurrentState.iconGroup = TelephonyIcons.CARRIER_NETWORK_CHANGE;
    410         } else if (isRoaming()) {
    411             mCurrentState.iconGroup = TelephonyIcons.ROAMING;
    412         } else if (isDataDisabled()) {
    413             mCurrentState.iconGroup = TelephonyIcons.DATA_DISABLED;
    414         }
    415         if (isEmergencyOnly() != mCurrentState.isEmergency) {
    416             mCurrentState.isEmergency = isEmergencyOnly();
    417             mNetworkController.recalculateEmergency();
    418         }
    419         // Fill in the network name if we think we have it.
    420         if (mCurrentState.networkName == mNetworkNameDefault && mServiceState != null
    421                 && !TextUtils.isEmpty(mServiceState.getOperatorAlphaShort())) {
    422             mCurrentState.networkName = mServiceState.getOperatorAlphaShort();
    423         }
    424 
    425         notifyListenersIfNecessary();
    426     }
    427 
    428     private boolean isDataDisabled() {
    429         return !mPhone.getDataEnabled(mSubscriptionInfo.getSubscriptionId());
    430     }
    431 
    432     @VisibleForTesting
    433     void setActivity(int activity) {
    434         mCurrentState.activityIn = activity == TelephonyManager.DATA_ACTIVITY_INOUT
    435                 || activity == TelephonyManager.DATA_ACTIVITY_IN;
    436         mCurrentState.activityOut = activity == TelephonyManager.DATA_ACTIVITY_INOUT
    437                 || activity == TelephonyManager.DATA_ACTIVITY_OUT;
    438         notifyListenersIfNecessary();
    439     }
    440 
    441     @Override
    442     public void dump(PrintWriter pw) {
    443         super.dump(pw);
    444         pw.println("  mSubscription=" + mSubscriptionInfo + ",");
    445         pw.println("  mServiceState=" + mServiceState + ",");
    446         pw.println("  mSignalStrength=" + mSignalStrength + ",");
    447         pw.println("  mDataState=" + mDataState + ",");
    448         pw.println("  mDataNetType=" + mDataNetType + ",");
    449     }
    450 
    451     class MobilePhoneStateListener extends PhoneStateListener {
    452         public MobilePhoneStateListener(int subId, Looper looper) {
    453             super(subId, looper);
    454         }
    455 
    456         @Override
    457         public void onSignalStrengthsChanged(SignalStrength signalStrength) {
    458             if (DEBUG) {
    459                 Log.d(mTag, "onSignalStrengthsChanged signalStrength=" + signalStrength +
    460                         ((signalStrength == null) ? "" : (" level=" + signalStrength.getLevel())));
    461             }
    462             mSignalStrength = signalStrength;
    463             updateTelephony();
    464         }
    465 
    466         @Override
    467         public void onServiceStateChanged(ServiceState state) {
    468             if (DEBUG) {
    469                 Log.d(mTag, "onServiceStateChanged voiceState=" + state.getVoiceRegState()
    470                         + " dataState=" + state.getDataRegState());
    471             }
    472             mServiceState = state;
    473             mDataNetType = state.getDataNetworkType();
    474             if (mDataNetType == TelephonyManager.NETWORK_TYPE_LTE && mServiceState != null &&
    475                     mServiceState.isUsingCarrierAggregation()) {
    476                 mDataNetType = TelephonyManager.NETWORK_TYPE_LTE_CA;
    477             }
    478             updateTelephony();
    479         }
    480 
    481         @Override
    482         public void onDataConnectionStateChanged(int state, int networkType) {
    483             if (DEBUG) {
    484                 Log.d(mTag, "onDataConnectionStateChanged: state=" + state
    485                         + " type=" + networkType);
    486             }
    487             mDataState = state;
    488             mDataNetType = networkType;
    489             if (mDataNetType == TelephonyManager.NETWORK_TYPE_LTE && mServiceState != null &&
    490                     mServiceState.isUsingCarrierAggregation()) {
    491                 mDataNetType = TelephonyManager.NETWORK_TYPE_LTE_CA;
    492             }
    493             updateTelephony();
    494         }
    495 
    496         @Override
    497         public void onDataActivity(int direction) {
    498             if (DEBUG) {
    499                 Log.d(mTag, "onDataActivity: direction=" + direction);
    500             }
    501             setActivity(direction);
    502         }
    503 
    504         @Override
    505         public void onCarrierNetworkChange(boolean active) {
    506             if (DEBUG) {
    507                 Log.d(mTag, "onCarrierNetworkChange: active=" + active);
    508             }
    509             mCurrentState.carrierNetworkChangeMode = active;
    510 
    511             updateTelephony();
    512         }
    513     };
    514 
    515     static class MobileIconGroup extends SignalController.IconGroup {
    516         final int mDataContentDescription; // mContentDescriptionDataType
    517         final int mDataType;
    518         final boolean mIsWide;
    519         final int mQsDataType;
    520 
    521         public MobileIconGroup(String name, int[][] sbIcons, int[][] qsIcons, int[] contentDesc,
    522                 int sbNullState, int qsNullState, int sbDiscState, int qsDiscState,
    523                 int discContentDesc, int dataContentDesc, int dataType, boolean isWide,
    524                 int qsDataType) {
    525             super(name, sbIcons, qsIcons, contentDesc, sbNullState, qsNullState, sbDiscState,
    526                     qsDiscState, discContentDesc);
    527             mDataContentDescription = dataContentDesc;
    528             mDataType = dataType;
    529             mIsWide = isWide;
    530             mQsDataType = qsDataType;
    531         }
    532     }
    533 
    534     static class MobileState extends SignalController.State {
    535         String networkName;
    536         String networkNameData;
    537         boolean dataSim;
    538         boolean dataConnected;
    539         boolean isEmergency;
    540         boolean airplaneMode;
    541         boolean carrierNetworkChangeMode;
    542         boolean isDefault;
    543         boolean userSetup;
    544 
    545         @Override
    546         public void copyFrom(State s) {
    547             super.copyFrom(s);
    548             MobileState state = (MobileState) s;
    549             dataSim = state.dataSim;
    550             networkName = state.networkName;
    551             networkNameData = state.networkNameData;
    552             dataConnected = state.dataConnected;
    553             isDefault = state.isDefault;
    554             isEmergency = state.isEmergency;
    555             airplaneMode = state.airplaneMode;
    556             carrierNetworkChangeMode = state.carrierNetworkChangeMode;
    557             userSetup = state.userSetup;
    558         }
    559 
    560         @Override
    561         protected void toString(StringBuilder builder) {
    562             super.toString(builder);
    563             builder.append(',');
    564             builder.append("dataSim=").append(dataSim).append(',');
    565             builder.append("networkName=").append(networkName).append(',');
    566             builder.append("networkNameData=").append(networkNameData).append(',');
    567             builder.append("dataConnected=").append(dataConnected).append(',');
    568             builder.append("isDefault=").append(isDefault).append(',');
    569             builder.append("isEmergency=").append(isEmergency).append(',');
    570             builder.append("airplaneMode=").append(airplaneMode).append(',');
    571             builder.append("carrierNetworkChangeMode=").append(carrierNetworkChangeMode)
    572                     .append(',');
    573             builder.append("userSetup=").append(userSetup);
    574         }
    575 
    576         @Override
    577         public boolean equals(Object o) {
    578             return super.equals(o)
    579                     && Objects.equals(((MobileState) o).networkName, networkName)
    580                     && Objects.equals(((MobileState) o).networkNameData, networkNameData)
    581                     && ((MobileState) o).dataSim == dataSim
    582                     && ((MobileState) o).dataConnected == dataConnected
    583                     && ((MobileState) o).isEmergency == isEmergency
    584                     && ((MobileState) o).airplaneMode == airplaneMode
    585                     && ((MobileState) o).carrierNetworkChangeMode == carrierNetworkChangeMode
    586                     && ((MobileState) o).userSetup == userSetup
    587                     && ((MobileState) o).isDefault == isDefault;
    588         }
    589     }
    590 }
    591