SysUI: Separate SignalControllers into separate files

and remove the TODO that says to do it.

Change-Id: I54ac3f27f9246aea87d21f2a1da6608ae675aae6
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
new file mode 100644
index 0000000..45b1e24
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -0,0 +1,545 @@
+/*
+ * 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);
+        // Only send data sim callbacks to QS.
+        if (mCurrentState.dataSim) {
+            int qsTypeIcon = mCurrentState.dataConnected ?
+                    icons.mQsDataType[mCurrentState.inetForNetwork] : 0;
+            int length = mSignalsChangedCallbacks.size();
+            for (int i = 0; i < length; i++) {
+                mSignalsChangedCallbacks.get(i).onMobileDataSignalChanged(mCurrentState.enabled
+                        && !mCurrentState.isEmergency && !mCurrentState.airplaneMode,
+                        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);
+            }
+        }
+        boolean showDataIcon = mCurrentState.dataConnected && mCurrentState.inetForNetwork != 0
+                || mCurrentState.iconGroup == TelephonyIcons.ROAMING;
+        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;
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 3642eae..5fce0cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -18,9 +18,8 @@
 
 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
 import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH;
-import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
-import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -28,34 +27,20 @@
 import android.content.res.Resources;
 import android.net.ConnectivityManager;
 import android.net.NetworkCapabilities;
-import android.net.NetworkInfo;
-import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
 import android.os.AsyncTask;
 import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Messenger;
 import android.provider.Settings;
-import android.telephony.PhoneStateListener;
-import android.telephony.ServiceState;
-import android.telephony.SignalStrength;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
-import android.text.format.DateFormat;
 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.PhoneConstants;
 import com.android.internal.telephony.TelephonyIntents;
-import com.android.internal.telephony.cdma.EriInfo;
-import com.android.internal.util.AsyncChannel;
 import com.android.systemui.DemoMode;
 import com.android.systemui.R;
 
@@ -69,7 +54,6 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
-import java.util.Objects;
 
 /** Platform implementation of the network controller. **/
 public class NetworkControllerImpl extends BroadcastReceiver
@@ -79,12 +63,6 @@
     static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     // additional diagnostics, but not logspew
     static final boolean CHATTY =  Log.isLoggable(TAG + ".Chat", Log.DEBUG);
-    // Save the previous SignalController.States of all SignalControllers for dumps.
-    static final boolean RECORD_HISTORY = true;
-    // If RECORD_HISTORY how many to save, must be a power of 2.
-    static final int HISTORY_SIZE = 16;
-
-    private static final int INET_CONDITION_THRESHOLD = 50;
 
     private final Context mContext;
     private final TelephonyManager mPhone;
@@ -821,969 +799,6 @@
         };
     };
 
