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