/* * Copyright (C) 2010 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 java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.res.Resources; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.net.wimax.WimaxManagerConstants; import android.os.Binder; import android.os.Handler; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; import android.os.SystemProperties; import android.provider.Settings; import android.provider.Telephony; import android.telephony.PhoneStateListener; import android.telephony.ServiceState; import android.telephony.SignalStrength; import android.telephony.TelephonyManager; import android.util.Slog; import android.view.View; import android.widget.ImageView; import android.widget.TextView; import com.android.internal.app.IBatteryStats; import com.android.internal.telephony.IccCard; import com.android.internal.telephony.TelephonyIntents; import com.android.internal.telephony.cdma.EriInfo; import com.android.server.am.BatteryStatsService; import com.android.internal.util.AsyncChannel; import com.android.systemui.R; public class NetworkController extends BroadcastReceiver { // debug static final String TAG = "StatusBar.NetworkController"; static final boolean DEBUG = false; static final boolean CHATTY = false; // additional diagnostics, but not logspew // telephony boolean mHspaDataDistinguishable; final TelephonyManager mPhone; boolean mDataConnected; IccCard.State mSimState = IccCard.State.READY; int mPhoneState = TelephonyManager.CALL_STATE_IDLE; int mDataNetType = TelephonyManager.NETWORK_TYPE_UNKNOWN; int mDataState = TelephonyManager.DATA_DISCONNECTED; int mDataActivity = TelephonyManager.DATA_ACTIVITY_NONE; ServiceState mServiceState; SignalStrength mSignalStrength; int[] mDataIconList = TelephonyIcons.DATA_G[0]; String mNetworkName; String mNetworkNameDefault; String mNetworkNameSeparator; int mPhoneSignalIconId; int mDataDirectionIconId; // data + data direction on phones int mDataSignalIconId; int mDataTypeIconId; boolean mDataActive; int mMobileActivityIconId; // overlay arrows for data direction int mLastSignalLevel; boolean mShowPhoneRSSIForData = false; boolean mShowAtLeastThreeGees = false; boolean mAlwaysShowCdmaRssi = false; String mContentDescriptionPhoneSignal; String mContentDescriptionWifi; String mContentDescriptionWimax; String mContentDescriptionCombinedSignal; String mContentDescriptionDataType; // wifi final WifiManager mWifiManager; AsyncChannel mWifiChannel; boolean mWifiEnabled, mWifiConnected; int mWifiRssi, mWifiLevel; String mWifiSsid; int mWifiIconId = 0; int mWifiActivityIconId = 0; // overlay arrows for wifi direction int mWifiActivity = WifiManager.DATA_ACTIVITY_NONE; // bluetooth private boolean mBluetoothTethered = false; private int mBluetoothTetherIconId = com.android.internal.R.drawable.stat_sys_tether_bluetooth; //wimax private boolean mWimaxSupported = false; private boolean mIsWimaxEnabled = false; private boolean mWimaxConnected = false; private boolean mWimaxIdle = false; private int mWimaxIconId = 0; private int mWimaxSignal = 0; private int mWimaxState = 0; private int mWimaxExtraState = 0; // data connectivity (regardless of state, can we access the internet?) // state of inet connection - 0 not connected, 100 connected private int mInetCondition = 0; private static final int INET_CONDITION_THRESHOLD = 50; private boolean mAirplaneMode = false; // our ui Context mContext; ArrayList<ImageView> mPhoneSignalIconViews = new ArrayList<ImageView>(); ArrayList<ImageView> mDataDirectionIconViews = new ArrayList<ImageView>(); ArrayList<ImageView> mDataDirectionOverlayIconViews = new ArrayList<ImageView>(); ArrayList<ImageView> mWifiIconViews = new ArrayList<ImageView>(); ArrayList<ImageView> mWimaxIconViews = new ArrayList<ImageView>(); ArrayList<ImageView> mCombinedSignalIconViews = new ArrayList<ImageView>(); ArrayList<ImageView> mDataTypeIconViews = new ArrayList<ImageView>(); ArrayList<TextView> mCombinedLabelViews = new ArrayList<TextView>(); ArrayList<TextView> mMobileLabelViews = new ArrayList<TextView>(); ArrayList<TextView> mWifiLabelViews = new ArrayList<TextView>(); ArrayList<SignalCluster> mSignalClusters = new ArrayList<SignalCluster>(); int mLastPhoneSignalIconId = -1; int mLastDataDirectionIconId = -1; int mLastDataDirectionOverlayIconId = -1; int mLastWifiIconId = -1; int mLastWimaxIconId = -1; int mLastCombinedSignalIconId = -1; int mLastDataTypeIconId = -1; String mLastCombinedLabel = ""; private boolean mHasMobileDataFeature; boolean mDataAndWifiStacked = false; // yuck -- stop doing this here and put it in the framework IBatteryStats mBatteryStats; public interface SignalCluster { void setWifiIndicators(boolean visible, int strengthIcon, int activityIcon, String contentDescription); void setMobileDataIndicators(boolean visible, int strengthIcon, int activityIcon, int typeIcon, String contentDescription, String typeContentDescription); void setIsAirplaneMode(boolean is); } /** * Construct this controller object and register for updates. */ public NetworkController(Context context) { mContext = context; final Resources res = context.getResources(); ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService( Context.CONNECTIVITY_SERVICE); mHasMobileDataFeature = cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE); mShowPhoneRSSIForData = res.getBoolean(R.bool.config_showPhoneRSSIForData); mShowAtLeastThreeGees = res.getBoolean(R.bool.config_showMin3G); mAlwaysShowCdmaRssi = res.getBoolean( com.android.internal.R.bool.config_alwaysUseCdmaRssi); // set up the default wifi icon, used when no radios have ever appeared updateWifiIcons(); updateWimaxIcons(); // telephony mPhone = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE); mPhone.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS | PhoneStateListener.LISTEN_CALL_STATE | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE | PhoneStateListener.LISTEN_DATA_ACTIVITY); mHspaDataDistinguishable = mContext.getResources().getBoolean( R.bool.config_hspa_data_distinguishable); mNetworkNameSeparator = mContext.getString(R.string.status_bar_network_name_separator); mNetworkNameDefault = mContext.getString( com.android.internal.R.string.lockscreen_carrier_default); mNetworkName = mNetworkNameDefault; // wifi mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); Handler handler = new WifiHandler(); mWifiChannel = new AsyncChannel(); Messenger wifiMessenger = mWifiManager.getMessenger(); if (wifiMessenger != null) { mWifiChannel.connect(mContext, handler, wifiMessenger); } // broadcasts IntentFilter filter = new IntentFilter(); filter.addAction(WifiManager.RSSI_CHANGED_ACTION); filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED); filter.addAction(Telephony.Intents.SPN_STRINGS_UPDATED_ACTION); filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); filter.addAction(ConnectivityManager.INET_CONDITION_ACTION); filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED); mWimaxSupported = mContext.getResources().getBoolean( com.android.internal.R.bool.config_wimaxEnabled); if(mWimaxSupported) { filter.addAction(WimaxManagerConstants.WIMAX_NETWORK_STATE_CHANGED_ACTION); filter.addAction(WimaxManagerConstants.SIGNAL_LEVEL_CHANGED_ACTION); filter.addAction(WimaxManagerConstants.NET_4G_STATE_CHANGED_ACTION); } context.registerReceiver(this, filter); // AIRPLANE_MODE_CHANGED is sent at boot; we've probably already missed it updateAirplaneMode(); // yuck mBatteryStats = BatteryStatsService.getService(); } public void addPhoneSignalIconView(ImageView v) { mPhoneSignalIconViews.add(v); } public void addDataDirectionIconView(ImageView v) { mDataDirectionIconViews.add(v); } public void addDataDirectionOverlayIconView(ImageView v) { mDataDirectionOverlayIconViews.add(v); } public void addWifiIconView(ImageView v) { mWifiIconViews.add(v); } public void addWimaxIconView(ImageView v) { mWimaxIconViews.add(v); } public void addCombinedSignalIconView(ImageView v) { mCombinedSignalIconViews.add(v); } public void addDataTypeIconView(ImageView v) { mDataTypeIconViews.add(v); } public void addCombinedLabelView(TextView v) { mCombinedLabelViews.add(v); } public void addMobileLabelView(TextView v) { mMobileLabelViews.add(v); } public void addWifiLabelView(TextView v) { mWifiLabelViews.add(v); } public void addSignalCluster(SignalCluster cluster) { mSignalClusters.add(cluster); refreshSignalCluster(cluster); } public void refreshSignalCluster(SignalCluster cluster) { cluster.setWifiIndicators( mWifiConnected, // only show wifi in the cluster if connected mWifiIconId, mWifiActivityIconId, mContentDescriptionWifi); if (mIsWimaxEnabled && mWimaxConnected) { // wimax is special cluster.setMobileDataIndicators( true, mAlwaysShowCdmaRssi ? mPhoneSignalIconId : mWimaxIconId, mMobileActivityIconId, mDataTypeIconId, mContentDescriptionWimax, mContentDescriptionDataType); } else { // normal mobile data cluster.setMobileDataIndicators( mHasMobileDataFeature, mShowPhoneRSSIForData ? mPhoneSignalIconId : mDataSignalIconId, mMobileActivityIconId, mDataTypeIconId, mContentDescriptionPhoneSignal, mContentDescriptionDataType); } cluster.setIsAirplaneMode(mAirplaneMode); } public void setStackedMode(boolean stacked) { mDataAndWifiStacked = true; } @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); if (action.equals(WifiManager.RSSI_CHANGED_ACTION) || action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION) || action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) { updateWifiState(intent); refreshViews(); } else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) { updateSimState(intent); updateDataIcon(); refreshViews(); } else if (action.equals(Telephony.Intents.SPN_STRINGS_UPDATED_ACTION)) { updateNetworkName(intent.getBooleanExtra(Telephony.Intents.EXTRA_SHOW_SPN, false), intent.getStringExtra(Telephony.Intents.EXTRA_SPN), intent.getBooleanExtra(Telephony.Intents.EXTRA_SHOW_PLMN, false), intent.getStringExtra(Telephony.Intents.EXTRA_PLMN)); refreshViews(); } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION) || action.equals(ConnectivityManager.INET_CONDITION_ACTION)) { updateConnectivity(intent); refreshViews(); } else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) { refreshViews(); } else if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) { updateAirplaneMode(); refreshViews(); } else if (action.equals(WimaxManagerConstants.NET_4G_STATE_CHANGED_ACTION) || action.equals(WimaxManagerConstants.SIGNAL_LEVEL_CHANGED_ACTION) || action.equals(WimaxManagerConstants.WIMAX_NETWORK_STATE_CHANGED_ACTION)) { updateWimaxState(intent); refreshViews(); } } // ===== Telephony ============================================================== PhoneStateListener mPhoneStateListener = new PhoneStateListener() { @Override public void onSignalStrengthsChanged(SignalStrength signalStrength) { if (DEBUG) { Slog.d(TAG, "onSignalStrengthsChanged signalStrength=" + signalStrength + ((signalStrength == null) ? "" : (" level=" + signalStrength.getLevel()))); } mSignalStrength = signalStrength; updateTelephonySignalStrength(); refreshViews(); } @Override public void onServiceStateChanged(ServiceState state) { if (DEBUG) { Slog.d(TAG, "onServiceStateChanged state=" + state.getState()); } mServiceState = state; updateTelephonySignalStrength(); updateDataNetType(); updateDataIcon(); refreshViews(); } @Override public void onCallStateChanged(int state, String incomingNumber) { if (DEBUG) { Slog.d(TAG, "onCallStateChanged state=" + state); } // In cdma, if a voice call is made, RSSI should switch to 1x. if (isCdma()) { updateTelephonySignalStrength(); refreshViews(); } } @Override public void onDataConnectionStateChanged(int state, int networkType) { if (DEBUG) { Slog.d(TAG, "onDataConnectionStateChanged: state=" + state + " type=" + networkType); } mDataState = state; mDataNetType = networkType; updateDataNetType(); updateDataIcon(); refreshViews(); } @Override public void onDataActivity(int direction) { if (DEBUG) { Slog.d(TAG, "onDataActivity: direction=" + direction); } mDataActivity = direction; updateDataIcon(); refreshViews(); } }; private final void updateSimState(Intent intent) { String stateExtra = intent.getStringExtra(IccCard.INTENT_KEY_ICC_STATE); if (IccCard.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) { mSimState = IccCard.State.ABSENT; } else if (IccCard.INTENT_VALUE_ICC_READY.equals(stateExtra)) { mSimState = IccCard.State.READY; } else if (IccCard.INTENT_VALUE_ICC_LOCKED.equals(stateExtra)) { final String lockedReason = intent.getStringExtra(IccCard.INTENT_KEY_LOCKED_REASON); if (IccCard.INTENT_VALUE_LOCKED_ON_PIN.equals(lockedReason)) { mSimState = IccCard.State.PIN_REQUIRED; } else if (IccCard.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) { mSimState = IccCard.State.PUK_REQUIRED; } else { mSimState = IccCard.State.NETWORK_LOCKED; } } else { mSimState = IccCard.State.UNKNOWN; } } private boolean isCdma() { return (mSignalStrength != null) && !mSignalStrength.isGsm(); } private boolean hasService() { if (mServiceState != null) { switch (mServiceState.getState()) { case ServiceState.STATE_OUT_OF_SERVICE: case ServiceState.STATE_POWER_OFF: return false; default: return true; } } else { return false; } } private void updateAirplaneMode() { mAirplaneMode = (Settings.System.getInt(mContext.getContentResolver(), Settings.System.AIRPLANE_MODE_ON, 0) == 1); } private final void updateTelephonySignalStrength() { if (!hasService()) { if (CHATTY) Slog.d(TAG, "updateTelephonySignalStrength: !hasService()"); mPhoneSignalIconId = R.drawable.stat_sys_signal_null; mDataSignalIconId = R.drawable.stat_sys_signal_null; } else { if (mSignalStrength == null) { if (CHATTY) Slog.d(TAG, "updateTelephonySignalStrength: mSignalStrength == null"); mPhoneSignalIconId = R.drawable.stat_sys_signal_null; mDataSignalIconId = R.drawable.stat_sys_signal_null; mContentDescriptionPhoneSignal = mContext.getString( AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0]); } else { int iconLevel; int[] iconList; if (isCdma() && mAlwaysShowCdmaRssi) { mLastSignalLevel = iconLevel = mSignalStrength.getCdmaLevel(); if(DEBUG) Slog.d(TAG, "mAlwaysShowCdmaRssi=" + mAlwaysShowCdmaRssi + " set to cdmaLevel=" + mSignalStrength.getCdmaLevel() + " instead of level=" + mSignalStrength.getLevel()); } else { mLastSignalLevel = iconLevel = mSignalStrength.getLevel(); } if (isCdma()) { if (isCdmaEri()) { iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH_ROAMING[mInetCondition]; } else { iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH[mInetCondition]; } } else { // Though mPhone is a Manager, this call is not an IPC if (mPhone.isNetworkRoaming()) { iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH_ROAMING[mInetCondition]; } else { iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH[mInetCondition]; } } mPhoneSignalIconId = iconList[iconLevel]; mContentDescriptionPhoneSignal = mContext.getString( AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[iconLevel]); mDataSignalIconId = TelephonyIcons.DATA_SIGNAL_STRENGTH[mInetCondition][iconLevel]; } } } private final void updateDataNetType() { if (mIsWimaxEnabled && mWimaxConnected) { // wimax is a special 4g network not handled by telephony mDataIconList = TelephonyIcons.DATA_4G[mInetCondition]; mDataTypeIconId = R.drawable.stat_sys_data_connected_4g; mContentDescriptionDataType = mContext.getString( R.string.accessibility_data_connection_4g); } else { switch (mDataNetType) { case TelephonyManager.NETWORK_TYPE_UNKNOWN: if (!mShowAtLeastThreeGees) { mDataIconList = TelephonyIcons.DATA_G[mInetCondition]; mDataTypeIconId = 0; mContentDescriptionDataType = mContext.getString( R.string.accessibility_data_connection_gprs); break; } else { // fall through } case TelephonyManager.NETWORK_TYPE_EDGE: if (!mShowAtLeastThreeGees) { mDataIconList = TelephonyIcons.DATA_E[mInetCondition]; mDataTypeIconId = R.drawable.stat_sys_data_connected_e; mContentDescriptionDataType = mContext.getString( R.string.accessibility_data_connection_edge); break; } else { // fall through } case TelephonyManager.NETWORK_TYPE_UMTS: mDataIconList = TelephonyIcons.DATA_3G[mInetCondition]; mDataTypeIconId = R.drawable.stat_sys_data_connected_3g; mContentDescriptionDataType = mContext.getString( R.string.accessibility_data_connection_3g); break; case TelephonyManager.NETWORK_TYPE_HSDPA: case TelephonyManager.NETWORK_TYPE_HSUPA: case TelephonyManager.NETWORK_TYPE_HSPA: case TelephonyManager.NETWORK_TYPE_HSPAP: if (mHspaDataDistinguishable) { mDataIconList = TelephonyIcons.DATA_H[mInetCondition]; mDataTypeIconId = R.drawable.stat_sys_data_connected_h; mContentDescriptionDataType = mContext.getString( R.string.accessibility_data_connection_3_5g); } else { mDataIconList = TelephonyIcons.DATA_3G[mInetCondition]; mDataTypeIconId = R.drawable.stat_sys_data_connected_3g; mContentDescriptionDataType = mContext.getString( R.string.accessibility_data_connection_3g); } break; case TelephonyManager.NETWORK_TYPE_CDMA: // display 1xRTT for IS95A/B mDataIconList = TelephonyIcons.DATA_1X[mInetCondition]; mDataTypeIconId = R.drawable.stat_sys_data_connected_1x; mContentDescriptionDataType = mContext.getString( R.string.accessibility_data_connection_cdma); break; case TelephonyManager.NETWORK_TYPE_1xRTT: mDataIconList = TelephonyIcons.DATA_1X[mInetCondition]; mDataTypeIconId = R.drawable.stat_sys_data_connected_1x; mContentDescriptionDataType = mContext.getString( R.string.accessibility_data_connection_cdma); break; case TelephonyManager.NETWORK_TYPE_EVDO_0: //fall through case TelephonyManager.NETWORK_TYPE_EVDO_A: case TelephonyManager.NETWORK_TYPE_EVDO_B: case TelephonyManager.NETWORK_TYPE_EHRPD: mDataIconList = TelephonyIcons.DATA_3G[mInetCondition]; mDataTypeIconId = R.drawable.stat_sys_data_connected_3g; mContentDescriptionDataType = mContext.getString( R.string.accessibility_data_connection_3g); break; case TelephonyManager.NETWORK_TYPE_LTE: mDataIconList = TelephonyIcons.DATA_4G[mInetCondition]; mDataTypeIconId = R.drawable.stat_sys_data_connected_4g; mContentDescriptionDataType = mContext.getString( R.string.accessibility_data_connection_4g); break; default: if (!mShowAtLeastThreeGees) { mDataIconList = TelephonyIcons.DATA_G[mInetCondition]; mDataTypeIconId = R.drawable.stat_sys_data_connected_g; mContentDescriptionDataType = mContext.getString( R.string.accessibility_data_connection_gprs); } else { mDataIconList = TelephonyIcons.DATA_3G[mInetCondition]; mDataTypeIconId = R.drawable.stat_sys_data_connected_3g; mContentDescriptionDataType = mContext.getString( R.string.accessibility_data_connection_3g); } break; } } if ((isCdma() && isCdmaEri()) || mPhone.isNetworkRoaming()) { mDataTypeIconId = R.drawable.stat_sys_data_connected_roam; } } boolean isCdmaEri() { if (mServiceState != null) { final int iconIndex = mServiceState.getCdmaEriIconIndex(); if (iconIndex != EriInfo.ROAMING_INDICATOR_OFF) { final int iconMode = mServiceState.getCdmaEriIconMode(); if (iconMode == EriInfo.ROAMING_ICON_MODE_NORMAL || iconMode == EriInfo.ROAMING_ICON_MODE_FLASH) { return true; } } } return false; } private final void updateDataIcon() { int iconId; boolean visible = true; if (!isCdma()) { // GSM case, we have to check also the sim state if (mSimState == IccCard.State.READY || mSimState == IccCard.State.UNKNOWN) { if (hasService() && mDataState == TelephonyManager.DATA_CONNECTED) { switch (mDataActivity) { case TelephonyManager.DATA_ACTIVITY_IN: iconId = mDataIconList[1]; break; case TelephonyManager.DATA_ACTIVITY_OUT: iconId = mDataIconList[2]; break; case TelephonyManager.DATA_ACTIVITY_INOUT: iconId = mDataIconList[3]; break; default: iconId = mDataIconList[0]; break; } mDataDirectionIconId = iconId; } else { iconId = 0; visible = false; } } else { iconId = R.drawable.stat_sys_no_sim; visible = false; // no SIM? no data } } else { // CDMA case, mDataActivity can be also DATA_ACTIVITY_DORMANT if (hasService() && mDataState == TelephonyManager.DATA_CONNECTED) { switch (mDataActivity) { case TelephonyManager.DATA_ACTIVITY_IN: iconId = mDataIconList[1]; break; case TelephonyManager.DATA_ACTIVITY_OUT: iconId = mDataIconList[2]; break; case TelephonyManager.DATA_ACTIVITY_INOUT: iconId = mDataIconList[3]; break; case TelephonyManager.DATA_ACTIVITY_DORMANT: default: iconId = mDataIconList[0]; break; } } else { iconId = 0; visible = false; } } // yuck - this should NOT be done by the status bar long ident = Binder.clearCallingIdentity(); try { mBatteryStats.notePhoneDataConnectionState(mPhone.getNetworkType(), visible); } catch (RemoteException e) { } finally { Binder.restoreCallingIdentity(ident); } mDataDirectionIconId = iconId; mDataConnected = visible; } void updateNetworkName(boolean showSpn, String spn, boolean showPlmn, String plmn) { if (false) { Slog.d("CarrierLabel", "updateNetworkName showSpn=" + showSpn + " spn=" + spn + " showPlmn=" + showPlmn + " plmn=" + plmn); } StringBuilder str = new StringBuilder(); boolean something = false; if (showPlmn && plmn != null) { str.append(plmn); something = true; } if (showSpn && spn != null) { if (something) { str.append(mNetworkNameSeparator); } str.append(spn); something = true; } if (something) { mNetworkName = str.toString(); } else { mNetworkName = mNetworkNameDefault; } } // ===== 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 { Slog.e(TAG, "Failed to connect to wifi"); } break; case WifiManager.DATA_ACTIVITY_NOTIFICATION: if (msg.arg1 != mWifiActivity) { mWifiActivity = msg.arg1; refreshViews(); } break; default: //Ignore break; } } } private void updateWifiState(Intent intent) { final String action = intent.getAction(); if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) { mWifiEnabled = 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); boolean wasConnected = mWifiConnected; mWifiConnected = networkInfo != null && networkInfo.isConnected(); // If we just connected, grab the inintial signal strength and ssid if (mWifiConnected && !wasConnected) { // try getting it out of the intent first WifiInfo info = (WifiInfo) intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO); if (info == null) { info = mWifiManager.getConnectionInfo(); } if (info != null) { mWifiSsid = huntForSsid(info); } else { mWifiSsid = null; } } else if (!mWifiConnected) { mWifiSsid = null; } // Apparently the wifi level is not stable at this point even if we've just connected to // the network; we need to wait for an RSSI_CHANGED_ACTION for that. So let's just set // it to 0 for now mWifiLevel = 0; mWifiRssi = -200; } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) { if (mWifiConnected) { mWifiRssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200); mWifiLevel = WifiManager.calculateSignalLevel( mWifiRssi, WifiIcons.WIFI_LEVEL_COUNT); } } updateWifiIcons(); } private void updateWifiIcons() { if (mWifiConnected) { mWifiIconId = WifiIcons.WIFI_SIGNAL_STRENGTH[mInetCondition][mWifiLevel]; mContentDescriptionWifi = mContext.getString( AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH[mWifiLevel]); } else { if (mDataAndWifiStacked) { mWifiIconId = 0; } else { mWifiIconId = mWifiEnabled ? WifiIcons.WIFI_SIGNAL_STRENGTH[0][0] : 0; } mContentDescriptionWifi = mContext.getString(R.string.accessibility_no_wifi); } } private String huntForSsid(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(); for (WifiConfiguration net : networks) { if (net.networkId == info.getNetworkId()) { return net.SSID; } } return null; } // ===== Wimax =================================================================== private final void updateWimaxState(Intent intent) { final String action = intent.getAction(); boolean wasConnected = mWimaxConnected; if (action.equals(WimaxManagerConstants.NET_4G_STATE_CHANGED_ACTION)) { int wimaxStatus = intent.getIntExtra(WimaxManagerConstants.EXTRA_4G_STATE, WimaxManagerConstants.NET_4G_STATE_UNKNOWN); mIsWimaxEnabled = (wimaxStatus == WimaxManagerConstants.NET_4G_STATE_ENABLED); } else if (action.equals(WimaxManagerConstants.SIGNAL_LEVEL_CHANGED_ACTION)) { mWimaxSignal = intent.getIntExtra(WimaxManagerConstants.EXTRA_NEW_SIGNAL_LEVEL, 0); } else if (action.equals(WimaxManagerConstants.WIMAX_NETWORK_STATE_CHANGED_ACTION)) { mWimaxState = intent.getIntExtra(WimaxManagerConstants.EXTRA_WIMAX_STATE, WimaxManagerConstants.NET_4G_STATE_UNKNOWN); mWimaxExtraState = intent.getIntExtra( WimaxManagerConstants.EXTRA_WIMAX_STATE_DETAIL, WimaxManagerConstants.NET_4G_STATE_UNKNOWN); mWimaxConnected = (mWimaxState == WimaxManagerConstants.WIMAX_STATE_CONNECTED); mWimaxIdle = (mWimaxExtraState == WimaxManagerConstants.WIMAX_IDLE); } updateDataNetType(); updateWimaxIcons(); } private void updateWimaxIcons() { if (mIsWimaxEnabled) { if (mWimaxConnected) { if (mWimaxIdle) mWimaxIconId = WimaxIcons.WIMAX_IDLE; else mWimaxIconId = WimaxIcons.WIMAX_SIGNAL_STRENGTH[mInetCondition][mWimaxSignal]; mContentDescriptionWimax = mContext.getString( AccessibilityContentDescriptions.WIMAX_CONNECTION_STRENGTH[mWimaxSignal]); } else { mWimaxIconId = WimaxIcons.WIMAX_DISCONNECTED; mContentDescriptionWimax = mContext.getString(R.string.accessibility_no_wimax); } } else { mWimaxIconId = 0; } } // ===== Full or limited Internet connectivity ================================== private void updateConnectivity(Intent intent) { if (CHATTY) { Slog.d(TAG, "updateConnectivity: intent=" + intent); } NetworkInfo info = (NetworkInfo)(intent.getParcelableExtra( ConnectivityManager.EXTRA_NETWORK_INFO)); int connectionStatus = intent.getIntExtra(ConnectivityManager.EXTRA_INET_CONDITION, 0); if (CHATTY) { Slog.d(TAG, "updateConnectivity: networkInfo=" + info); Slog.d(TAG, "updateConnectivity: connectionStatus=" + connectionStatus); } mInetCondition = (connectionStatus > INET_CONDITION_THRESHOLD ? 1 : 0); if (info != null && info.getType() == ConnectivityManager.TYPE_BLUETOOTH) { mBluetoothTethered = info.isConnected(); } else { mBluetoothTethered = false; } // We want to update all the icons, all at once, for any condition change updateDataNetType(); updateWimaxIcons(); updateDataIcon(); updateTelephonySignalStrength(); updateWifiIcons(); } // ===== Update the views ======================================================= void refreshViews() { Context context = mContext; int combinedSignalIconId = 0; int combinedActivityIconId = 0; String combinedLabel = ""; String wifiLabel = ""; String mobileLabel = ""; int N; if (!mHasMobileDataFeature) { mDataSignalIconId = mPhoneSignalIconId = 0; mobileLabel = ""; } else { // 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 is connected, we show nothing. // Otherwise (nothing connected) we show "No internet connection". if (mDataConnected) { mobileLabel = mNetworkName; } else if (mWifiConnected) { if (hasService()) { mobileLabel = mNetworkName; } else { mobileLabel = ""; } } else { mobileLabel = context.getString(R.string.status_bar_settings_signal_meter_disconnected); } // Now for things that should only be shown when actually using mobile data. if (mDataConnected) { combinedSignalIconId = mDataSignalIconId; switch (mDataActivity) { case TelephonyManager.DATA_ACTIVITY_IN: mMobileActivityIconId = R.drawable.stat_sys_signal_in; break; case TelephonyManager.DATA_ACTIVITY_OUT: mMobileActivityIconId = R.drawable.stat_sys_signal_out; break; case TelephonyManager.DATA_ACTIVITY_INOUT: mMobileActivityIconId = R.drawable.stat_sys_signal_inout; break; default: mMobileActivityIconId = 0; break; } combinedLabel = mobileLabel; combinedActivityIconId = mMobileActivityIconId; combinedSignalIconId = mDataSignalIconId; // set by updateDataIcon() mContentDescriptionCombinedSignal = mContentDescriptionDataType; } } if (mWifiConnected) { if (mWifiSsid == null) { wifiLabel = context.getString(R.string.status_bar_settings_signal_meter_wifi_nossid); mWifiActivityIconId = 0; // no wifis, no bits } else { wifiLabel = mWifiSsid; if (DEBUG) { wifiLabel += "xxxxXXXXxxxxXXXX"; } switch (mWifiActivity) { case WifiManager.DATA_ACTIVITY_IN: mWifiActivityIconId = R.drawable.stat_sys_wifi_in; break; case WifiManager.DATA_ACTIVITY_OUT: mWifiActivityIconId = R.drawable.stat_sys_wifi_out; break; case WifiManager.DATA_ACTIVITY_INOUT: mWifiActivityIconId = R.drawable.stat_sys_wifi_inout; break; case WifiManager.DATA_ACTIVITY_NONE: mWifiActivityIconId = 0; break; } } combinedActivityIconId = mWifiActivityIconId; combinedLabel = wifiLabel; combinedSignalIconId = mWifiIconId; // set by updateWifiIcons() mContentDescriptionCombinedSignal = mContentDescriptionWifi; } else { if (mHasMobileDataFeature) { wifiLabel = ""; } else { wifiLabel = context.getString(R.string.status_bar_settings_signal_meter_disconnected); } } if (mBluetoothTethered) { combinedLabel = mContext.getString(R.string.bluetooth_tethered); combinedSignalIconId = mBluetoothTetherIconId; mContentDescriptionCombinedSignal = mContext.getString( R.string.accessibility_bluetooth_tether); } if (mAirplaneMode && (mServiceState == null || (!hasService() && !mServiceState.isEmergencyOnly()))) { // Only display the flight-mode icon if not in "emergency calls only" mode. // look again; your radios are now airplanes mContentDescriptionPhoneSignal = mContext.getString( R.string.accessibility_airplane_mode); mPhoneSignalIconId = mDataSignalIconId = R.drawable.stat_sys_signal_flightmode; mDataTypeIconId = 0; // combined values from connected wifi take precedence over airplane mode if (mWifiConnected) { // Suppress "No internet connection." from mobile if wifi connected. mobileLabel = ""; } else { if (mHasMobileDataFeature) { // let the mobile icon show "No internet connection." wifiLabel = ""; } else { wifiLabel = context.getString(R.string.status_bar_settings_signal_meter_disconnected); combinedLabel = wifiLabel; } mContentDescriptionCombinedSignal = mContentDescriptionPhoneSignal; combinedSignalIconId = mDataSignalIconId; } } else if (!mDataConnected && !mWifiConnected && !mBluetoothTethered && !mWimaxConnected) { // pretty much totally disconnected combinedLabel = context.getString(R.string.status_bar_settings_signal_meter_disconnected); // On devices without mobile radios, we want to show the wifi icon combinedSignalIconId = mHasMobileDataFeature ? mDataSignalIconId : mWifiIconId; mContentDescriptionCombinedSignal = mHasMobileDataFeature ? mContentDescriptionDataType : mContentDescriptionWifi; if ((isCdma() && isCdmaEri()) || mPhone.isNetworkRoaming()) { mDataTypeIconId = R.drawable.stat_sys_data_connected_roam; } else { mDataTypeIconId = 0; } } if (DEBUG) { Slog.d(TAG, "refreshViews connected={" + (mWifiConnected?" wifi":"") + (mDataConnected?" data":"") + " } level=" + ((mSignalStrength == null)?"??":Integer.toString(mSignalStrength.getLevel())) + " combinedSignalIconId=0x" + Integer.toHexString(combinedSignalIconId) + "/" + getResourceName(combinedSignalIconId) + " combinedActivityIconId=0x" + Integer.toHexString(combinedActivityIconId) + " mAirplaneMode=" + mAirplaneMode + " mDataActivity=" + mDataActivity + " mPhoneSignalIconId=0x" + Integer.toHexString(mPhoneSignalIconId) + " mDataDirectionIconId=0x" + Integer.toHexString(mDataDirectionIconId) + " mDataSignalIconId=0x" + Integer.toHexString(mDataSignalIconId) + " mDataTypeIconId=0x" + Integer.toHexString(mDataTypeIconId) + " mWifiIconId=0x" + Integer.toHexString(mWifiIconId) + " mBluetoothTetherIconId=0x" + Integer.toHexString(mBluetoothTetherIconId)); } if (mLastPhoneSignalIconId != mPhoneSignalIconId || mLastDataDirectionOverlayIconId != combinedActivityIconId || mLastWifiIconId != mWifiIconId || mLastWimaxIconId != mWimaxIconId || mLastDataTypeIconId != mDataTypeIconId) { // NB: the mLast*s will be updated later for (SignalCluster cluster : mSignalClusters) { refreshSignalCluster(cluster); } } // the phone icon on phones if (mLastPhoneSignalIconId != mPhoneSignalIconId) { mLastPhoneSignalIconId = mPhoneSignalIconId; N = mPhoneSignalIconViews.size(); for (int i=0; i<N; i++) { final ImageView v = mPhoneSignalIconViews.get(i); if (mPhoneSignalIconId == 0) { v.setVisibility(View.GONE); } else { v.setVisibility(View.VISIBLE); v.setImageResource(mPhoneSignalIconId); v.setContentDescription(mContentDescriptionPhoneSignal); } } } // the data icon on phones if (mLastDataDirectionIconId != mDataDirectionIconId) { mLastDataDirectionIconId = mDataDirectionIconId; N = mDataDirectionIconViews.size(); for (int i=0; i<N; i++) { final ImageView v = mDataDirectionIconViews.get(i); v.setImageResource(mDataDirectionIconId); v.setContentDescription(mContentDescriptionDataType); } } // the wifi icon on phones if (mLastWifiIconId != mWifiIconId) { mLastWifiIconId = mWifiIconId; N = mWifiIconViews.size(); for (int i=0; i<N; i++) { final ImageView v = mWifiIconViews.get(i); if (mWifiIconId == 0) { v.setVisibility(View.GONE); } else { v.setVisibility(View.VISIBLE); v.setImageResource(mWifiIconId); v.setContentDescription(mContentDescriptionWifi); } } } // the wimax icon on phones if (mLastWimaxIconId != mWimaxIconId) { mLastWimaxIconId = mWimaxIconId; N = mWimaxIconViews.size(); for (int i=0; i<N; i++) { final ImageView v = mWimaxIconViews.get(i); if (mWimaxIconId == 0) { v.setVisibility(View.GONE); } else { v.setVisibility(View.VISIBLE); v.setImageResource(mWimaxIconId); v.setContentDescription(mContentDescriptionWimax); } } } // the combined data signal icon if (mLastCombinedSignalIconId != combinedSignalIconId) { mLastCombinedSignalIconId = combinedSignalIconId; N = mCombinedSignalIconViews.size(); for (int i=0; i<N; i++) { final ImageView v = mCombinedSignalIconViews.get(i); v.setImageResource(combinedSignalIconId); v.setContentDescription(mContentDescriptionCombinedSignal); } } // the data network type overlay if (mLastDataTypeIconId != mDataTypeIconId) { mLastDataTypeIconId = mDataTypeIconId; N = mDataTypeIconViews.size(); for (int i=0; i<N; i++) { final ImageView v = mDataTypeIconViews.get(i); if (mDataTypeIconId == 0) { v.setVisibility(View.GONE); } else { v.setVisibility(View.VISIBLE); v.setImageResource(mDataTypeIconId); v.setContentDescription(mContentDescriptionDataType); } } } // the data direction overlay if (mLastDataDirectionOverlayIconId != combinedActivityIconId) { if (DEBUG) { Slog.d(TAG, "changing data overlay icon id to " + combinedActivityIconId); } mLastDataDirectionOverlayIconId = combinedActivityIconId; N = mDataDirectionOverlayIconViews.size(); for (int i=0; i<N; i++) { final ImageView v = mDataDirectionOverlayIconViews.get(i); if (combinedActivityIconId == 0) { v.setVisibility(View.GONE); } else { v.setVisibility(View.VISIBLE); v.setImageResource(combinedActivityIconId); v.setContentDescription(mContentDescriptionDataType); } } } // the combinedLabel in the notification panel if (!mLastCombinedLabel.equals(combinedLabel)) { mLastCombinedLabel = combinedLabel; N = mCombinedLabelViews.size(); for (int i=0; i<N; i++) { TextView v = mCombinedLabelViews.get(i); v.setText(combinedLabel); } } // wifi label N = mWifiLabelViews.size(); for (int i=0; i<N; i++) { TextView v = mWifiLabelViews.get(i); if ("".equals(wifiLabel)) { v.setVisibility(View.GONE); } else { v.setVisibility(View.VISIBLE); v.setText(wifiLabel); } } // mobile label N = mMobileLabelViews.size(); for (int i=0; i<N; i++) { TextView v = mMobileLabelViews.get(i); if ("".equals(mobileLabel)) { v.setVisibility(View.GONE); } else { v.setVisibility(View.VISIBLE); v.setText(mobileLabel); } } } public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("NetworkController state:"); pw.println(" - telephony ------"); pw.print(" hasService()="); pw.println(hasService()); pw.print(" mHspaDataDistinguishable="); pw.println(mHspaDataDistinguishable); pw.print(" mDataConnected="); pw.println(mDataConnected); pw.print(" mSimState="); pw.println(mSimState); pw.print(" mPhoneState="); pw.println(mPhoneState); pw.print(" mDataState="); pw.println(mDataState); pw.print(" mDataActivity="); pw.println(mDataActivity); pw.print(" mDataNetType="); pw.print(mDataNetType); pw.print("/"); pw.println(TelephonyManager.getNetworkTypeName(mDataNetType)); pw.print(" mServiceState="); pw.println(mServiceState); pw.print(" mSignalStrength="); pw.println(mSignalStrength); pw.print(" mLastSignalLevel="); pw.println(mLastSignalLevel); pw.print(" mNetworkName="); pw.println(mNetworkName); pw.print(" mNetworkNameDefault="); pw.println(mNetworkNameDefault); pw.print(" mNetworkNameSeparator="); pw.println(mNetworkNameSeparator.replace("\n","\\n")); pw.print(" mPhoneSignalIconId=0x"); pw.print(Integer.toHexString(mPhoneSignalIconId)); pw.print("/"); pw.println(getResourceName(mPhoneSignalIconId)); pw.print(" mDataDirectionIconId="); pw.print(Integer.toHexString(mDataDirectionIconId)); pw.print("/"); pw.println(getResourceName(mDataDirectionIconId)); pw.print(" mDataSignalIconId="); pw.print(Integer.toHexString(mDataSignalIconId)); pw.print("/"); pw.println(getResourceName(mDataSignalIconId)); pw.print(" mDataTypeIconId="); pw.print(Integer.toHexString(mDataTypeIconId)); pw.print("/"); pw.println(getResourceName(mDataTypeIconId)); pw.println(" - wifi ------"); pw.print(" mWifiEnabled="); pw.println(mWifiEnabled); pw.print(" mWifiConnected="); pw.println(mWifiConnected); pw.print(" mWifiRssi="); pw.println(mWifiRssi); pw.print(" mWifiLevel="); pw.println(mWifiLevel); pw.print(" mWifiSsid="); pw.println(mWifiSsid); pw.println(String.format(" mWifiIconId=0x%08x/%s", mWifiIconId, getResourceName(mWifiIconId))); pw.print(" mWifiActivity="); pw.println(mWifiActivity); if (mWimaxSupported) { pw.println(" - wimax ------"); pw.print(" mIsWimaxEnabled="); pw.println(mIsWimaxEnabled); pw.print(" mWimaxConnected="); pw.println(mWimaxConnected); pw.print(" mWimaxIdle="); pw.println(mWimaxIdle); pw.println(String.format(" mWimaxIconId=0x%08x/%s", mWimaxIconId, getResourceName(mWimaxIconId))); pw.println(String.format(" mWimaxSignal=%d", mWimaxSignal)); pw.println(String.format(" mWimaxState=%d", mWimaxState)); pw.println(String.format(" mWimaxExtraState=%d", mWimaxExtraState)); } pw.println(" - Bluetooth ----"); pw.print(" mBtReverseTethered="); pw.println(mBluetoothTethered); pw.println(" - connectivity ------"); pw.print(" mInetCondition="); pw.println(mInetCondition); pw.println(" - icons ------"); pw.print(" mLastPhoneSignalIconId=0x"); pw.print(Integer.toHexString(mLastPhoneSignalIconId)); pw.print("/"); pw.println(getResourceName(mLastPhoneSignalIconId)); pw.print(" mLastDataDirectionIconId=0x"); pw.print(Integer.toHexString(mLastDataDirectionIconId)); pw.print("/"); pw.println(getResourceName(mLastDataDirectionIconId)); pw.print(" mLastDataDirectionOverlayIconId=0x"); pw.print(Integer.toHexString(mLastDataDirectionOverlayIconId)); pw.print("/"); pw.println(getResourceName(mLastDataDirectionOverlayIconId)); pw.print(" mLastWifiIconId=0x"); pw.print(Integer.toHexString(mLastWifiIconId)); pw.print("/"); pw.println(getResourceName(mLastWifiIconId)); pw.print(" mLastCombinedSignalIconId=0x"); pw.print(Integer.toHexString(mLastCombinedSignalIconId)); pw.print("/"); pw.println(getResourceName(mLastCombinedSignalIconId)); pw.print(" mLastDataTypeIconId=0x"); pw.print(Integer.toHexString(mLastDataTypeIconId)); pw.print("/"); pw.println(getResourceName(mLastDataTypeIconId)); pw.print(" mLastCombinedLabel="); pw.print(mLastCombinedLabel); pw.println(""); } private String getResourceName(int resId) { if (resId != 0) { final Resources res = mContext.getResources(); try { return res.getResourceName(resId); } catch (android.content.res.Resources.NotFoundException ex) { return "(unknown)"; } } else { return "(null)"; } } }