OSDN Git Service

resolved conflicts for merge of b9470f3a to master
authorJeff Davidson <jpd@google.com>
Wed, 28 Jan 2015 00:24:32 +0000 (16:24 -0800)
committerJeff Davidson <jpd@google.com>
Wed, 28 Jan 2015 00:24:32 +0000 (16:24 -0800)
Change-Id: I85a975253b0b18b8c9e6d069c88fc12c93ed82ad

1  2 
packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java

index ac5602c,0000000..af92423
mode 100644,000000..100644
--- /dev/null
@@@ -1,545 -1,0 +1,546 @@@
-             int qsTypeIcon = mCurrentState.dataConnected ?
-                     icons.mQsDataType[mCurrentState.inetForNetwork] : 0;
 +/*
 + * 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.net.NetworkCapabilities;
 +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.util.Log;
 +import android.util.SparseArray;
 +
 +import com.android.internal.annotations.VisibleForTesting;
 +import com.android.internal.telephony.IccCardConstants;
 +import com.android.internal.telephony.TelephonyIntents;
 +import com.android.internal.telephony.cdma.EriInfo;
 +import com.android.systemui.R;
 +import com.android.systemui.statusbar.policy.NetworkController.NetworkSignalChangedCallback;
 +import com.android.systemui.statusbar.policy.NetworkControllerImpl.Config;
 +import com.android.systemui.statusbar.policy.NetworkControllerImpl.SignalCluster;
 +
 +import java.io.PrintWriter;
 +import java.util.List;
 +import java.util.Objects;
 +
 +
 +public class MobileSignalController extends SignalController<
 +        MobileSignalController.MobileState, MobileSignalController.MobileIconGroup> {
 +    private final TelephonyManager mPhone;
 +    private final String mNetworkNameDefault;
 +    private final String mNetworkNameSeparator;
 +    @VisibleForTesting
 +    final PhoneStateListener mPhoneStateListener;
 +    // Save entire info for logging, we only use the id.
 +    private final SubscriptionInfo mSubscriptionInfo;
 +
 +    // @VisibleForDemoMode
 +    final SparseArray<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 IccCardConstants.State mSimState = IccCardConstants.State.READY;
 +    private int mDataNetType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
 +    private int mDataState = TelephonyManager.DATA_DISCONNECTED;
 +    private ServiceState mServiceState;
 +    private SignalStrength mSignalStrength;
 +    private MobileIconGroup mDefaultIcons;
 +    private Config mConfig;
 +
 +    // 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, List<NetworkSignalChangedCallback> signalCallbacks,
 +            List<SignalCluster> signalClusters, NetworkControllerImpl networkController,
 +            SubscriptionInfo info) {
 +        super("MobileSignalController(" + info.getSubscriptionId() + ")", context,
 +                NetworkCapabilities.TRANSPORT_CELLULAR, signalCallbacks, signalClusters,
 +                networkController);
 +        mNetworkToIconLookup = new SparseArray<>();
 +        mConfig = config;
 +        mPhone = phone;
 +        mSubscriptionInfo = info;
 +        mPhoneStateListener = new MobilePhoneStateListener(info.getSubscriptionId());
 +        mNetworkNameSeparator = getStringIfExists(R.string.status_bar_network_name_separator);
 +        mNetworkNameDefault = getStringIfExists(
 +                com.android.internal.R.string.lockscreen_carrier_default);
 +
 +        mapIconSets();
 +
 +        mLastState.networkName = mCurrentState.networkName = mNetworkNameDefault;
 +        mLastState.enabled = mCurrentState.enabled = hasMobileData;
 +        mLastState.iconGroup = mCurrentState.iconGroup = mDefaultIcons;
 +        // Get initial data sim state.
 +        updateDataSim();
 +    }
 +
 +    public void setConfiguration(Config config) {
 +        mConfig = config;
 +        mapIconSets();
 +        updateTelephony();
 +    }
 +
 +    /**
 +     * Get (the mobile parts of) the carrier string.
 +     *
 +     * @param currentLabel can be used for concatenation, currently just empty
 +     * @param connected whether the device has connection to the internet at all
 +     * @param isMobileLabel whether to always return the network or just when data is connected
 +     */
 +    public String getLabel(String currentLabel, boolean connected, boolean isMobileLabel) {
 +        if (!mCurrentState.enabled) {
 +            return "";
 +        } else {
 +            String mobileLabel = "";
 +            // We want to show the carrier name if in service and either:
 +            // - We are connected to mobile data, or
 +            // - We are not connected to mobile data, as long as the *reason* packets are not
 +            //   being routed over that link is that we have better connectivity via wifi.
 +            // If data is disconnected for some other reason but wifi (or ethernet/bluetooth)
 +            // is connected, we show nothing.
 +            // Otherwise (nothing connected) we show "No internet connection".
 +            if (mCurrentState.dataConnected) {
 +                mobileLabel = mCurrentState.networkName;
 +            } else if (connected || mCurrentState.isEmergency) {
 +                if (mCurrentState.connected || mCurrentState.isEmergency) {
 +                    // The isEmergencyOnly test covers the case of a phone with no SIM
 +                    mobileLabel = mCurrentState.networkName;
 +                }
 +            } else {
 +                mobileLabel = mContext.getString(
 +                        R.string.status_bar_settings_signal_meter_disconnected);
 +            }
 +
 +            if (currentLabel.length() != 0) {
 +                currentLabel = currentLabel + mNetworkNameSeparator;
 +            }
 +            // Now for things that should only be shown when actually using mobile data.
 +            if (isMobileLabel) {
 +                return currentLabel + mobileLabel;
 +            } else {
 +                return currentLabel
 +                        + (mCurrentState.dataConnected ? mobileLabel : currentLabel);
 +            }
 +        }
 +    }
 +
 +    public int getDataContentDescription() {
 +        return getIcons().mDataContentDescription;
 +    }
 +
 +    @VisibleForTesting
 +    protected IccCardConstants.State getSimState() {
 +        return mSimState;
 +    }
 +
 +    public void setAirplaneMode(boolean airplaneMode) {
 +        mCurrentState.airplaneMode = airplaneMode;
 +        notifyListenersIfNecessary();
 +    }
 +
 +    public void setInetCondition(int inetCondition, int inetConditionForNetwork) {
 +        // For mobile data, use general inet condition for phone signal indexing,
 +        // and network specific for data indexing (I think this might be a bug, but
 +        // keeping for now).
 +        // TODO: Update with explanation of why.
 +        mCurrentState.inetForNetwork = inetConditionForNetwork;
 +        setInetCondition(inetCondition);
 +    }
 +
 +    /**
 +     * 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);
 +    }
 +
 +    /**
 +     * Stop listening for phone state changes.
 +     */
 +    public void unregisterListener() {
 +        mPhone.listen(mPhoneStateListener, 0);
 +    }
 +
 +    /**
 +     * Produce a mapping of data network types to icon groups for simple and quick use in
 +     * updateTelephony.
 +     */
 +    private void mapIconSets() {
 +        mNetworkToIconLookup.clear();
 +
 +        mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EVDO_0, TelephonyIcons.THREE_G);
 +        mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EVDO_A, TelephonyIcons.THREE_G);
 +        mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EVDO_B, TelephonyIcons.THREE_G);
 +        mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EHRPD, TelephonyIcons.THREE_G);
 +        mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_UMTS, TelephonyIcons.THREE_G);
 +
 +        if (!mConfig.showAtLeast3G) {
 +            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_UNKNOWN,
 +                    TelephonyIcons.UNKNOWN);
 +            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EDGE, TelephonyIcons.E);
 +            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_CDMA, TelephonyIcons.ONE_X);
 +            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_1xRTT, TelephonyIcons.ONE_X);
 +
 +            mDefaultIcons = TelephonyIcons.G;
 +        } else {
 +            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_UNKNOWN,
 +                    TelephonyIcons.THREE_G);
 +            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EDGE,
 +                    TelephonyIcons.THREE_G);
 +            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_CDMA,
 +                    TelephonyIcons.THREE_G);
 +            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_1xRTT,
 +                    TelephonyIcons.THREE_G);
 +            mDefaultIcons = TelephonyIcons.THREE_G;
 +        }
 +
 +        MobileIconGroup hGroup = TelephonyIcons.THREE_G;
 +        if (mConfig.hspaDataDistinguishable) {
 +            hGroup = TelephonyIcons.H;
 +        }
 +        mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSDPA, hGroup);
 +        mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSUPA, hGroup);
 +        mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSPA, hGroup);
 +        mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSPAP, hGroup);
 +
 +        if (mConfig.show4gForLte) {
 +            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE, TelephonyIcons.FOUR_G);
 +        } else {
 +            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE, TelephonyIcons.LTE);
 +        }
 +    }
 +
 +    @Override
 +    public void notifyListeners() {
 +        MobileIconGroup icons = getIcons();
 +
 +        String contentDescription = getStringIfExists(getContentDescription());
 +        String dataContentDescription = getStringIfExists(icons.mDataContentDescription);
++
++        boolean showDataIcon = mCurrentState.dataConnected && mCurrentState.inetForNetwork != 0
++                || mCurrentState.iconGroup == TelephonyIcons.ROAMING;
++
 +        // Only send data sim callbacks to QS.
 +        if (mCurrentState.dataSim) {
-         boolean showDataIcon = mCurrentState.dataConnected && mCurrentState.inetForNetwork != 0
-                 || mCurrentState.iconGroup == TelephonyIcons.ROAMING;
++            int qsTypeIcon = showDataIcon ? icons.mQsDataType[mCurrentState.inetForNetwork] : 0;
 +            int length = mSignalsChangedCallbacks.size();
 +            for (int i = 0; i < length; i++) {
 +                mSignalsChangedCallbacks.get(i).onMobileDataSignalChanged(mCurrentState.enabled
 +                        && !mCurrentState.isEmergency,
 +                        getQsCurrentIconId(), contentDescription,
 +                        qsTypeIcon,
 +                        mCurrentState.dataConnected && mCurrentState.activityIn,
 +                        mCurrentState.dataConnected && mCurrentState.activityOut,
 +                        dataContentDescription,
 +                        mCurrentState.isEmergency ? null : mCurrentState.networkName,
 +                        // Only wide if actually showing something.
 +                        icons.mIsWide && qsTypeIcon != 0);
 +            }
 +        }
 +        int typeIcon = showDataIcon ? icons.mDataType : 0;
 +        int signalClustersLength = mSignalClusters.size();
 +        for (int i = 0; i < signalClustersLength; i++) {
 +            mSignalClusters.get(i).setMobileDataIndicators(
 +                    mCurrentState.enabled && !mCurrentState.airplaneMode,
 +                    getCurrentIconId(),
 +                    typeIcon,
 +                    contentDescription,
 +                    dataContentDescription,
 +                    // Only wide if actually showing something.
 +                    icons.mIsWide && typeIcon != 0,
 +                    mSubscriptionInfo.getSubscriptionId());
 +        }
 +    }
 +
 +    @Override
 +    protected MobileState cleanState() {
 +        return new MobileState();
 +    }
 +
 +    private boolean hasService() {
 +        if (mServiceState != null) {
 +            // Consider the device to be in service if either voice or data
 +            // service is available. Some SIM cards are marketed as data-only
 +            // and do not support voice service, and on these SIM cards, we
 +            // want to show signal bars for data service as well as the "no
 +            // service" or "emergency calls only" text that indicates that voice
 +            // is not available.
 +            switch (mServiceState.getVoiceRegState()) {
 +                case ServiceState.STATE_POWER_OFF:
 +                    return false;
 +                case ServiceState.STATE_OUT_OF_SERVICE:
 +                case ServiceState.STATE_EMERGENCY_ONLY:
 +                    return mServiceState.getDataRegState() == ServiceState.STATE_IN_SERVICE;
 +                default:
 +                    return true;
 +            }
 +        } else {
 +            return false;
 +        }
 +    }
 +
 +    private boolean isCdma() {
 +        return (mSignalStrength != null) && !mSignalStrength.isGsm();
 +    }
 +
 +    public boolean isEmergencyOnly() {
 +        return (mServiceState != null && mServiceState.isEmergencyOnly());
 +    }
 +
 +    private boolean isRoaming() {
 +        if (isCdma()) {
 +            final int iconMode = mServiceState.getCdmaEriIconMode();
 +            return mServiceState.getCdmaEriIconIndex() != EriInfo.ROAMING_INDICATOR_OFF
 +                    && (iconMode == EriInfo.ROAMING_ICON_MODE_NORMAL
 +                        || iconMode == EriInfo.ROAMING_ICON_MODE_FLASH);
 +        } else {
 +            return mServiceState != null && mServiceState.getRoaming();
 +        }
 +    }
 +
 +    public void handleBroadcast(Intent intent) {
 +        String action = intent.getAction();
 +        if (action.equals(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION)) {
 +            updateNetworkName(intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_SPN, false),
 +                    intent.getStringExtra(TelephonyIntents.EXTRA_SPN),
 +                    intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_PLMN, false),
 +                    intent.getStringExtra(TelephonyIntents.EXTRA_PLMN));
 +            notifyListenersIfNecessary();
 +        } else if (action.equals(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)) {
 +            updateDataSim();
 +        }
 +    }
 +
 +    private void updateDataSim() {
 +        int defaultDataSub = SubscriptionManager.getDefaultDataSubId();
 +        if (SubscriptionManager.isValidSubscriptionId(defaultDataSub)) {
 +            mCurrentState.dataSim = defaultDataSub == 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;
 +        }
 +        notifyListenersIfNecessary();
 +    }
 +
 +    /**
 +     * Updates the network's name based on incoming spn and plmn.
 +     */
 +    void updateNetworkName(boolean showSpn, String spn, boolean showPlmn, String plmn) {
 +        if (CHATTY) {
 +            Log.d("CarrierLabel", "updateNetworkName showSpn=" + showSpn + " spn=" + spn
 +                    + " showPlmn=" + showPlmn + " plmn=" + plmn);
 +        }
 +        StringBuilder str = new StringBuilder();
 +        if (showPlmn && plmn != null) {
 +            str.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;
 +        }
 +    }
 +
 +    /**
 +     * Updates the current state based on mServiceState, mSignalStrength, mDataNetType,
 +     * mDataState, 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=" + hasService()
 +                    + " ss=" + mSignalStrength);
 +        }
 +        mCurrentState.connected = hasService() && mSignalStrength != null;
 +        if (mCurrentState.connected) {
 +            if (!mSignalStrength.isGsm() && mConfig.alwaysShowCdmaRssi) {
 +                mCurrentState.level = mSignalStrength.getCdmaLevel();
 +            } else {
 +                mCurrentState.level = mSignalStrength.getLevel();
 +            }
 +        }
 +        if (mNetworkToIconLookup.indexOfKey(mDataNetType) >= 0) {
 +            mCurrentState.iconGroup = mNetworkToIconLookup.get(mDataNetType);
 +        } else {
 +            mCurrentState.iconGroup = mDefaultIcons;
 +        }
 +        mCurrentState.dataConnected = mCurrentState.connected
 +                && mDataState == TelephonyManager.DATA_CONNECTED;
 +
 +        if (isRoaming()) {
 +            mCurrentState.iconGroup = TelephonyIcons.ROAMING;
 +        }
 +        if (isEmergencyOnly() != mCurrentState.isEmergency) {
 +            mCurrentState.isEmergency = isEmergencyOnly();
 +            mNetworkController.recalculateEmergency();
 +        }
 +        // Fill in the network name if we think we have it.
 +        if (mCurrentState.networkName == mNetworkNameDefault && mServiceState != null
 +                && mServiceState.getOperatorAlphaShort() != null) {
 +            mCurrentState.networkName = mServiceState.getOperatorAlphaShort();
 +        }
 +        notifyListenersIfNecessary();
 +    }
 +
 +    @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("  mDataState=" + mDataState + ",");
 +        pw.println("  mDataNetType=" + mDataNetType + ",");
 +    }
 +
 +    class MobilePhoneStateListener extends PhoneStateListener {
 +        public MobilePhoneStateListener(int subId) {
 +            super(subId);
 +        }
 +
 +        @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.getVoiceRegState()
 +                        + " dataState=" + state.getDataRegState());
 +            }
 +            mServiceState = state;
 +            updateTelephony();
 +        }
 +
 +        @Override
 +        public void onDataConnectionStateChanged(int state, int networkType) {
 +            if (DEBUG) {
 +                Log.d(mTag, "onDataConnectionStateChanged: state=" + state
 +                        + " type=" + networkType);
 +            }
 +            mDataState = state;
 +            mDataNetType = networkType;
 +            updateTelephony();
 +        }
 +
 +        @Override
 +        public void onDataActivity(int direction) {
 +            if (DEBUG) {
 +                Log.d(mTag, "onDataActivity: direction=" + direction);
 +            }
 +            setActivity(direction);
 +        }
 +    };
 +
 +    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,
 +                int[] qsDataType) {
 +            super(name, sbIcons, qsIcons, contentDesc, sbNullState, qsNullState, sbDiscState,
 +                    qsDiscState, discContentDesc);
 +            mDataContentDescription = dataContentDesc;
 +            mDataType = dataType;
 +            mIsWide = isWide;
 +            mQsDataType = qsDataType;
 +        }
 +    }
 +
 +    static class MobileState extends SignalController.State {
 +        String networkName;
 +        boolean dataSim;
 +        boolean dataConnected;
 +        boolean isEmergency;
 +        boolean airplaneMode;
 +        int inetForNetwork;
 +
 +        @Override
 +        public void copyFrom(State s) {
 +            super.copyFrom(s);
 +            MobileState state = (MobileState) s;
 +            dataSim = state.dataSim;
 +            networkName = state.networkName;
 +            dataConnected = state.dataConnected;
 +            inetForNetwork = state.inetForNetwork;
 +            isEmergency = state.isEmergency;
 +            airplaneMode = state.airplaneMode;
 +        }
 +
 +        @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("dataConnected=").append(dataConnected).append(',');
 +            builder.append("inetForNetwork=").append(inetForNetwork).append(',');
 +            builder.append("isEmergency=").append(isEmergency).append(',');
 +            builder.append("airplaneMode=").append(airplaneMode);
 +        }
 +
 +        @Override
 +        public boolean equals(Object o) {
 +            return super.equals(o)
 +                    && Objects.equals(((MobileState) o).networkName, networkName)
 +                    && ((MobileState) o).dataSim == dataSim
 +                    && ((MobileState) o).dataConnected == dataConnected
 +                    && ((MobileState) o).isEmergency == isEmergency
 +                    && ((MobileState) o).airplaneMode == airplaneMode
 +                    && ((MobileState) o).inetForNetwork == inetForNetwork;
 +        }
 +    }
 +}