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