NetworkController.java revision acab04dfc16f0ad08ecd167ae759c158cea2e19f
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.systemui.statusbar.policy;
18
19import java.io.FileDescriptor;
20import java.io.PrintWriter;
21import java.util.ArrayList;
22import java.util.List;
23
24import android.content.BroadcastReceiver;
25import android.content.Context;
26import android.content.Intent;
27import android.content.IntentFilter;
28import android.content.res.Resources;
29import android.net.ConnectivityManager;
30import android.net.NetworkInfo;
31import android.net.wifi.SupplicantState;
32import android.net.wifi.WifiConfiguration;
33import android.net.wifi.WifiInfo;
34import android.net.wifi.WifiManager;
35import android.os.Binder;
36import android.os.Handler;
37import android.os.HandlerThread;
38import android.os.Looper;
39import android.os.Message;
40import android.os.Messenger;
41import android.os.RemoteException;
42import android.os.SystemProperties;
43import android.provider.Settings;
44import android.provider.Telephony;
45import android.telephony.PhoneStateListener;
46import android.telephony.ServiceState;
47import android.telephony.SignalStrength;
48import android.telephony.TelephonyManager;
49import android.util.Slog;
50import android.view.View;
51import android.widget.ImageView;
52import android.widget.TextView;
53
54import com.android.internal.app.IBatteryStats;
55import com.android.internal.telephony.IccCard;
56import com.android.internal.telephony.TelephonyIntents;
57import com.android.internal.telephony.cdma.EriInfo;
58import com.android.server.am.BatteryStatsService;
59import com.android.internal.util.AsyncChannel;
60
61import com.android.systemui.R;
62
63public class NetworkController extends BroadcastReceiver {
64    // debug
65    static final String TAG = "StatusBar.NetworkController";
66    static final boolean DEBUG = false;
67
68    // telephony
69    boolean mHspaDataDistinguishable;
70    final TelephonyManager mPhone;
71    boolean mDataConnected;
72    IccCard.State mSimState = IccCard.State.READY;
73    int mPhoneState = TelephonyManager.CALL_STATE_IDLE;
74    int mDataNetType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
75    int mDataState = TelephonyManager.DATA_DISCONNECTED;
76    int mDataActivity = TelephonyManager.DATA_ACTIVITY_NONE;
77    ServiceState mServiceState;
78    SignalStrength mSignalStrength;
79    int[] mDataIconList = TelephonyIcons.DATA_G[0];
80    String mNetworkName;
81    String mNetworkNameDefault;
82    String mNetworkNameSeparator;
83    int mPhoneSignalIconId;
84    int mDataDirectionIconId;
85    int mDataDirectionOverlayIconId;
86    int mDataSignalIconId;
87    int mDataTypeIconId;
88    boolean mDataActive;
89
90    // wifi
91    final WifiManager mWifiManager;
92    AsyncChannel mWifiChannel;
93    boolean mWifiEnabled, mWifiConnected;
94    int mWifiLevel;
95    String mWifiSsid;
96    int mWifiIconId = 0;
97    int mWifiActivity = WifiManager.DATA_ACTIVITY_NONE;
98
99    // bluetooth
100    private boolean mBluetoothTethered = false;
101    private int mBluetoothTetherIconId =
102        com.android.internal.R.drawable.stat_sys_tether_bluetooth;
103
104    // data connectivity (regardless of state, can we access the internet?)
105    // state of inet connection - 0 not connected, 100 connected
106    private int mInetCondition = 0;
107    private static final int INET_CONDITION_THRESHOLD = 50;
108
109    private boolean mAirplaneMode = false;
110
111    // our ui
112    Context mContext;
113    ArrayList<ImageView> mPhoneSignalIconViews = new ArrayList<ImageView>();
114    ArrayList<ImageView> mDataDirectionIconViews = new ArrayList<ImageView>();
115    ArrayList<ImageView> mDataDirectionOverlayIconViews = new ArrayList<ImageView>();
116    ArrayList<ImageView> mWifiIconViews = new ArrayList<ImageView>();
117    ArrayList<ImageView> mCombinedSignalIconViews = new ArrayList<ImageView>();
118    ArrayList<ImageView> mDataTypeIconViews = new ArrayList<ImageView>();
119    ArrayList<TextView> mLabelViews = new ArrayList<TextView>();
120    int mLastPhoneSignalIconId = -1;
121    int mLastDataDirectionIconId = -1;
122    int mLastDataDirectionOverlayIconId = -1;
123    int mLastWifiIconId = -1;
124    int mLastCombinedSignalIconId = -1;
125    int mLastDataTypeIconId = -1;
126    String mLastLabel = "";
127
128    // yuck -- stop doing this here and put it in the framework
129    IBatteryStats mBatteryStats;
130
131    /**
132     * Construct this controller object and register for updates.
133     */
134    public NetworkController(Context context) {
135        mContext = context;
136
137        // set up the default wifi icon, used when no radios have ever appeared
138        updateWifiIcons();
139
140        // telephony
141        mPhone = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
142        mPhone.listen(mPhoneStateListener,
143                          PhoneStateListener.LISTEN_SERVICE_STATE
144                        | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS
145                        | PhoneStateListener.LISTEN_CALL_STATE
146                        | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
147                        | PhoneStateListener.LISTEN_DATA_ACTIVITY);
148        mHspaDataDistinguishable = mContext.getResources().getBoolean(
149                R.bool.config_hspa_data_distinguishable);
150        mNetworkNameSeparator = mContext.getString(R.string.status_bar_network_name_separator);
151        mNetworkNameDefault = mContext.getString(
152                com.android.internal.R.string.lockscreen_carrier_default);
153        mNetworkName = mNetworkNameDefault;
154
155        // wifi
156        mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
157        Handler handler = new WifiHandler();
158        mWifiChannel = new AsyncChannel();
159        Messenger wifiMessenger = mWifiManager.getMessenger();
160        if (wifiMessenger != null) {
161            mWifiChannel.connect(mContext, handler, wifiMessenger);
162        }
163
164        // broadcasts
165        IntentFilter filter = new IntentFilter();
166        filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
167        filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
168        filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
169        filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
170        filter.addAction(Telephony.Intents.SPN_STRINGS_UPDATED_ACTION);
171        filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
172        filter.addAction(ConnectivityManager.INET_CONDITION_ACTION);
173        filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
174        filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
175        context.registerReceiver(this, filter);
176
177        // AIRPLANE_MODE_CHANGED is sent at boot; we've probably already missed it
178        updateAirplaneMode();
179
180        // yuck
181        mBatteryStats = BatteryStatsService.getService();
182    }
183
184    public void addPhoneSignalIconView(ImageView v) {
185        mPhoneSignalIconViews.add(v);
186    }
187
188    public void addDataDirectionIconView(ImageView v) {
189        mDataDirectionIconViews.add(v);
190    }
191
192    public void addDataDirectionOverlayIconView(ImageView v) {
193        mDataDirectionOverlayIconViews.add(v);
194    }
195
196    public void addWifiIconView(ImageView v) {
197        mWifiIconViews.add(v);
198    }
199
200    public void addCombinedSignalIconView(ImageView v) {
201        mCombinedSignalIconViews.add(v);
202    }
203
204    public void addDataTypeIconView(ImageView v) {
205        mDataTypeIconViews.add(v);
206    }
207
208    public void addLabelView(TextView v) {
209        mLabelViews.add(v);
210    }
211
212    @Override
213    public void onReceive(Context context, Intent intent) {
214        final String action = intent.getAction();
215        if (action.equals(WifiManager.RSSI_CHANGED_ACTION)
216                || action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)
217                || action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
218            updateWifiState(intent);
219            refreshViews();
220        } else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
221            updateSimState(intent);
222            updateDataIcon();
223            refreshViews();
224        } else if (action.equals(Telephony.Intents.SPN_STRINGS_UPDATED_ACTION)) {
225            updateNetworkName(intent.getBooleanExtra(Telephony.Intents.EXTRA_SHOW_SPN, false),
226                        intent.getStringExtra(Telephony.Intents.EXTRA_SPN),
227                        intent.getBooleanExtra(Telephony.Intents.EXTRA_SHOW_PLMN, false),
228                        intent.getStringExtra(Telephony.Intents.EXTRA_PLMN));
229            refreshViews();
230        } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION) ||
231                 action.equals(ConnectivityManager.INET_CONDITION_ACTION)) {
232            updateConnectivity(intent);
233            refreshViews();
234        } else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
235            refreshViews();
236        } else if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
237            updateAirplaneMode();
238            refreshViews();
239        }
240    }
241
242
243    // ===== Telephony ==============================================================
244
245    PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
246        @Override
247        public void onSignalStrengthsChanged(SignalStrength signalStrength) {
248            if (DEBUG) {
249                Slog.d(TAG, "onSignalStrengthsChanged signalStrength=" + signalStrength);
250            }
251            mSignalStrength = signalStrength;
252            updateTelephonySignalStrength();
253            refreshViews();
254        }
255
256        @Override
257        public void onServiceStateChanged(ServiceState state) {
258            if (DEBUG) {
259                Slog.d(TAG, "onServiceStateChanged state=" + state.getState());
260            }
261            mServiceState = state;
262            updateTelephonySignalStrength();
263            updateDataIcon();
264            refreshViews();
265        }
266
267        @Override
268        public void onCallStateChanged(int state, String incomingNumber) {
269            if (DEBUG) {
270                Slog.d(TAG, "onCallStateChanged state=" + state);
271            }
272            // In cdma, if a voice call is made, RSSI should switch to 1x.
273            if (isCdma()) {
274                updateTelephonySignalStrength();
275                refreshViews();
276            }
277        }
278
279        @Override
280        public void onDataConnectionStateChanged(int state, int networkType) {
281            if (DEBUG) {
282                Slog.d(TAG, "onDataConnectionStateChanged: state=" + state
283                        + " type=" + networkType);
284            }
285            mDataState = state;
286            mDataNetType = networkType;
287            if (state < 0) {
288                // device without a data connection
289                mSignalStrength = null;
290            }
291            updateDataNetType();
292            updateDataIcon();
293            refreshViews();
294        }
295
296        @Override
297        public void onDataActivity(int direction) {
298            if (DEBUG) {
299                Slog.d(TAG, "onDataActivity: direction=" + direction);
300            }
301            mDataActivity = direction;
302            updateDataIcon();
303            refreshViews();
304        }
305    };
306
307    private final void updateSimState(Intent intent) {
308        String stateExtra = intent.getStringExtra(IccCard.INTENT_KEY_ICC_STATE);
309        if (IccCard.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) {
310            mSimState = IccCard.State.ABSENT;
311        }
312        else if (IccCard.INTENT_VALUE_ICC_READY.equals(stateExtra)) {
313            mSimState = IccCard.State.READY;
314        }
315        else if (IccCard.INTENT_VALUE_ICC_LOCKED.equals(stateExtra)) {
316            final String lockedReason = intent.getStringExtra(IccCard.INTENT_KEY_LOCKED_REASON);
317            if (IccCard.INTENT_VALUE_LOCKED_ON_PIN.equals(lockedReason)) {
318                mSimState = IccCard.State.PIN_REQUIRED;
319            }
320            else if (IccCard.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) {
321                mSimState = IccCard.State.PUK_REQUIRED;
322            }
323            else {
324                mSimState = IccCard.State.NETWORK_LOCKED;
325            }
326        } else {
327            mSimState = IccCard.State.UNKNOWN;
328        }
329    }
330
331    private boolean isCdma() {
332        return (mSignalStrength != null) && !mSignalStrength.isGsm();
333    }
334
335    private boolean hasService() {
336        if (mServiceState != null) {
337            switch (mServiceState.getState()) {
338                case ServiceState.STATE_OUT_OF_SERVICE:
339                case ServiceState.STATE_POWER_OFF:
340                    return false;
341                default:
342                    return true;
343            }
344        } else {
345            return false;
346        }
347    }
348
349    private boolean hasMobileDataFeature() {
350        // XXX: HAX: replace when a more reliable method is available
351        return (! "wifi-only".equals(SystemProperties.get("ro.carrier")));
352    }
353
354
355    private void updateAirplaneMode() {
356        mAirplaneMode = (Settings.System.getInt(mContext.getContentResolver(),
357            Settings.System.AIRPLANE_MODE_ON, 0) == 1);
358    }
359
360    private final void updateTelephonySignalStrength() {
361        if (!hasService()) {
362            //Slog.d(TAG, "updateTelephonySignalStrength: no service");
363            mPhoneSignalIconId = R.drawable.stat_sys_signal_null;
364            mDataSignalIconId = R.drawable.stat_sys_signal_0; // note we use 0 instead of null
365        } else {
366            if (mSignalStrength == null) {
367                mPhoneSignalIconId = R.drawable.stat_sys_signal_null;
368                mDataSignalIconId = R.drawable.stat_sys_signal_0; // note we use 0 instead of null
369            } else {
370                int iconLevel;
371                int[] iconList;
372                iconLevel = mSignalStrength.getLevel();
373                if (isCdma()) {
374                    if (isCdmaEri()) {
375                        iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH_ROAMING[mInetCondition];
376                    } else {
377                        iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH[mInetCondition];
378                    }
379                } else {
380                    // Though mPhone is a Manager, this call is not an IPC
381                    if (mPhone.isNetworkRoaming()) {
382                        iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH_ROAMING[mInetCondition];
383                    } else {
384                        iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH[mInetCondition];
385                    }
386                }
387                mPhoneSignalIconId = iconList[iconLevel];
388                mDataSignalIconId = TelephonyIcons.DATA_SIGNAL_STRENGTH[mInetCondition][iconLevel];
389            }
390        }
391    }
392
393    private final void updateDataNetType() {
394        switch (mDataNetType) {
395            case TelephonyManager.NETWORK_TYPE_UNKNOWN:
396                mDataIconList = TelephonyIcons.DATA_G[mInetCondition];
397                mDataTypeIconId = 0;
398                break;
399            case TelephonyManager.NETWORK_TYPE_EDGE:
400                mDataIconList = TelephonyIcons.DATA_E[mInetCondition];
401                mDataTypeIconId = R.drawable.stat_sys_signal_edge;
402                break;
403            case TelephonyManager.NETWORK_TYPE_UMTS:
404                mDataIconList = TelephonyIcons.DATA_3G[mInetCondition];
405                mDataTypeIconId = R.drawable.stat_sys_signal_3g;
406                break;
407            case TelephonyManager.NETWORK_TYPE_HSDPA:
408            case TelephonyManager.NETWORK_TYPE_HSUPA:
409            case TelephonyManager.NETWORK_TYPE_HSPA:
410                if (mHspaDataDistinguishable) {
411                    mDataIconList = TelephonyIcons.DATA_H[mInetCondition];
412                    mDataTypeIconId = R.drawable.stat_sys_signal_hsdpa;
413                } else {
414                    mDataIconList = TelephonyIcons.DATA_3G[mInetCondition];
415                    mDataTypeIconId = R.drawable.stat_sys_signal_3g;
416                }
417                break;
418            case TelephonyManager.NETWORK_TYPE_CDMA:
419                // display 1xRTT for IS95A/B
420                mDataIconList = TelephonyIcons.DATA_1X[mInetCondition];
421                mDataTypeIconId = R.drawable.stat_sys_signal_1x;
422                break;
423            case TelephonyManager.NETWORK_TYPE_1xRTT:
424                mDataIconList = TelephonyIcons.DATA_1X[mInetCondition];
425                mDataTypeIconId = R.drawable.stat_sys_signal_1x;
426                break;
427            case TelephonyManager.NETWORK_TYPE_EVDO_0: //fall through
428            case TelephonyManager.NETWORK_TYPE_EVDO_A:
429            case TelephonyManager.NETWORK_TYPE_EVDO_B:
430            case TelephonyManager.NETWORK_TYPE_EHRPD:
431                mDataIconList = TelephonyIcons.DATA_3G[mInetCondition];
432                mDataTypeIconId = R.drawable.stat_sys_signal_3g;
433                break;
434            case TelephonyManager.NETWORK_TYPE_LTE:
435                mDataIconList = TelephonyIcons.DATA_4G[mInetCondition];
436                mDataTypeIconId = R.drawable.stat_sys_signal_4g;
437                break;
438            default:
439                mDataIconList = TelephonyIcons.DATA_G[mInetCondition];
440                mDataTypeIconId = R.drawable.stat_sys_signal_gprs;
441                break;
442        }
443        if ((isCdma() && isCdmaEri()) || mPhone.isNetworkRoaming()) {
444            mDataTypeIconId = R.drawable.stat_sys_signal_roam;
445        }
446    }
447
448    boolean isCdmaEri() {
449        if (mServiceState != null) {
450            final int iconIndex = mServiceState.getCdmaEriIconIndex();
451            if (iconIndex != EriInfo.ROAMING_INDICATOR_OFF) {
452                final int iconMode = mServiceState.getCdmaEriIconMode();
453                if (iconMode == EriInfo.ROAMING_ICON_MODE_NORMAL
454                        || iconMode == EriInfo.ROAMING_ICON_MODE_FLASH) {
455                    return true;
456                }
457            }
458        }
459        return false;
460    }
461
462    private final void updateDataIcon() {
463        int iconId;
464        boolean visible = true;
465
466        if (!isCdma()) {
467            // GSM case, we have to check also the sim state
468            if (mSimState == IccCard.State.READY || mSimState == IccCard.State.UNKNOWN) {
469                if (hasService() && mDataState == TelephonyManager.DATA_CONNECTED) {
470                    switch (mDataActivity) {
471                        case TelephonyManager.DATA_ACTIVITY_IN:
472                            iconId = mDataIconList[1];
473                            break;
474                        case TelephonyManager.DATA_ACTIVITY_OUT:
475                            iconId = mDataIconList[2];
476                            break;
477                        case TelephonyManager.DATA_ACTIVITY_INOUT:
478                            iconId = mDataIconList[3];
479                            break;
480                        default:
481                            iconId = mDataIconList[0];
482                            break;
483                    }
484                    mDataDirectionIconId = iconId;
485                } else {
486                    iconId = 0;
487                    visible = false;
488                }
489            } else {
490                iconId = R.drawable.stat_sys_no_sim;
491            }
492        } else {
493            // CDMA case, mDataActivity can be also DATA_ACTIVITY_DORMANT
494            if (hasService() && mDataState == TelephonyManager.DATA_CONNECTED) {
495                switch (mDataActivity) {
496                    case TelephonyManager.DATA_ACTIVITY_IN:
497                        iconId = mDataIconList[1];
498                        break;
499                    case TelephonyManager.DATA_ACTIVITY_OUT:
500                        iconId = mDataIconList[2];
501                        break;
502                    case TelephonyManager.DATA_ACTIVITY_INOUT:
503                        iconId = mDataIconList[3];
504                        break;
505                    case TelephonyManager.DATA_ACTIVITY_DORMANT:
506                    default:
507                        iconId = mDataIconList[0];
508                        break;
509                }
510            } else {
511                iconId = 0;
512                visible = false;
513            }
514        }
515
516        // yuck - this should NOT be done by the status bar
517        long ident = Binder.clearCallingIdentity();
518        try {
519            mBatteryStats.notePhoneDataConnectionState(mPhone.getNetworkType(), visible);
520        } catch (RemoteException e) {
521        } finally {
522            Binder.restoreCallingIdentity(ident);
523        }
524
525        mDataDirectionIconId = iconId;
526        mDataConnected = visible;
527    }
528
529    void updateNetworkName(boolean showSpn, String spn, boolean showPlmn, String plmn) {
530        if (false) {
531            Slog.d("CarrierLabel", "updateNetworkName showSpn=" + showSpn + " spn=" + spn
532                    + " showPlmn=" + showPlmn + " plmn=" + plmn);
533        }
534        StringBuilder str = new StringBuilder();
535        boolean something = false;
536        if (showPlmn && plmn != null) {
537            str.append(plmn);
538            something = true;
539        }
540        if (showSpn && spn != null) {
541            if (something) {
542                str.append(mNetworkNameSeparator);
543            }
544            str.append(spn);
545            something = true;
546        }
547        if (something) {
548            mNetworkName = str.toString();
549        } else {
550            mNetworkName = mNetworkNameDefault;
551        }
552    }
553
554    // ===== Wifi ===================================================================
555
556    class WifiHandler extends Handler {
557        @Override
558        public void handleMessage(Message msg) {
559            switch (msg.what) {
560                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
561                    if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
562                        mWifiChannel.sendMessage(Message.obtain(this,
563                                AsyncChannel.CMD_CHANNEL_FULL_CONNECTION));
564                    } else {
565                        Slog.e(TAG, "Failed to connect to wifi");
566                    }
567                    break;
568                case WifiManager.DATA_ACTIVITY_NOTIFICATION:
569                    if (msg.arg1 != mWifiActivity) {
570                        mWifiActivity = msg.arg1;
571                        refreshViews();
572                    }
573                    break;
574                default:
575                    //Ignore
576                    break;
577            }
578        }
579    }
580
581    private void updateWifiState(Intent intent) {
582        final String action = intent.getAction();
583        if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
584            mWifiEnabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
585                    WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED;
586
587        } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
588            final NetworkInfo networkInfo = (NetworkInfo)
589                    intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
590            boolean wasConnected = mWifiConnected;
591            mWifiConnected = networkInfo != null && networkInfo.isConnected();
592            // If we just connected, grab the inintial signal strength and ssid
593            if (mWifiConnected && !wasConnected) {
594                WifiInfo info = mWifiManager.getConnectionInfo();
595                if (info != null) {
596                    mWifiLevel = WifiManager.calculateSignalLevel(info.getRssi(),
597                            WifiIcons.WIFI_LEVEL_COUNT);
598                    mWifiSsid = huntForSsid(info);
599                } else {
600                    mWifiLevel = 0;
601                    mWifiSsid = null;
602                }
603            } else if (!mWifiConnected) {
604                mWifiLevel = 0;
605                mWifiSsid = null;
606            }
607
608        } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
609            if (mWifiConnected) {
610                final int newRssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200);
611                mWifiLevel = WifiManager.calculateSignalLevel(newRssi, WifiIcons.WIFI_LEVEL_COUNT);
612            }
613        }
614
615        updateWifiIcons();
616    }
617
618    private void updateWifiIcons() {
619        if (mWifiConnected) {
620            mWifiIconId = WifiIcons.WIFI_SIGNAL_STRENGTH[mInetCondition][mWifiLevel];
621        } else {
622            mWifiIconId = WifiIcons.WIFI_SIGNAL_STRENGTH[0][0];
623        }
624    }
625
626    private String huntForSsid(WifiInfo info) {
627        String ssid = info.getSSID();
628        if (ssid != null) {
629            return ssid;
630        }
631        // OK, it's not in the connectionInfo; we have to go hunting for it
632        List<WifiConfiguration> networks = mWifiManager.getConfiguredNetworks();
633        for (WifiConfiguration net : networks) {
634            if (net.networkId == info.getNetworkId()) {
635                return net.SSID;
636            }
637        }
638        return null;
639    }
640
641
642    // ===== Full or limited Internet connectivity ==================================
643
644    private void updateConnectivity(Intent intent) {
645        NetworkInfo info = (NetworkInfo)(intent.getParcelableExtra(
646                ConnectivityManager.EXTRA_NETWORK_INFO));
647        int connectionStatus = intent.getIntExtra(ConnectivityManager.EXTRA_INET_CONDITION, 0);
648
649        int inetCondition = (connectionStatus > INET_CONDITION_THRESHOLD ? 1 : 0);
650
651        switch (info.getType()) {
652            case ConnectivityManager.TYPE_MOBILE:
653                mInetCondition = inetCondition;
654                updateDataNetType();
655                updateDataIcon();
656                updateTelephonySignalStrength(); // apply any change in connectionStatus
657                break;
658            case ConnectivityManager.TYPE_WIFI:
659                mInetCondition = inetCondition;
660                updateWifiIcons();
661                break;
662            case ConnectivityManager.TYPE_BLUETOOTH:
663                mInetCondition = inetCondition;
664                if (info != null) {
665                    mBluetoothTethered = info.isConnected() ? true: false;
666                } else {
667                    mBluetoothTethered = false;
668                }
669                break;
670        }
671    }
672
673
674    // ===== Update the views =======================================================
675
676    // figure out what to show- there should be one connected network or nothing
677    // General order of preference is: wifi, 3G than bluetooth. This might vary by product.
678    void refreshViews() {
679        Context context = mContext;
680
681        int combinedSignalIconId;
682        int dataDirectionOverlayIconId = 0;
683        int dataTypeIconId;
684        String label;
685        int N;
686
687        if (mWifiConnected) {
688            if (mWifiSsid == null) {
689                label = context.getString(R.string.status_bar_settings_signal_meter_wifi_nossid);
690            } else {
691                label = mWifiSsid;
692                switch (mWifiActivity) {
693                    case WifiManager.DATA_ACTIVITY_IN:
694                        dataDirectionOverlayIconId = R.drawable.stat_sys_wifi_in;
695                        break;
696                    case WifiManager.DATA_ACTIVITY_OUT:
697                        dataDirectionOverlayIconId = R.drawable.stat_sys_wifi_out;
698                        break;
699                    case WifiManager.DATA_ACTIVITY_INOUT:
700                        dataDirectionOverlayIconId = R.drawable.stat_sys_wifi_inout;
701                        break;
702                    case WifiManager.DATA_ACTIVITY_NONE:
703                        break;
704                }
705            }
706            combinedSignalIconId = mWifiIconId;
707            dataTypeIconId = 0;
708        } else if (mDataConnected) {
709            label = mNetworkName;
710            combinedSignalIconId = mDataSignalIconId;
711            switch (mDataActivity) {
712                case TelephonyManager.DATA_ACTIVITY_IN:
713                    dataDirectionOverlayIconId = R.drawable.stat_sys_signal_in;
714                    break;
715                case TelephonyManager.DATA_ACTIVITY_OUT:
716                    dataDirectionOverlayIconId = R.drawable.stat_sys_signal_out;
717                    break;
718                case TelephonyManager.DATA_ACTIVITY_INOUT:
719                    dataDirectionOverlayIconId = R.drawable.stat_sys_signal_inout;
720                    break;
721                default:
722                    dataDirectionOverlayIconId = 0;
723                    break;
724            }
725            combinedSignalIconId = mDataSignalIconId;
726            dataTypeIconId = mDataTypeIconId;
727        } else if (mBluetoothTethered) {
728            label = mContext.getString(R.string.bluetooth_tethered);
729            combinedSignalIconId = mBluetoothTetherIconId;
730            dataTypeIconId = 0;
731        } else if (mAirplaneMode &&
732                (mServiceState == null || (!hasService() && !mServiceState.isEmergencyOnly()))) {
733            // Only display the flight-mode icon if not in "emergency calls only" mode.
734            label = context.getString(R.string.status_bar_settings_signal_meter_disconnected);
735            combinedSignalIconId = R.drawable.stat_sys_signal_flightmode;
736            dataTypeIconId = 0;
737        } else {
738            label = context.getString(R.string.status_bar_settings_signal_meter_disconnected);
739            // On devices without mobile radios, we want to show the wifi icon
740            combinedSignalIconId =
741                hasMobileDataFeature() ? mDataSignalIconId : mWifiIconId;
742            dataTypeIconId = 0;
743        }
744
745        if (DEBUG) {
746            Slog.d(TAG, "refreshViews combinedSignalIconId=0x"
747                    + Integer.toHexString(combinedSignalIconId)
748                    + "/" + getResourceName(combinedSignalIconId)
749                    + " dataDirectionOverlayIconId=0x" + Integer.toHexString(dataDirectionOverlayIconId)
750                    + " mAirplaneMode=" + mAirplaneMode
751                    + " mDataActivity=" + mDataActivity
752                    + " mPhoneSignalIconId=0x" + Integer.toHexString(mPhoneSignalIconId)
753                    + " mDataDirectionIconId=0x" + Integer.toHexString(mDataDirectionIconId)
754                    + " mDataSignalIconId=0x" + Integer.toHexString(mDataSignalIconId)
755                    + " mDataTypeIconId=0x" + Integer.toHexString(mDataTypeIconId)
756                    + " mWifiIconId=0x" + Integer.toHexString(mWifiIconId)
757                    + " mBluetoothTetherIconId=0x" + Integer.toHexString(mBluetoothTetherIconId));
758        }
759
760        // the phone icon on phones
761        if (mLastPhoneSignalIconId != mPhoneSignalIconId) {
762            mLastPhoneSignalIconId = mPhoneSignalIconId;
763            N = mPhoneSignalIconViews.size();
764            for (int i=0; i<N; i++) {
765                final ImageView v = mPhoneSignalIconViews.get(i);
766                v.setImageResource(mPhoneSignalIconId);
767            }
768        }
769
770        // the data icon on phones
771        if (mLastDataDirectionIconId != mDataDirectionIconId) {
772            mLastDataDirectionIconId = mDataDirectionIconId;
773            N = mDataDirectionIconViews.size();
774            for (int i=0; i<N; i++) {
775                final ImageView v = mDataDirectionIconViews.get(i);
776                v.setImageResource(mDataDirectionIconId);
777            }
778        }
779
780        // the wifi icon on phones
781        if (mLastWifiIconId != mWifiIconId) {
782            mLastWifiIconId = mWifiIconId;
783            N = mWifiIconViews.size();
784            for (int i=0; i<N; i++) {
785                final ImageView v = mWifiIconViews.get(i);
786                v.setImageResource(mWifiIconId);
787            }
788        }
789
790        // the combined data signal icon
791        if (mLastCombinedSignalIconId != combinedSignalIconId) {
792            mLastCombinedSignalIconId = combinedSignalIconId;
793            N = mCombinedSignalIconViews.size();
794            for (int i=0; i<N; i++) {
795                final ImageView v = mCombinedSignalIconViews.get(i);
796                v.setImageResource(combinedSignalIconId);
797            }
798        }
799
800        // the data network type overlay
801        if (mLastDataTypeIconId != dataTypeIconId) {
802            mLastDataTypeIconId = dataTypeIconId;
803            N = mDataTypeIconViews.size();
804            for (int i=0; i<N; i++) {
805                final ImageView v = mDataTypeIconViews.get(i);
806                if (dataTypeIconId == 0) {
807                    v.setVisibility(View.INVISIBLE);
808                } else {
809                    v.setVisibility(View.VISIBLE);
810                    v.setImageResource(dataTypeIconId);
811                }
812            }
813        }
814
815        // the data direction overlay
816        if (mLastDataDirectionOverlayIconId != dataDirectionOverlayIconId) {
817            if (DEBUG) {
818                Slog.d(TAG, "changing data overlay icon id to " + dataDirectionOverlayIconId);
819            }
820            mLastDataDirectionOverlayIconId = dataDirectionOverlayIconId;
821            N = mDataDirectionOverlayIconViews.size();
822            for (int i=0; i<N; i++) {
823                final ImageView v = mDataDirectionOverlayIconViews.get(i);
824                if (dataDirectionOverlayIconId == 0) {
825                    v.setVisibility(View.INVISIBLE);
826                } else {
827                    v.setVisibility(View.VISIBLE);
828                    v.setImageResource(dataDirectionOverlayIconId);
829                }
830            }
831        }
832
833        // the label in the notification panel
834        if (!mLastLabel.equals(label)) {
835            mLastLabel = label;
836            N = mLabelViews.size();
837            for (int i=0; i<N; i++) {
838                TextView v = mLabelViews.get(i);
839                v.setText(label);
840            }
841        }
842    }
843
844    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
845        pw.println("  - telephony ------");
846        pw.print("  mHspaDataDistinguishable=");
847        pw.println(mHspaDataDistinguishable);
848        pw.print("  mDataConnected=");
849        pw.println(mDataConnected);
850        pw.print("  mSimState=");
851        pw.println(mSimState);
852        pw.print("  mPhoneState=");
853        pw.println(mPhoneState);
854        pw.print("  mDataState=");
855        pw.println(mDataState);
856        pw.print("  mDataActivity=");
857        pw.println(mDataActivity);
858        pw.print("  mServiceState=");
859        pw.println(mServiceState);
860        pw.print("  mNetworkName=");
861        pw.println(mNetworkName);
862        pw.print("  mNetworkNameDefault=");
863        pw.println(mNetworkNameDefault);
864        pw.print("  mNetworkNameSeparator=");
865        pw.println(mNetworkNameSeparator);
866        pw.print("  mPhoneSignalIconId=0x");
867        pw.print(Integer.toHexString(mPhoneSignalIconId));
868        pw.print("/");
869        pw.println(getResourceName(mPhoneSignalIconId));
870        pw.print("  mDataDirectionIconId=");
871        pw.print(Integer.toHexString(mDataDirectionIconId));
872        pw.print("/");
873        pw.println(getResourceName(mDataDirectionIconId));
874        pw.print("  mDataSignalIconId=");
875        pw.print(Integer.toHexString(mDataSignalIconId));
876        pw.print("/");
877        pw.println(getResourceName(mDataSignalIconId));
878        pw.print("  mDataTypeIconId=");
879        pw.print(Integer.toHexString(mDataTypeIconId));
880        pw.print("/");
881        pw.println(getResourceName(mDataTypeIconId));
882
883        pw.println("  - wifi ------");
884        pw.print("  mWifiEnabled=");
885        pw.println(mWifiEnabled);
886        pw.print("  mWifiConnected=");
887        pw.println(mWifiConnected);
888        pw.print("  mWifiLevel=");
889        pw.println(mWifiLevel);
890        pw.print("  mWifiSsid=");
891        pw.println(mWifiSsid);
892        pw.print("  mWifiIconId=");
893        pw.println(mWifiIconId);
894        pw.print("  mWifiActivity=");
895        pw.println(mWifiActivity);
896
897
898        pw.println("  - Bluetooth ----");
899        pw.print(" mBtReverseTethered=");
900        pw.println(mBluetoothTethered);
901
902        pw.println("  - connectivity ------");
903        pw.print("  mInetCondition=");
904        pw.println(mInetCondition);
905
906        pw.println("  - icons ------");
907        pw.print("  mLastPhoneSignalIconId=0x");
908        pw.print(Integer.toHexString(mLastPhoneSignalIconId));
909        pw.print("/");
910        pw.println(getResourceName(mLastPhoneSignalIconId));
911        pw.print("  mLastDataDirectionIconId=0x");
912        pw.print(Integer.toHexString(mLastDataDirectionIconId));
913        pw.print("/");
914        pw.println(getResourceName(mLastDataDirectionIconId));
915        pw.print("  mLastDataDirectionOverlayIconId=0x");
916        pw.print(Integer.toHexString(mLastDataDirectionOverlayIconId));
917        pw.print("/");
918        pw.println(getResourceName(mLastDataDirectionOverlayIconId));
919        pw.print("  mLastWifiIconId=0x");
920        pw.print(Integer.toHexString(mLastWifiIconId));
921        pw.print("/");
922        pw.println(getResourceName(mLastWifiIconId));
923        pw.print("  mLastCombinedSignalIconId=0x");
924        pw.print(Integer.toHexString(mLastCombinedSignalIconId));
925        pw.print("/");
926        pw.println(getResourceName(mLastCombinedSignalIconId));
927        pw.print("  mLastDataTypeIconId=0x");
928        pw.print(Integer.toHexString(mLastDataTypeIconId));
929        pw.print("/");
930        pw.println(getResourceName(mLastCombinedSignalIconId));
931        pw.print("  mLastLabel=");
932        pw.print(mLastLabel);
933    }
934
935    private String getResourceName(int resId) {
936        if (resId == 0) {
937            final Resources res = mContext.getResources();
938            try {
939                return res.getResourceName(resId);
940            } catch (android.content.res.Resources.NotFoundException ex) {
941                return "(unknown)";
942            }
943        } else {
944            return "(null)";
945        }
946    }
947
948}
949