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