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 */
16package com.android.systemui.statusbar.policy;
17
18import android.content.Context;
19import android.content.Intent;
20import android.net.NetworkCapabilities;
21import android.os.Looper;
22import android.telephony.PhoneStateListener;
23import android.telephony.ServiceState;
24import android.telephony.SignalStrength;
25import android.telephony.SubscriptionInfo;
26import android.telephony.SubscriptionManager;
27import android.telephony.TelephonyManager;
28import android.text.TextUtils;
29import android.util.Log;
30import android.util.SparseArray;
31
32import com.android.internal.annotations.VisibleForTesting;
33import com.android.internal.telephony.TelephonyIntents;
34import com.android.internal.telephony.cdma.EriInfo;
35import com.android.systemui.R;
36import com.android.systemui.statusbar.policy.NetworkController.IconState;
37import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
38import com.android.systemui.statusbar.policy.NetworkControllerImpl.Config;
39import com.android.systemui.statusbar.policy.NetworkControllerImpl.SubscriptionDefaults;
40
41import java.io.PrintWriter;
42import java.util.BitSet;
43import java.util.Objects;
44
45
46public 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