/* * 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 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.provider.Settings; 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.IccCardConstants; import com.android.internal.telephony.TelephonyIntents; import com.android.internal.telephony.cdma.EriInfo; import com.android.internal.util.AsyncChannel; import com.android.server.am.BatteryStatsService; import com.android.systemui.R; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; 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; IccCardConstants.State mSimState = IccCardConstants.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 mQSPhoneSignalIconId; int mDataDirectionIconId; // data + data direction on phones int mDataSignalIconId; int mDataTypeIconId; int mQSDataTypeIconId; int mAirplaneIconId; 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 mQSWifiIconId = 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 boolean mConnected = false; private int mConnectedNetworkType = ConnectivityManager.TYPE_NONE; private String mConnectedNetworkTypeName; private int mInetCondition = 0; private static final int INET_CONDITION_THRESHOLD = 50; private boolean mAirplaneMode = false; private boolean mLastAirplaneMode = true; // our ui Context mContext; ArrayList mPhoneSignalIconViews = new ArrayList(); ArrayList mDataDirectionIconViews = new ArrayList(); ArrayList mDataDirectionOverlayIconViews = new ArrayList(); ArrayList mWifiIconViews = new ArrayList(); ArrayList mWimaxIconViews = new ArrayList(); ArrayList mCombinedSignalIconViews = new ArrayList(); ArrayList mDataTypeIconViews = new ArrayList(); ArrayList mCombinedLabelViews = new ArrayList(); ArrayList mMobileLabelViews = new ArrayList(); ArrayList mWifiLabelViews = new ArrayList(); ArrayList mEmergencyLabelViews = new ArrayList(); ArrayList mSignalClusters = new ArrayList(); ArrayList mSignalsChangedCallbacks = new ArrayList(); 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, int airplaneIcon); } public interface NetworkSignalChangedCallback { void onWifiSignalChanged(boolean enabled, int wifiSignalIconId, String wifitSignalContentDescriptionId, String description); void onMobileDataSignalChanged(boolean enabled, int mobileSignalIconId, String mobileSignalContentDescriptionId, int dataTypeIconId, String dataTypeContentDescriptionId, String description); void onAirplaneModeChanged(boolean enabled); } /** * 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.getWifiServiceMessenger(); 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(TelephonyIntents.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 boolean hasMobileDataFeature() { return mHasMobileDataFeature; } public boolean isEmergencyOnly() { return (mServiceState != null && mServiceState.isEmergencyOnly()); } 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 addEmergencyLabelView(TextView v) { mEmergencyLabelViews.add(v); } public void addSignalCluster(SignalCluster cluster) { mSignalClusters.add(cluster); refreshSignalCluster(cluster); } public void addNetworkSignalChangedCallback(NetworkSignalChangedCallback cb) { mSignalsChangedCallbacks.add(cb); notifySignalsChangedCallbacks(cb); } public void refreshSignalCluster(SignalCluster cluster) { cluster.setWifiIndicators( // only show wifi in the cluster if connected or if wifi-only mWifiEnabled && (mWifiConnected || !mHasMobileDataFeature), 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, mAirplaneIconId); } void notifySignalsChangedCallbacks(NetworkSignalChangedCallback cb) { // only show wifi in the cluster if connected or if wifi-only boolean wifiEnabled = mWifiEnabled && (mWifiConnected || !mHasMobileDataFeature); String wifiDesc = wifiEnabled ? mWifiSsid : null; cb.onWifiSignalChanged(wifiEnabled, mQSWifiIconId, mContentDescriptionWifi, wifiDesc); if (isEmergencyOnly()) { cb.onMobileDataSignalChanged(false, mQSPhoneSignalIconId, mContentDescriptionPhoneSignal, mQSDataTypeIconId, mContentDescriptionDataType, null); } else { if (mIsWimaxEnabled && mWimaxConnected) { // Wimax is special cb.onMobileDataSignalChanged(true, mQSPhoneSignalIconId, mContentDescriptionPhoneSignal, mQSDataTypeIconId, mContentDescriptionDataType, mNetworkName); } else { // Normal mobile data cb.onMobileDataSignalChanged(mHasMobileDataFeature, mQSPhoneSignalIconId, mContentDescriptionPhoneSignal, mQSDataTypeIconId, mContentDescriptionDataType, mNetworkName); } } cb.onAirplaneModeChanged(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(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)); 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(IccCardConstants.INTENT_KEY_ICC_STATE); if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) { mSimState = IccCardConstants.State.ABSENT; } else if (IccCardConstants.INTENT_VALUE_ICC_READY.equals(stateExtra)) { mSimState = IccCardConstants.State.READY; } else if (IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(stateExtra)) { final String lockedReason = intent.getStringExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON); if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PIN.equals(lockedReason)) { mSimState = IccCardConstants.State.PIN_REQUIRED; } else if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) { mSimState = IccCardConstants.State.PUK_REQUIRED; } else { mSimState = IccCardConstants.State.NETWORK_LOCKED; } } else { mSimState = IccCardConstants.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.Global.getInt(mContext.getContentResolver(), Settings.Global.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; mQSPhoneSignalIconId = R.drawable.ic_qs_signal_no_signal; 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; mQSPhoneSignalIconId = R.drawable.ic_qs_signal_no_signal; 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]; mQSPhoneSignalIconId = TelephonyIcons.QS_TELEPHONY_SIGNAL_STRENGTH[mInetCondition][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; mQSDataTypeIconId = R.drawable.ic_qs_signal_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; mQSDataTypeIconId = 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; mQSDataTypeIconId = R.drawable.ic_qs_signal_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; mQSDataTypeIconId = R.drawable.ic_qs_signal_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; mQSDataTypeIconId = R.drawable.ic_qs_signal_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; mQSDataTypeIconId = R.drawable.ic_qs_signal_3g; mContentDescriptionDataType = mContext.getString( R.string.accessibility_data_connection_3g); } break; case TelephonyManager.NETWORK_TYPE_CDMA: if (!mShowAtLeastThreeGees) { // display 1xRTT for IS95A/B mDataIconList = TelephonyIcons.DATA_1X[mInetCondition]; mDataTypeIconId = R.drawable.stat_sys_data_connected_1x; mQSDataTypeIconId = R.drawable.ic_qs_signal_1x; mContentDescriptionDataType = mContext.getString( R.string.accessibility_data_connection_cdma); break; } else { // fall through } case TelephonyManager.NETWORK_TYPE_1xRTT: if (!mShowAtLeastThreeGees) { mDataIconList = TelephonyIcons.DATA_1X[mInetCondition]; mDataTypeIconId = R.drawable.stat_sys_data_connected_1x; mQSDataTypeIconId = R.drawable.ic_qs_signal_1x; mContentDescriptionDataType = mContext.getString( R.string.accessibility_data_connection_cdma); break; } else { // fall through } 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; mQSDataTypeIconId = R.drawable.ic_qs_signal_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; mQSDataTypeIconId = R.drawable.ic_qs_signal_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; mQSDataTypeIconId = R.drawable.ic_qs_signal_g; mContentDescriptionDataType = mContext.getString( R.string.accessibility_data_connection_gprs); } else { mDataIconList = TelephonyIcons.DATA_3G[mInetCondition]; mDataTypeIconId = R.drawable.stat_sys_data_connected_3g; mQSDataTypeIconId = R.drawable.ic_qs_signal_3g; mContentDescriptionDataType = mContext.getString( R.string.accessibility_data_connection_3g); } break; } } if (isCdma()) { if (isCdmaEri()) { mDataTypeIconId = R.drawable.stat_sys_data_connected_roam; mQSDataTypeIconId = R.drawable.ic_qs_signal_r; } } else if (mPhone.isNetworkRoaming()) { mDataTypeIconId = R.drawable.stat_sys_data_connected_roam; mQSDataTypeIconId = R.drawable.ic_qs_signal_r; } } 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 == IccCardConstants.State.READY || mSimState == IccCardConstants.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; } } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) { 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]; mQSWifiIconId = WifiIcons.QS_WIFI_SIGNAL_STRENGTH[mInetCondition][mWifiLevel]; mContentDescriptionWifi = mContext.getString( AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH[mWifiLevel]); } else { if (mDataAndWifiStacked) { mWifiIconId = 0; mQSWifiIconId = 0; } else { mWifiIconId = mWifiEnabled ? R.drawable.stat_sys_wifi_signal_null : 0; mQSWifiIconId = mWifiEnabled ? R.drawable.ic_qs_wifi_no_network : 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 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); } final ConnectivityManager connManager = (ConnectivityManager) mContext .getSystemService(Context.CONNECTIVITY_SERVICE); final NetworkInfo info = connManager.getActiveNetworkInfo(); // Are we connected at all, by any interface? mConnected = info != null && info.isConnected(); if (mConnected) { mConnectedNetworkType = info.getType(); mConnectedNetworkTypeName = info.getTypeName(); } else { mConnectedNetworkType = ConnectivityManager.TYPE_NONE; mConnectedNetworkTypeName = null; } 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; final boolean emergencyOnly = isEmergencyOnly(); if (!mHasMobileDataFeature) { mDataSignalIconId = mPhoneSignalIconId = 0; mQSPhoneSignalIconId = 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 (or ethernet/bluetooth) // is connected, we show nothing. // Otherwise (nothing connected) we show "No internet connection". if (mDataConnected) { mobileLabel = mNetworkName; } else if (mConnected || emergencyOnly) { if (hasService() || emergencyOnly) { // The isEmergencyOnly test covers the case of a phone with no SIM mobileLabel = mNetworkName; } else { // Tablets, basically 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; } else { mMobileActivityIconId = 0; } } 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); } final boolean ethernetConnected = (mConnectedNetworkType == ConnectivityManager.TYPE_ETHERNET); if (ethernetConnected) { combinedLabel = context.getString(R.string.ethernet_label); } 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); mAirplaneIconId = R.drawable.stat_sys_signal_flightmode; mPhoneSignalIconId = mDataSignalIconId = mDataTypeIconId = mQSDataTypeIconId = 0; mQSPhoneSignalIconId = 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 && !ethernetConnected) { // 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; mDataTypeIconId = 0; mQSDataTypeIconId = 0; if (isCdma()) { if (isCdmaEri()) { mDataTypeIconId = R.drawable.stat_sys_data_connected_roam; mQSDataTypeIconId = R.drawable.ic_qs_signal_r; } } else if (mPhone.isNetworkRoaming()) { mDataTypeIconId = R.drawable.stat_sys_data_connected_roam; mQSDataTypeIconId = R.drawable.ic_qs_signal_r; } } 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) + " mobileLabel=" + mobileLabel + " wifiLabel=" + wifiLabel + " emergencyOnly=" + emergencyOnly + " combinedLabel=" + combinedLabel + " mAirplaneMode=" + mAirplaneMode + " mDataActivity=" + mDataActivity + " mPhoneSignalIconId=0x" + Integer.toHexString(mPhoneSignalIconId) + " mQSPhoneSignalIconId=0x" + Integer.toHexString(mQSPhoneSignalIconId) + " mDataDirectionIconId=0x" + Integer.toHexString(mDataDirectionIconId) + " mDataSignalIconId=0x" + Integer.toHexString(mDataSignalIconId) + " mDataTypeIconId=0x" + Integer.toHexString(mDataTypeIconId) + " mQSDataTypeIconId=0x" + Integer.toHexString(mQSDataTypeIconId) + " mWifiIconId=0x" + Integer.toHexString(mWifiIconId) + " mQSWifiIconId=0x" + Integer.toHexString(mQSWifiIconId) + " mBluetoothTetherIconId=0x" + Integer.toHexString(mBluetoothTetherIconId)); } if (mLastPhoneSignalIconId != mPhoneSignalIconId || mLastDataDirectionOverlayIconId != combinedActivityIconId || mLastWifiIconId != mWifiIconId || mLastWimaxIconId != mWimaxIconId || mLastDataTypeIconId != mDataTypeIconId || mLastAirplaneMode != mAirplaneMode) { // NB: the mLast*s will be updated later for (SignalCluster cluster : mSignalClusters) { refreshSignalCluster(cluster); } for (NetworkSignalChangedCallback cb : mSignalsChangedCallbacks) { notifySignalsChangedCallbacks(cb); } } if (mLastAirplaneMode != mAirplaneMode) { mLastAirplaneMode = mAirplaneMode; } // the phone icon on phones if (mLastPhoneSignalIconId != mPhoneSignalIconId) { mLastPhoneSignalIconId = mPhoneSignalIconId; N = mPhoneSignalIconViews.size(); for (int i=0; i