-    // TODO: Move to its own file.
-    static class WifiSignalController extends
-            SignalController<WifiSignalController.WifiState, SignalController.IconGroup> {
-        private final WifiManager mWifiManager;
-        private final AsyncChannel mWifiChannel;
-        private final boolean mHasMobileData;
-
-        public WifiSignalController(Context context, boolean hasMobileData,
-                List<NetworkSignalChangedCallback> signalCallbacks,
-                List<SignalCluster> signalClusters, NetworkControllerImpl networkController) {
-            super("WifiSignalController", context, NetworkCapabilities.TRANSPORT_WIFI,
-                    signalCallbacks, signalClusters, networkController);
-            mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
-            mHasMobileData = hasMobileData;
-            Handler handler = new WifiHandler();
-            mWifiChannel = new AsyncChannel();
-            Messenger wifiMessenger = mWifiManager.getWifiServiceMessenger();
-            if (wifiMessenger != null) {
-                mWifiChannel.connect(context, handler, wifiMessenger);
-            }
-            // WiFi only has one state.
-            mCurrentState.iconGroup = mLastState.iconGroup = new IconGroup(
-                    "Wi-Fi Icons",
-                    WifiIcons.WIFI_SIGNAL_STRENGTH,
-                    WifiIcons.QS_WIFI_SIGNAL_STRENGTH,
-                    AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH,
-                    WifiIcons.WIFI_NO_NETWORK,
-                    WifiIcons.QS_WIFI_NO_NETWORK,
-                    WifiIcons.WIFI_NO_NETWORK,
-                    WifiIcons.QS_WIFI_NO_NETWORK,
-                    AccessibilityContentDescriptions.WIFI_NO_CONNECTION
-                    );
-        }
-
-        @Override
-        protected WifiState cleanState() {
-            return new WifiState();
-        }
-
-        @Override
-        public void notifyListeners() {
-            // only show wifi in the cluster if connected or if wifi-only
-            boolean wifiVisible = mCurrentState.enabled
-                    && (mCurrentState.connected || !mHasMobileData);
-            String wifiDesc = wifiVisible ? mCurrentState.ssid : null;
-            boolean ssidPresent = wifiVisible && mCurrentState.ssid != null;
-            String contentDescription = getStringIfExists(getContentDescription());
-            int length = mSignalsChangedCallbacks.size();
-            for (int i = 0; i < length; i++) {
-                mSignalsChangedCallbacks.get(i).onWifiSignalChanged(mCurrentState.enabled,
-                        mCurrentState.connected, getQsCurrentIconId(),
-                        ssidPresent && mCurrentState.activityIn,
-                        ssidPresent && mCurrentState.activityOut, contentDescription, wifiDesc);
-            }
-
-            int signalClustersLength = mSignalClusters.size();
-            for (int i = 0; i < signalClustersLength; i++) {
-                mSignalClusters.get(i).setWifiIndicators(wifiVisible, getCurrentIconId(),
-                        contentDescription);
-            }
-        }
-
-        /**
-         * Extract wifi state directly from broadcasts about changes in wifi state.
-         */
-        public void handleBroadcast(Intent intent) {
-            String action = intent.getAction();
-            if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
-                mCurrentState.enabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
-                        WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED;
-            } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
-                final NetworkInfo networkInfo = (NetworkInfo)
-                        intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
-                mCurrentState.connected = networkInfo != null && networkInfo.isConnected();
-                // If Connected grab the signal strength and ssid.
-                if (mCurrentState.connected) {
-                    // try getting it out of the intent first
-                    WifiInfo info = intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO) != null
-                            ? (WifiInfo) intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO)
-                            : mWifiManager.getConnectionInfo();
-                    if (info != null) {
-                        mCurrentState.ssid = getSsid(info);
-                    } else {
-                        mCurrentState.ssid = null;
-                    }
-                } else if (!mCurrentState.connected) {
-                    mCurrentState.ssid = null;
-                }
-            } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
-                // Default to -200 as its below WifiManager.MIN_RSSI.
-                mCurrentState.rssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200);
-                mCurrentState.level = WifiManager.calculateSignalLevel(
-                        mCurrentState.rssi, WifiIcons.WIFI_LEVEL_COUNT);
-            }
-
-            notifyListenersIfNecessary();
-        }
-
-        private String getSsid(WifiInfo info) {
-            String ssid = info.getSSID();
-            if (ssid != null) {
-                return ssid;
-            }
-            // OK, it's not in the connectionInfo; we have to go hunting for it
-            List<WifiConfiguration> networks = mWifiManager.getConfiguredNetworks();
-            int length = networks.size();
-            for (int i = 0; i < length; i++) {
-                if (networks.get(i).networkId == info.getNetworkId()) {
-                    return networks.get(i).SSID;
-                }
-            }
-            return null;
-        }
-
-        @VisibleForTesting
-        void setActivity(int wifiActivity) {
-            mCurrentState.activityIn = wifiActivity == WifiManager.DATA_ACTIVITY_INOUT
-                    || wifiActivity == WifiManager.DATA_ACTIVITY_IN;
-            mCurrentState.activityOut = wifiActivity == WifiManager.DATA_ACTIVITY_INOUT
-                    || wifiActivity == WifiManager.DATA_ACTIVITY_OUT;
-            notifyListenersIfNecessary();
-        }
-
-        /**
-         * Handler to receive the data activity on wifi.
-         */
-        class WifiHandler extends Handler {
-            @Override
-            public void handleMessage(Message msg) {
-                switch (msg.what) {
-                    case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
-                        if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
-                            mWifiChannel.sendMessage(Message.obtain(this,
-                                    AsyncChannel.CMD_CHANNEL_FULL_CONNECTION));
-                        } else {
-                            Log.e(mTag, "Failed to connect to wifi");
-                        }
-                        break;
-                    case WifiManager.DATA_ACTIVITY_NOTIFICATION:
-                        setActivity(msg.arg1);
-                        break;
-                    default:
-                        // Ignore
-                        break;
-                }
-            }
-        }
-
-        static class WifiState extends SignalController.State {
-            String ssid;
-
-            @Override
-            public void copyFrom(State s) {
-                super.copyFrom(s);
-                WifiState state = (WifiState) s;
-                ssid = state.ssid;
-            }
-
-            @Override
-            protected void toString(StringBuilder builder) {
-                super.toString(builder);
-                builder.append(',').append("ssid=").append(ssid);
-            }
-
-            @Override
-            public boolean equals(Object o) {
-                return super.equals(o)
-                        && Objects.equals(((WifiState) o).ssid, ssid);
-            }
-        }
-    }
-
-    // TODO: Move to its own file.
-    public static 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);
-            // Only send data sim callbacks to QS.
-            if (mCurrentState.dataSim) {
-                int qsTypeIcon = mCurrentState.dataConnected ?
-                        icons.mQsDataType[mCurrentState.inetForNetwork] : 0;
-                int length = mSignalsChangedCallbacks.size();
-                for (int i = 0; i < length; i++) {
-                    mSignalsChangedCallbacks.get(i).onMobileDataSignalChanged(mCurrentState.enabled
-                            && !mCurrentState.isEmergency && !mCurrentState.airplaneMode,
-                            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);
-                }
-            }
-            boolean showDataIcon = mCurrentState.dataConnected && mCurrentState.inetForNetwork != 0
-                    || mCurrentState.iconGroup == TelephonyIcons.ROAMING;
-            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(TAG, "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;
-            }
-        }
-    }
-
-    /**
-     * Common base class for handling signal for both wifi and mobile data.
-     */
-    static abstract class SignalController<T extends SignalController.State,
-            I extends SignalController.IconGroup> {
-        protected final String mTag;
-        protected final T mCurrentState;
-        protected final T mLastState;
-        protected final int mTransportType;
-        protected final Context mContext;
-        // The owner of the SignalController (i.e. NetworkController will maintain the following
-        // lists and call notifyListeners whenever the list has changed to ensure everyone
-        // is aware of current state.
-        protected final List<NetworkSignalChangedCallback> mSignalsChangedCallbacks;
-        protected final List<SignalCluster> mSignalClusters;
-        protected final NetworkControllerImpl mNetworkController;
-
-        // Save the previous HISTORY_SIZE states for logging.
-        private final State[] mHistory;
-        // Where to copy the next state into.
-        private int mHistoryIndex;
-
-        public SignalController(String tag, Context context, int type,
-                List<NetworkSignalChangedCallback> signalCallbacks,
-                List<SignalCluster> signalClusters, NetworkControllerImpl networkController) {
-            mTag = TAG + "." + tag;
-            mNetworkController = networkController;
-            mTransportType = type;
-            mContext = context;
-            mSignalsChangedCallbacks = signalCallbacks;
-            mSignalClusters = signalClusters;
-            mCurrentState = cleanState();
-            mLastState = cleanState();
-            if (RECORD_HISTORY) {
-                mHistory = new State[HISTORY_SIZE];
-                for (int i = 0; i < HISTORY_SIZE; i++) {
-                    mHistory[i] = cleanState();
-                }
-            }
-        }
-
-        public T getState() {
-            return mCurrentState;
-        }
-
-        public int getTransportType() {
-            return mTransportType;
-        }
-
-        public void setInetCondition(int inetCondition) {
-            mCurrentState.inetCondition = inetCondition;
-            notifyListenersIfNecessary();
-        }
-
-        /**
-         * Used at the end of demo mode to clear out any ugly state that it has created.
-         * Since we haven't had any callbacks, then isDirty will not have been triggered,
-         * so we can just take the last good state directly from there.
-         *
-         * Used for demo mode.
-         */
-        void resetLastState() {
-            mCurrentState.copyFrom(mLastState);
-        }
-
-        /**
-         * Determines if the state of this signal controller has changed and
-         * needs to trigger callbacks related to it.
-         */
-        public boolean isDirty() {
-            if (!mLastState.equals(mCurrentState)) {
-                if (DEBUG) {
-                    Log.d(mTag, "Change in state from: " + mLastState + "\n"
-                            + "\tto: " + mCurrentState);
-                }
-                return true;
-            }
-            return false;
-        }
-
-        public void saveLastState() {
-            if (RECORD_HISTORY) {
-                recordLastState();
-            }
-            // Updates the current time.
-            mCurrentState.time = System.currentTimeMillis();
-            mLastState.copyFrom(mCurrentState);
-        }
-
-        /**
-         * Gets the signal icon for QS based on current state of connected, enabled, and level.
-         */
-        public int getQsCurrentIconId() {
-            if (mCurrentState.connected) {
-                return getIcons().mQsIcons[mCurrentState.inetCondition][mCurrentState.level];
-            } else if (mCurrentState.enabled) {
-                return getIcons().mQsDiscState;
-            } else {
-                return getIcons().mQsNullState;
-            }
-        }
-
-        /**
-         * Gets the signal icon for SB based on current state of connected, enabled, and level.
-         */
-        public int getCurrentIconId() {
-            if (mCurrentState.connected) {
-                return getIcons().mSbIcons[mCurrentState.inetCondition][mCurrentState.level];
-            } else if (mCurrentState.enabled) {
-                return getIcons().mSbDiscState;
-            } else {
-                return getIcons().mSbNullState;
-            }
-        }
-
-        /**
-         * Gets the content description id for the signal based on current state of connected and
-         * level.
-         */
-        public int getContentDescription() {
-            if (mCurrentState.connected) {
-                return getIcons().mContentDesc[mCurrentState.level];
-            } else {
-                return getIcons().mDiscContentDesc;
-            }
-        }
-
-        public void notifyListenersIfNecessary() {
-            if (isDirty()) {
-                saveLastState();
-                notifyListeners();
-                mNetworkController.refreshCarrierLabel();
-            }
-        }
-
-        /**
-         * Returns the resource if resId is not 0, and an empty string otherwise.
-         */
-        protected String getStringIfExists(int resId) {
-            return resId != 0 ? mContext.getString(resId) : "";
-        }
-
-        protected I getIcons() {
-            return (I) mCurrentState.iconGroup;
-        }
-
-        /**
-         * Saves the last state of any changes, so we can log the current
-         * and last value of any state data.
-         */
-        protected void recordLastState() {
-            mHistory[mHistoryIndex++ & (HISTORY_SIZE - 1)].copyFrom(mLastState);
-        }
-
-        public void dump(PrintWriter pw) {
-            pw.println("  - " + mTag + " -----");
-            pw.println("  Current State: " + mCurrentState);
-            if (RECORD_HISTORY) {
-                // Count up the states that actually contain time stamps, and only display those.
-                int size = 0;
-                for (int i = 0; i < HISTORY_SIZE; i++) {
-                    if (mHistory[i].time != 0) size++;
-                }
-                // Print out the previous states in ordered number.
-                for (int i = mHistoryIndex + HISTORY_SIZE - 1;
-                        i >= mHistoryIndex + HISTORY_SIZE - size; i--) {
-                    pw.println("  Previous State(" + (mHistoryIndex + HISTORY_SIZE - i) + ": "
-                            + mHistory[i & (HISTORY_SIZE - 1)]);
-                }
-            }
-        }
-
-        /**
-         * Trigger callbacks based on current state.  The callbacks should be completely
-         * based on current state, and only need to be called in the scenario where
-         * mCurrentState != mLastState.
-         */
-        public abstract void notifyListeners();
-
-        /**
-         * Generate a blank T.
-         */
-        protected abstract T cleanState();
-
-        /*
-         * Holds icons for a given state. Arrays are generally indexed as inet
-         * state (full connectivity or not) first, and second dimension as
-         * signal strength.
-         */
-        static class IconGroup {
-            final int[][] mSbIcons;
-            final int[][] mQsIcons;
-            final int[] mContentDesc;
-            final int mSbNullState;
-            final int mQsNullState;
-            final int mSbDiscState;
-            final int mQsDiscState;
-            final int mDiscContentDesc;
-            // For logging.
-            final String mName;
-
-            public IconGroup(String name, int[][] sbIcons, int[][] qsIcons, int[] contentDesc,
-                    int sbNullState, int qsNullState, int sbDiscState, int qsDiscState,
-                    int discContentDesc) {
-                mName = name;
-                mSbIcons = sbIcons;
-                mQsIcons = qsIcons;
-                mContentDesc = contentDesc;
-                mSbNullState = sbNullState;
-                mQsNullState = qsNullState;
-                mSbDiscState = sbDiscState;
-                mQsDiscState = qsDiscState;
-                mDiscContentDesc = discContentDesc;
-            }
-
-            @Override
-            public String toString() {
-                return "IconGroup(" + mName + ")";
-            }
-        }
-
-        static class State {
-            boolean connected;
-            boolean enabled;
-            boolean activityIn;
-            boolean activityOut;
-            int level;
-            IconGroup iconGroup;
-            int inetCondition;
-            int rssi; // Only for logging.
-
-            // Not used for comparison, just used for logging.
-            long time;
-
-            public void copyFrom(State state) {
-                connected = state.connected;
-                enabled = state.enabled;
-                level = state.level;
-                iconGroup = state.iconGroup;
-                inetCondition = state.inetCondition;
-                activityIn = state.activityIn;
-                activityOut = state.activityOut;
-                rssi = state.rssi;
-                time = state.time;
-            }
-
-            @Override
-            public String toString() {
-                if (time != 0) {
-                    StringBuilder builder = new StringBuilder();
-                    toString(builder);
-                    return builder.toString();
-                } else {
-                    return "Empty " + getClass().getSimpleName();
-                }
-            }
-
-            protected void toString(StringBuilder builder) {
-                builder.append("connected=").append(connected).append(',')
-                        .append("enabled=").append(enabled).append(',')
-                        .append("level=").append(level).append(',')
-                        .append("inetCondition=").append(inetCondition).append(',')
-                        .append("iconGroup=").append(iconGroup).append(',')
-                        .append("activityIn=").append(activityIn).append(',')
-                        .append("activityOut=").append(activityOut).append(',')
-                        .append("rssi=").append(rssi).append(',')
-                        .append("lastModified=").append(DateFormat.format("MM-dd hh:mm:ss", time));
-            }
-
-            @Override
-            public boolean equals(Object o) {
-                if (!o.getClass().equals(getClass())) {
-                    return false;
-                }
-                State other = (State) o;
-                return other.connected == connected
-                        && other.enabled == enabled
-                        && other.level == level
-                        && other.inetCondition == inetCondition
-                        && other.iconGroup == iconGroup
-                        && other.activityIn == activityIn
-                        && other.activityOut == activityOut
-                        && other.rssi == rssi;
-            }
-        }
-    }
-
     public interface SignalCluster {
         void setWifiIndicators(boolean visible, int strengthIcon, String contentDescription);
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java
new file mode 100644
index 0000000..14c3d9c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java
@@ -0,0 +1,324 @@
+/*
+ * 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 static com.android.systemui.statusbar.policy.NetworkControllerImpl.TAG;
+
+import android.content.Context;
+import android.text.format.DateFormat;
+import android.util.Log;
+
+import com.android.systemui.statusbar.policy.NetworkController.NetworkSignalChangedCallback;
+import com.android.systemui.statusbar.policy.NetworkControllerImpl.SignalCluster;
+
+import java.io.PrintWriter;
+import java.util.List;
+
+
+/**
+ * Common base class for handling signal for both wifi and mobile data.
+ */
+public abstract class SignalController<T extends SignalController.State,
+        I extends SignalController.IconGroup> {
+    // Save the previous SignalController.States of all SignalControllers for dumps.
+    static final boolean RECORD_HISTORY = true;
+    // If RECORD_HISTORY how many to save, must be a power of 2.
+    static final int HISTORY_SIZE = 16;
+
+    protected static final boolean DEBUG = NetworkControllerImpl.DEBUG;
+    protected static final boolean CHATTY = NetworkControllerImpl.CHATTY;
+
+    protected final String mTag;
+    protected final T mCurrentState;
+    protected final T mLastState;
+    protected final int mTransportType;
+    protected final Context mContext;
+    // The owner of the SignalController (i.e. NetworkController will maintain the following
+    // lists and call notifyListeners whenever the list has changed to ensure everyone
+    // is aware of current state.
+    protected final List<NetworkSignalChangedCallback> mSignalsChangedCallbacks;
+    protected final List<SignalCluster> mSignalClusters;
+    protected final NetworkControllerImpl mNetworkController;
+
+    // Save the previous HISTORY_SIZE states for logging.
+    private final State[] mHistory;
+    // Where to copy the next state into.
+    private int mHistoryIndex;
+
+    public SignalController(String tag, Context context, int type,
+            List<NetworkSignalChangedCallback> signalCallbacks,
+            List<SignalCluster> signalClusters, NetworkControllerImpl networkController) {
+        mTag = TAG + "." + tag;
+        mNetworkController = networkController;
+        mTransportType = type;
+        mContext = context;
+        mSignalsChangedCallbacks = signalCallbacks;
+        mSignalClusters = signalClusters;
+        mCurrentState = cleanState();
+        mLastState = cleanState();
+        if (RECORD_HISTORY) {
+            mHistory = new State[HISTORY_SIZE];
+            for (int i = 0; i < HISTORY_SIZE; i++) {
+                mHistory[i] = cleanState();
+            }
+        }
+    }
+
+    public T getState() {
+        return mCurrentState;
+    }
+
+    public int getTransportType() {
+        return mTransportType;
+    }
+
+    public void setInetCondition(int inetCondition) {
+        mCurrentState.inetCondition = inetCondition;
+        notifyListenersIfNecessary();
+    }
+
+    /**
+     * Used at the end of demo mode to clear out any ugly state that it has created.
+     * Since we haven't had any callbacks, then isDirty will not have been triggered,
+     * so we can just take the last good state directly from there.
+     *
+     * Used for demo mode.
+     */
+    public void resetLastState() {
+        mCurrentState.copyFrom(mLastState);
+    }
+
+    /**
+     * Determines if the state of this signal controller has changed and
+     * needs to trigger callbacks related to it.
+     */
+    public boolean isDirty() {
+        if (!mLastState.equals(mCurrentState)) {
+            if (DEBUG) {
+                Log.d(mTag, "Change in state from: " + mLastState + "\n"
+                        + "\tto: " + mCurrentState);
+            }
+            return true;
+        }
+        return false;
+    }
+
+    public void saveLastState() {
+        if (RECORD_HISTORY) {
+            recordLastState();
+        }
+        // Updates the current time.
+        mCurrentState.time = System.currentTimeMillis();
+        mLastState.copyFrom(mCurrentState);
+    }
+
+    /**
+     * Gets the signal icon for QS based on current state of connected, enabled, and level.
+     */
+    public int getQsCurrentIconId() {
+        if (mCurrentState.connected) {
+            return getIcons().mQsIcons[mCurrentState.inetCondition][mCurrentState.level];
+        } else if (mCurrentState.enabled) {
+            return getIcons().mQsDiscState;
+        } else {
+            return getIcons().mQsNullState;
+        }
+    }
+
+    /**
+     * Gets the signal icon for SB based on current state of connected, enabled, and level.
+     */
+    public int getCurrentIconId() {
+        if (mCurrentState.connected) {
+            return getIcons().mSbIcons[mCurrentState.inetCondition][mCurrentState.level];
+        } else if (mCurrentState.enabled) {
+            return getIcons().mSbDiscState;
+        } else {
+            return getIcons().mSbNullState;
+        }
+    }
+
+    /**
+     * Gets the content description id for the signal based on current state of connected and
+     * level.
+     */
+    public int getContentDescription() {
+        if (mCurrentState.connected) {
+            return getIcons().mContentDesc[mCurrentState.level];
+        } else {
+            return getIcons().mDiscContentDesc;
+        }
+    }
+
+    public void notifyListenersIfNecessary() {
+        if (isDirty()) {
+            saveLastState();
+            notifyListeners();
+            mNetworkController.refreshCarrierLabel();
+        }
+    }
+
+    /**
+     * Returns the resource if resId is not 0, and an empty string otherwise.
+     */
+    protected String getStringIfExists(int resId) {
+        return resId != 0 ? mContext.getString(resId) : "";
+    }
+
+    protected I getIcons() {
+        return (I) mCurrentState.iconGroup;
+    }
+
+    /**
+     * Saves the last state of any changes, so we can log the current
+     * and last value of any state data.
+     */
+    protected void recordLastState() {
+        mHistory[mHistoryIndex++ & (HISTORY_SIZE - 1)].copyFrom(mLastState);
+    }
+
+    public void dump(PrintWriter pw) {
+        pw.println("  - " + mTag + " -----");
+        pw.println("  Current State: " + mCurrentState);
+        if (RECORD_HISTORY) {
+            // Count up the states that actually contain time stamps, and only display those.
+            int size = 0;
+            for (int i = 0; i < HISTORY_SIZE; i++) {
+                if (mHistory[i].time != 0) size++;
+            }
+            // Print out the previous states in ordered number.
+            for (int i = mHistoryIndex + HISTORY_SIZE - 1;
+                    i >= mHistoryIndex + HISTORY_SIZE - size; i--) {
+                pw.println("  Previous State(" + (mHistoryIndex + HISTORY_SIZE - i) + "): "
+                        + mHistory[i & (HISTORY_SIZE - 1)]);
+            }
+        }
+    }
+
+    /**
+     * Trigger callbacks based on current state.  The callbacks should be completely
+     * based on current state, and only need to be called in the scenario where
+     * mCurrentState != mLastState.
+     */
+    public abstract void notifyListeners();
+
+    /**
+     * Generate a blank T.
+     */
+    protected abstract T cleanState();
+
+    /*
+     * Holds icons for a given state. Arrays are generally indexed as inet
+     * state (full connectivity or not) first, and second dimension as
+     * signal strength.
+     */
+    static class IconGroup {
+        final int[][] mSbIcons;
+        final int[][] mQsIcons;
+        final int[] mContentDesc;
+        final int mSbNullState;
+        final int mQsNullState;
+        final int mSbDiscState;
+        final int mQsDiscState;
+        final int mDiscContentDesc;
+        // For logging.
+        final String mName;
+
+        public IconGroup(String name, int[][] sbIcons, int[][] qsIcons, int[] contentDesc,
+                int sbNullState, int qsNullState, int sbDiscState, int qsDiscState,
+                int discContentDesc) {
+            mName = name;
+            mSbIcons = sbIcons;
+            mQsIcons = qsIcons;
+            mContentDesc = contentDesc;
+            mSbNullState = sbNullState;
+            mQsNullState = qsNullState;
+            mSbDiscState = sbDiscState;
+            mQsDiscState = qsDiscState;
+            mDiscContentDesc = discContentDesc;
+        }
+
+        @Override
+        public String toString() {
+            return "IconGroup(" + mName + ")";
+        }
+    }
+
+    static class State {
+        boolean connected;
+        boolean enabled;
+        boolean activityIn;
+        boolean activityOut;
+        int level;
+        IconGroup iconGroup;
+        int inetCondition;
+        int rssi; // Only for logging.
+
+        // Not used for comparison, just used for logging.
+        long time;
+
+        public void copyFrom(State state) {
+            connected = state.connected;
+            enabled = state.enabled;
+            level = state.level;
+            iconGroup = state.iconGroup;
+            inetCondition = state.inetCondition;
+            activityIn = state.activityIn;
+            activityOut = state.activityOut;
+            rssi = state.rssi;
+            time = state.time;
+        }
+
+        @Override
+        public String toString() {
+            if (time != 0) {
+                StringBuilder builder = new StringBuilder();
+                toString(builder);
+                return builder.toString();
+            } else {
+                return "Empty " + getClass().getSimpleName();
+            }
+        }
+
+        protected void toString(StringBuilder builder) {
+            builder.append("connected=").append(connected).append(',')
+                    .append("enabled=").append(enabled).append(',')
+                    .append("level=").append(level).append(',')
+                    .append("inetCondition=").append(inetCondition).append(',')
+                    .append("iconGroup=").append(iconGroup).append(',')
+                    .append("activityIn=").append(activityIn).append(',')
+                    .append("activityOut=").append(activityOut).append(',')
+                    .append("rssi=").append(rssi).append(',')
+                    .append("lastModified=").append(DateFormat.format("MM-dd hh:mm:ss", time));
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (!o.getClass().equals(getClass())) {
+                return false;
+            }
+            State other = (State) o;
+            return other.connected == connected
+                    && other.enabled == enabled
+                    && other.level == level
+                    && other.inetCondition == inetCondition
+                    && other.iconGroup == iconGroup
+                    && other.activityIn == activityIn
+                    && other.activityOut == activityOut
+                    && other.rssi == rssi;
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
index 4091619..d266ed8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
@@ -17,7 +17,7 @@
 package com.android.systemui.statusbar.policy;
 
 import com.android.systemui.R;
-import com.android.systemui.statusbar.policy.NetworkControllerImpl.MobileSignalController.MobileIconGroup;
+import com.android.systemui.statusbar.policy.MobileSignalController.MobileIconGroup;
 
 class TelephonyIcons {
     //***** Signal strength icons
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
new file mode 100644
index 0000000..a97ca50
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -0,0 +1,208 @@
+/*
+ * 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.net.NetworkInfo;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Messenger;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.AsyncChannel;
+import com.android.systemui.statusbar.policy.NetworkController.NetworkSignalChangedCallback;
+import com.android.systemui.statusbar.policy.NetworkControllerImpl.SignalCluster;
+
+import java.util.List;
+import java.util.Objects;
+
+
+public class WifiSignalController extends
+        SignalController<WifiSignalController.WifiState, SignalController.IconGroup> {
+    private final WifiManager mWifiManager;
+    private final AsyncChannel mWifiChannel;
+    private final boolean mHasMobileData;
+
+    public WifiSignalController(Context context, boolean hasMobileData,
+            List<NetworkSignalChangedCallback> signalCallbacks,
+            List<SignalCluster> signalClusters, NetworkControllerImpl networkController) {
+        super("WifiSignalController", context, NetworkCapabilities.TRANSPORT_WIFI,
+                signalCallbacks, signalClusters, networkController);
+        mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+        mHasMobileData = hasMobileData;
+        Handler handler = new WifiHandler();
+        mWifiChannel = new AsyncChannel();
+        Messenger wifiMessenger = mWifiManager.getWifiServiceMessenger();
+        if (wifiMessenger != null) {
+            mWifiChannel.connect(context, handler, wifiMessenger);
+        }
+        // WiFi only has one state.
+        mCurrentState.iconGroup = mLastState.iconGroup = new IconGroup(
+                "Wi-Fi Icons",
+                WifiIcons.WIFI_SIGNAL_STRENGTH,
+                WifiIcons.QS_WIFI_SIGNAL_STRENGTH,
+                AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH,
+                WifiIcons.WIFI_NO_NETWORK,
+                WifiIcons.QS_WIFI_NO_NETWORK,
+                WifiIcons.WIFI_NO_NETWORK,
+                WifiIcons.QS_WIFI_NO_NETWORK,
+                AccessibilityContentDescriptions.WIFI_NO_CONNECTION
+                );
+    }
+
+    @Override
+    protected WifiState cleanState() {
+        return new WifiState();
+    }
+
+    @Override
+    public void notifyListeners() {
+        // only show wifi in the cluster if connected or if wifi-only
+        boolean wifiVisible = mCurrentState.enabled
+                && (mCurrentState.connected || !mHasMobileData);
+        String wifiDesc = wifiVisible ? mCurrentState.ssid : null;
+        boolean ssidPresent = wifiVisible && mCurrentState.ssid != null;
+        String contentDescription = getStringIfExists(getContentDescription());
+        int length = mSignalsChangedCallbacks.size();
+        for (int i = 0; i < length; i++) {
+            mSignalsChangedCallbacks.get(i).onWifiSignalChanged(mCurrentState.enabled,
+                    mCurrentState.connected, getQsCurrentIconId(),
+                    ssidPresent && mCurrentState.activityIn,
+                    ssidPresent && mCurrentState.activityOut, contentDescription, wifiDesc);
+        }
+
+        int signalClustersLength = mSignalClusters.size();
+        for (int i = 0; i < signalClustersLength; i++) {
+            mSignalClusters.get(i).setWifiIndicators(wifiVisible, getCurrentIconId(),
+                    contentDescription);
+        }
+    }
+
+    /**
+     * Extract wifi state directly from broadcasts about changes in wifi state.
+     */
+    public void handleBroadcast(Intent intent) {
+        String action = intent.getAction();
+        if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
+            mCurrentState.enabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
+                    WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED;
+        } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
+            final NetworkInfo networkInfo = (NetworkInfo)
+                    intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
+            mCurrentState.connected = networkInfo != null && networkInfo.isConnected();
+            // If Connected grab the signal strength and ssid.
+            if (mCurrentState.connected) {
+                // try getting it out of the intent first
+                WifiInfo info = intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO) != null
+                        ? (WifiInfo) intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO)
+                        : mWifiManager.getConnectionInfo();
+                if (info != null) {
+                    mCurrentState.ssid = getSsid(info);
+                } else {
+                    mCurrentState.ssid = null;
+                }
+            } else if (!mCurrentState.connected) {
+                mCurrentState.ssid = null;
+            }
+        } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
+            // Default to -200 as its below WifiManager.MIN_RSSI.
+            mCurrentState.rssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200);
+            mCurrentState.level = WifiManager.calculateSignalLevel(
+                    mCurrentState.rssi, WifiIcons.WIFI_LEVEL_COUNT);
+        }
+
+        notifyListenersIfNecessary();
+    }
+
+    private String getSsid(WifiInfo info) {
+        String ssid = info.getSSID();
+        if (ssid != null) {
+            return ssid;
+        }
+        // OK, it's not in the connectionInfo; we have to go hunting for it
+        List<WifiConfiguration> networks = mWifiManager.getConfiguredNetworks();
+        int length = networks.size();
+        for (int i = 0; i < length; i++) {
+            if (networks.get(i).networkId == info.getNetworkId()) {
+                return networks.get(i).SSID;
+            }
+        }
+        return null;
+    }
+
+    @VisibleForTesting
+    void setActivity(int wifiActivity) {
+        mCurrentState.activityIn = wifiActivity == WifiManager.DATA_ACTIVITY_INOUT
+                || wifiActivity == WifiManager.DATA_ACTIVITY_IN;
+        mCurrentState.activityOut = wifiActivity == WifiManager.DATA_ACTIVITY_INOUT
+                || wifiActivity == WifiManager.DATA_ACTIVITY_OUT;
+        notifyListenersIfNecessary();
+    }
+
+    /**
+     * Handler to receive the data activity on wifi.
+     */
+    private class WifiHandler extends Handler {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
+                    if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
+                        mWifiChannel.sendMessage(Message.obtain(this,
+                                AsyncChannel.CMD_CHANNEL_FULL_CONNECTION));
+                    } else {
+                        Log.e(mTag, "Failed to connect to wifi");
+                    }
+                    break;
+                case WifiManager.DATA_ACTIVITY_NOTIFICATION:
+                    setActivity(msg.arg1);
+                    break;
+                default:
+                    // Ignore
+                    break;
+            }
+        }
+    }
+
+    static class WifiState extends SignalController.State {
+        String ssid;
+
+        @Override
+        public void copyFrom(State s) {
+            super.copyFrom(s);
+            WifiState state = (WifiState) s;
+            ssid = state.ssid;
+        }
+
+        @Override
+        protected void toString(StringBuilder builder) {
+            super.toString(builder);
+            builder.append(',').append("ssid=").append(ssid);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            return super.equals(o)
+                    && Objects.equals(((WifiState) o).ssid, ssid);
+        }
+    }
+}