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