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);
+ }
+ }
+}