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