ConnectivityService.java revision 54b6cfa9a9e5b861a9930af873580d6dc20f773c
1/*
2 * Copyright (C) 2008 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.server;
18
19import android.app.Notification;
20import android.app.NotificationManager;
21import android.content.ContentResolver;
22import android.content.Context;
23import android.content.Intent;
24import android.content.pm.PackageManager;
25import android.net.ConnectivityManager;
26import android.net.IConnectivityManager;
27import android.net.MobileDataStateTracker;
28import android.net.NetworkInfo;
29import android.net.NetworkStateTracker;
30import android.net.wifi.WifiStateTracker;
31import android.os.Binder;
32import android.os.Handler;
33import android.os.Looper;
34import android.os.Message;
35import android.os.ServiceManager;
36import android.os.SystemProperties;
37import android.provider.Settings;
38import android.telephony.TelephonyManager;
39import android.util.EventLog;
40import android.util.Log;
41
42import java.io.FileDescriptor;
43import java.io.PrintWriter;
44
45/**
46 * {@hide}
47 */
48public class ConnectivityService extends IConnectivityManager.Stub {
49
50    private static final boolean DBG = false;
51    private static final String TAG = "ConnectivityService";
52
53    // Event log tags (must be in sync with event-log-tags)
54    private static final int EVENTLOG_CONNECTIVITY_STATE_CHANGED = 50020;
55
56    /**
57     * Sometimes we want to refer to the individual network state
58     * trackers separately, and sometimes we just want to treat them
59     * abstractly.
60     */
61    private NetworkStateTracker mNetTrackers[];
62    private boolean mTeardownRequested[];
63    private WifiStateTracker mWifiStateTracker;
64    private MobileDataStateTracker mMobileDataStateTracker;
65    private WifiWatchdogService mWifiWatchdogService;
66
67    private Context mContext;
68    private int mNetworkPreference;
69    private NetworkStateTracker mActiveNetwork;
70
71    private int mNumDnsEntries;
72    private static int mDnsChangeCounter;
73
74    private boolean mTestMode;
75    private static ConnectivityService sServiceInstance;
76
77    private static class ConnectivityThread extends Thread {
78        private Context mContext;
79
80        private ConnectivityThread(Context context) {
81            super("ConnectivityThread");
82            mContext = context;
83        }
84
85        @Override
86        public void run() {
87            Looper.prepare();
88            synchronized (this) {
89                sServiceInstance = new ConnectivityService(mContext);
90                notifyAll();
91            }
92            Looper.loop();
93        }
94
95        public static ConnectivityService getServiceInstance(Context context) {
96            ConnectivityThread thread = new ConnectivityThread(context);
97            thread.start();
98
99            synchronized (thread) {
100                while (sServiceInstance == null) {
101                    try {
102                        // Wait until sServiceInstance has been initialized.
103                        thread.wait();
104                    } catch (InterruptedException e) {
105                    }
106                }
107            }
108
109            return sServiceInstance;
110        }
111    }
112
113    public static ConnectivityService getInstance(Context context) {
114        return ConnectivityThread.getServiceInstance(context);
115    }
116
117    private ConnectivityService(Context context) {
118        if (DBG) Log.v(TAG, "ConnectivityService starting up");
119        mContext = context;
120        mNetTrackers = new NetworkStateTracker[2];
121        mTeardownRequested = new boolean[2];
122        Handler handler = new MyHandler();
123
124        mNetworkPreference = getPersistedNetworkPreference();
125
126        /*
127         * Create the network state trackers for Wi-Fi and mobile
128         * data. Maybe this could be done with a factory class,
129         * but it's not clear that it's worth it, given that
130         * the number of different network types is not going
131         * to change very often.
132         */
133        if (DBG) Log.v(TAG, "Starting Wifi Service.");
134        mWifiStateTracker = new WifiStateTracker(context, handler);
135        WifiService wifiService = new WifiService(context, mWifiStateTracker);
136        ServiceManager.addService(Context.WIFI_SERVICE, wifiService);
137        // The WifiStateTracker should appear first in the list
138        mNetTrackers[ConnectivityManager.TYPE_WIFI] = mWifiStateTracker;
139
140        mMobileDataStateTracker = new MobileDataStateTracker(context, handler);
141        mNetTrackers[ConnectivityManager.TYPE_MOBILE] = mMobileDataStateTracker;
142
143        mActiveNetwork = null;
144        mNumDnsEntries = 0;
145
146        mTestMode = SystemProperties.get("cm.test.mode").equals("true")
147                && SystemProperties.get("ro.build.type").equals("eng");
148
149        for (NetworkStateTracker t : mNetTrackers)
150            t.startMonitoring();
151
152        // Constructing this starts it too
153        mWifiWatchdogService = new WifiWatchdogService(context, mWifiStateTracker);
154    }
155
156    /**
157     * Sets the preferred network.
158     * @param preference the new preference
159     */
160    public synchronized void setNetworkPreference(int preference) {
161        enforceChangePermission();
162        if (ConnectivityManager.isNetworkTypeValid(preference)) {
163            int oldPreference = mNetworkPreference;
164            persistNetworkPreference(preference);
165            if (mNetworkPreference != oldPreference)
166                enforcePreference();
167        }
168    }
169
170    public int getNetworkPreference() {
171        enforceAccessPermission();
172        return mNetworkPreference;
173    }
174
175    private void persistNetworkPreference(int networkPreference) {
176        final ContentResolver cr = mContext.getContentResolver();
177        Settings.System.putInt(cr, Settings.System.NETWORK_PREFERENCE, networkPreference);
178    }
179
180    private int getPersistedNetworkPreference() {
181        final ContentResolver cr = mContext.getContentResolver();
182
183        final int networkPrefSetting = Settings.System
184                .getInt(cr, Settings.System.NETWORK_PREFERENCE, -1);
185        if (networkPrefSetting != -1) {
186            return networkPrefSetting;
187        }
188
189        return ConnectivityManager.DEFAULT_NETWORK_PREFERENCE;
190    }
191
192    /**
193     * Make the state of network connectivity conform to the preference settings.
194     * In this method, we only tear down a non-preferred network. Establishing
195     * a connection to the preferred network is taken care of when we handle
196     * the disconnect event from the non-preferred network
197     * (see {@link #handleDisconnect(NetworkInfo)}).
198     */
199    private void enforcePreference() {
200        if (mActiveNetwork == null)
201            return;
202
203        for (NetworkStateTracker t : mNetTrackers) {
204            if (t == mActiveNetwork) {
205                int netType = t.getNetworkInfo().getType();
206                int otherNetType = ((netType == ConnectivityManager.TYPE_WIFI) ?
207                        ConnectivityManager.TYPE_MOBILE :
208                        ConnectivityManager.TYPE_WIFI);
209
210                if (t.getNetworkInfo().getType() != mNetworkPreference) {
211                    NetworkStateTracker otherTracker = mNetTrackers[otherNetType];
212                    if (otherTracker.isAvailable()) {
213                        teardown(t);
214                    }
215                }
216            }
217        }
218    }
219
220    private boolean teardown(NetworkStateTracker netTracker) {
221        if (netTracker.teardown()) {
222            mTeardownRequested[netTracker.getNetworkInfo().getType()] = true;
223            return true;
224        } else {
225            return false;
226        }
227    }
228
229    /**
230     * Return NetworkInfo for the active network interface. It is assumed that at most
231     * one network is active at a time. If more than one is active, it is indeterminate
232     * which will be returned.
233     * @return the info for the active network, or {@code null} if none is active
234     */
235    public NetworkInfo getActiveNetworkInfo() {
236        enforceAccessPermission();
237        for (NetworkStateTracker t : mNetTrackers) {
238            NetworkInfo info = t.getNetworkInfo();
239            if (info.isConnected()) {
240                return info;
241            }
242        }
243        return null;
244    }
245
246    public NetworkInfo getNetworkInfo(int networkType) {
247        enforceAccessPermission();
248        if (ConnectivityManager.isNetworkTypeValid(networkType)) {
249            NetworkStateTracker t = mNetTrackers[networkType];
250            if (t != null)
251                return t.getNetworkInfo();
252        }
253        return null;
254    }
255
256    public NetworkInfo[] getAllNetworkInfo() {
257        enforceAccessPermission();
258        NetworkInfo[] result = new NetworkInfo[mNetTrackers.length];
259        int i = 0;
260        for (NetworkStateTracker t : mNetTrackers) {
261            result[i++] = t.getNetworkInfo();
262        }
263        return result;
264    }
265
266    public boolean setRadios(boolean turnOn) {
267        boolean result = true;
268        enforceChangePermission();
269        for (NetworkStateTracker t : mNetTrackers) {
270            result = t.setRadio(turnOn) && result;
271        }
272        return result;
273    }
274
275    public boolean setRadio(int netType, boolean turnOn) {
276        enforceChangePermission();
277        if (!ConnectivityManager.isNetworkTypeValid(netType)) {
278            return false;
279        }
280        NetworkStateTracker tracker = mNetTrackers[netType];
281        return tracker != null && tracker.setRadio(turnOn);
282    }
283
284    public int startUsingNetworkFeature(int networkType, String feature) {
285        enforceChangePermission();
286        if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
287            return -1;
288        }
289        NetworkStateTracker tracker = mNetTrackers[networkType];
290        if (tracker != null) {
291            return tracker.startUsingNetworkFeature(feature, getCallingPid(), getCallingUid());
292        }
293        return -1;
294
295    }
296
297    public int stopUsingNetworkFeature(int networkType, String feature) {
298        enforceChangePermission();
299        if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
300            return -1;
301        }
302        NetworkStateTracker tracker = mNetTrackers[networkType];
303        if (tracker != null) {
304            return tracker.stopUsingNetworkFeature(feature, getCallingPid(), getCallingUid());
305        }
306        return -1;
307    }
308
309    /**
310     * Ensure that a network route exists to deliver traffic to the specified
311     * host via the specified network interface.
312     * @param networkType the type of the network over which traffic to the specified
313     * host is to be routed
314     * @param hostAddress the IP address of the host to which the route is desired
315     * @return {@code true} on success, {@code false} on failure
316     */
317    public boolean requestRouteToHost(int networkType, int hostAddress) {
318        enforceChangePermission();
319        if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
320            return false;
321        }
322        NetworkStateTracker tracker = mNetTrackers[networkType];
323        /*
324         * If there's only one connected network, and it's the one requested,
325         * then we don't have to do anything - the requested route already
326         * exists. If it's not the requested network, then it's not possible
327         * to establish the requested route. Finally, if there is more than
328         * one connected network, then we must insert an entry in the routing
329         * table.
330         */
331        if (getNumConnectedNetworks() > 1) {
332            return tracker.requestRouteToHost(hostAddress);
333        } else {
334            return tracker.getNetworkInfo().getType() == networkType;
335        }
336    }
337
338    private int getNumConnectedNetworks() {
339        int numConnectedNets = 0;
340
341        for (NetworkStateTracker nt : mNetTrackers) {
342            if (nt.getNetworkInfo().isConnected()
343                    && !mTeardownRequested[nt.getNetworkInfo().getType()]) {
344                ++numConnectedNets;
345            }
346        }
347        return numConnectedNets;
348    }
349
350    private void enforceAccessPermission() {
351        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NETWORK_STATE,
352                                          "ConnectivityService");
353    }
354
355    private void enforceChangePermission() {
356        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_NETWORK_STATE,
357                                          "ConnectivityService");
358
359    }
360
361    /**
362     * Handle a {@code DISCONNECTED} event. If this pertains to the non-active network,
363     * we ignore it. If it is for the active network, we send out a broadcast.
364     * But first, we check whether it might be possible to connect to a different
365     * network.
366     * @param info the {@code NetworkInfo} for the network
367     */
368    private void handleDisconnect(NetworkInfo info) {
369
370        if (DBG) Log.v(TAG, "Handle DISCONNECT for " + info.getTypeName());
371
372        /*
373         * If the disconnected network is not the active one, then don't report
374         * this as a loss of connectivity. What probably happened is that we're
375         * getting the disconnect for a network that we explicitly disabled
376         * in accordance with network preference policies.
377         */
378        mTeardownRequested[info.getType()] = false;
379        if (mActiveNetwork == null ||  info.getType() != mActiveNetwork.getNetworkInfo().getType())
380            return;
381
382        NetworkStateTracker newNet;
383        if (info.getType() == ConnectivityManager.TYPE_MOBILE) {
384            newNet = mWifiStateTracker;
385        } else /* info().getType() == TYPE_WIFI */ {
386            newNet = mMobileDataStateTracker;
387        }
388
389        NetworkInfo switchTo = null;
390        if (newNet.isAvailable()) {
391            mActiveNetwork = newNet;
392            switchTo = newNet.getNetworkInfo();
393            switchTo.setFailover(true);
394            if (!switchTo.isConnectedOrConnecting())
395                newNet.reconnect();
396        }
397
398        boolean otherNetworkConnected = false;
399        Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
400        intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
401        if (info.isFailover()) {
402            intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
403            info.setFailover(false);
404        }
405        if (info.getReason() != null) {
406            intent.putExtra(ConnectivityManager.EXTRA_REASON, info.getReason());
407        }
408        if (info.getExtraInfo() != null) {
409            intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, info.getExtraInfo());
410        }
411        if (switchTo != null) {
412            otherNetworkConnected = switchTo.isConnected();
413            if (DBG) {
414                if (otherNetworkConnected) {
415                    Log.v(TAG, "Switching to already connected " + switchTo.getTypeName());
416                } else {
417                    Log.v(TAG, "Attempting to switch to " + switchTo.getTypeName());
418                }
419            }
420            intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO, switchTo);
421        } else {
422            intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
423        }
424        if (DBG) Log.v(TAG, "Sending DISCONNECT bcast for " + info.getTypeName() +
425                (switchTo == null ? "" : " other=" + switchTo.getTypeName()));
426
427        mContext.sendStickyBroadcast(intent);
428        /*
429         * If the failover network is already connected, then immediately send out
430         * a followup broadcast indicating successful failover
431         */
432        if (switchTo != null && otherNetworkConnected)
433            sendConnectedBroadcast(switchTo);
434    }
435
436    private void sendConnectedBroadcast(NetworkInfo info) {
437        Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
438        intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
439        if (info.isFailover()) {
440            intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
441            info.setFailover(false);
442        }
443        if (info.getReason() != null) {
444            intent.putExtra(ConnectivityManager.EXTRA_REASON, info.getReason());
445        }
446        if (info.getExtraInfo() != null) {
447            intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, info.getExtraInfo());
448        }
449        mContext.sendStickyBroadcast(intent);
450    }
451
452    private void handleConnectionFailure(NetworkInfo info) {
453        mTeardownRequested[info.getType()] = false;
454        if (getActiveNetworkInfo() == null) {
455            String reason = info.getReason();
456            String extraInfo = info.getExtraInfo();
457
458            if (DBG) {
459                String reasonText;
460                if (reason == null) {
461                    reasonText = ".";
462                } else {
463                    reasonText = " (" + reason + ").";
464                }
465                Log.v(TAG, "Attempt to connect to " + info.getTypeName() + " failed" + reasonText);
466            }
467
468            Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
469            intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
470            intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
471            if (reason != null) {
472                intent.putExtra(ConnectivityManager.EXTRA_REASON, reason);
473            }
474            if (extraInfo != null) {
475                intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, extraInfo);
476            }
477            if (info.isFailover()) {
478                intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
479                info.setFailover(false);
480            }
481            mContext.sendStickyBroadcast(intent);
482        }
483    }
484
485    private void handleConnect(NetworkInfo info) {
486        if (DBG) Log.v(TAG, "Handle CONNECT for " + info.getTypeName());
487
488        // snapshot isFailover, because sendConnectedBroadcast() resets it
489        boolean isFailover = info.isFailover();
490        NetworkStateTracker thisNet = mNetTrackers[info.getType()];
491        NetworkStateTracker deadnet = null;
492        NetworkStateTracker otherNet;
493        if (info.getType() == ConnectivityManager.TYPE_MOBILE) {
494            otherNet = mWifiStateTracker;
495        } else /* info().getType() == TYPE_WIFI */ {
496            otherNet = mMobileDataStateTracker;
497        }
498        /*
499        * Check policy to see whether we are now connected to a network that
500        * takes precedence over the other one. If so, we need to tear down
501        * the other one.
502        */
503        NetworkInfo wifiInfo = mWifiStateTracker.getNetworkInfo();
504        NetworkInfo mobileInfo = mMobileDataStateTracker.getNetworkInfo();
505        if (wifiInfo.isConnected() && mobileInfo.isConnected()) {
506            if (mNetworkPreference == ConnectivityManager.TYPE_WIFI)
507                deadnet = mMobileDataStateTracker;
508            else
509                deadnet = mWifiStateTracker;
510        }
511
512        boolean toredown = false;
513        mTeardownRequested[info.getType()] = false;
514        if (!mTestMode && deadnet != null) {
515            if (DBG) Log.v(TAG, "Policy requires " +
516                  deadnet.getNetworkInfo().getTypeName() + " teardown");
517            toredown = teardown(deadnet);
518            if (DBG && !toredown) {
519                Log.d(TAG, "Network declined teardown request");
520            }
521        }
522
523        if (!toredown || deadnet.getNetworkInfo().getType() != info.getType()) {
524            mActiveNetwork = thisNet;
525            if (DBG) Log.v(TAG, "Sending CONNECT bcast for " + info.getTypeName());
526            thisNet.updateNetworkSettings();
527            sendConnectedBroadcast(info);
528            if (isFailover) {
529                otherNet.releaseWakeLock();
530            }
531        } else {
532            if (DBG) Log.v(TAG, "Not broadcasting CONNECT_ACTION to torn down network " +
533                info.getTypeName());
534        }
535    }
536
537    private void handleScanResultsAvailable(NetworkInfo info) {
538        int networkType = info.getType();
539        if (networkType != ConnectivityManager.TYPE_WIFI) {
540            if (DBG) Log.v(TAG, "Got ScanResultsAvailable for " + info.getTypeName() + " network."
541                + " Don't know how to handle.");
542        }
543
544        mNetTrackers[networkType].interpretScanResultsAvailable();
545    }
546
547    private void handleNotificationChange(boolean visible, int id, Notification notification) {
548        NotificationManager notificationManager = (NotificationManager) mContext
549                .getSystemService(Context.NOTIFICATION_SERVICE);
550
551        if (visible) {
552            notificationManager.notify(id, notification);
553        } else {
554            notificationManager.cancel(id);
555        }
556    }
557
558    /**
559     * After any kind of change in the connectivity state of any network,
560     * make sure that anything that depends on the connectivity state of
561     * more than one network is set up correctly. We're mainly concerned
562     * with making sure that the list of DNS servers is set up  according
563     * to which networks are connected, and ensuring that the right routing
564     * table entries exist.
565     */
566    private void handleConnectivityChange() {
567        /*
568         * If both mobile and wifi are enabled, add the host routes that
569         * will allow MMS traffic to pass on the mobile network. But
570         * remove the default route for the mobile network, so that there
571         * will be only one default route, to ensure that all traffic
572         * except MMS will travel via Wi-Fi.
573         */
574        int numConnectedNets = handleConfigurationChange();
575        if (numConnectedNets > 1) {
576            mMobileDataStateTracker.addPrivateRoutes();
577            mMobileDataStateTracker.removeDefaultRoute();
578        } else if (mMobileDataStateTracker.getNetworkInfo().isConnected()) {
579            mMobileDataStateTracker.removePrivateRoutes();
580            mMobileDataStateTracker.restoreDefaultRoute();
581        }
582    }
583
584    private int handleConfigurationChange() {
585        /*
586         * Set DNS properties. Always put Wi-Fi entries at the front of
587         * the list if it is active.
588         */
589        int index = 1;
590        String lastDns = "";
591        int numConnectedNets = 0;
592        int incrValue = ConnectivityManager.TYPE_MOBILE - ConnectivityManager.TYPE_WIFI;
593        int stopValue = ConnectivityManager.TYPE_MOBILE + incrValue;
594
595        for (int net = ConnectivityManager.TYPE_WIFI; net != stopValue; net += incrValue) {
596            NetworkStateTracker nt = mNetTrackers[net];
597            if (nt.getNetworkInfo().isConnected()
598                    && !mTeardownRequested[nt.getNetworkInfo().getType()]) {
599                ++numConnectedNets;
600                String[] dnsList = nt.getNameServers();
601                for (int i = 0; i < dnsList.length && dnsList[i] != null; i++) {
602                    // skip duplicate entries
603                    if (!dnsList[i].equals(lastDns)) {
604                        SystemProperties.set("net.dns" + index++, dnsList[i]);
605                        lastDns = dnsList[i];
606                    }
607                }
608            }
609        }
610        // Null out any DNS properties that are no longer used
611        for (int i = index; i <= mNumDnsEntries; i++) {
612            SystemProperties.set("net.dns" + i, "");
613        }
614        mNumDnsEntries = index - 1;
615        // Notify the name resolver library of the change
616        SystemProperties.set("net.dnschange", String.valueOf(mDnsChangeCounter++));
617        return numConnectedNets;
618    }
619
620    @Override
621    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
622        if (mContext.checkCallingPermission(android.Manifest.permission.DUMP)
623                != PackageManager.PERMISSION_GRANTED) {
624            pw.println("Permission Denial: can't dump ConnectivityService from from pid="
625                    + Binder.getCallingPid()
626                    + ", uid=" + Binder.getCallingUid());
627            return;
628        }
629        if (mActiveNetwork == null) {
630            pw.println("No active network");
631        } else {
632            pw.println("Active network: " + mActiveNetwork.getNetworkInfo().getTypeName());
633        }
634        pw.println();
635        for (NetworkStateTracker nst : mNetTrackers) {
636            pw.println(nst.getNetworkInfo());
637            pw.println(nst);
638            pw.println();
639        }
640    }
641
642    private class MyHandler extends Handler {
643        @Override
644        public void handleMessage(Message msg) {
645            NetworkInfo info;
646            switch (msg.what) {
647                case NetworkStateTracker.EVENT_STATE_CHANGED:
648                    info = (NetworkInfo) msg.obj;
649                    if (DBG) Log.v(TAG, "ConnectivityChange for " + info.getTypeName() + ": " +
650                            info.getState() + "/" + info.getDetailedState());
651
652                    // Connectivity state changed:
653                    // [31-11] Reserved for future use
654                    // [10-9] Mobile network connection type (as defined by the TelephonyManager)
655                    // [8-3] Detailed state ordinal (as defined by NetworkInfo.DetailedState)
656                    // [2-0] Network type (as defined by ConnectivityManager)
657                    int eventLogParam = (info.getType() & 0x7) |
658                         ((info.getDetailedState().ordinal() & 0x3f) << 3) |
659                         (TelephonyManager.getDefault().getNetworkType() << 9);
660                    EventLog.writeEvent(EVENTLOG_CONNECTIVITY_STATE_CHANGED, eventLogParam);
661
662                    if (info.getDetailedState() == NetworkInfo.DetailedState.FAILED) {
663                        handleConnectionFailure(info);
664                    } else if (info.getState() == NetworkInfo.State.DISCONNECTED) {
665                        handleDisconnect(info);
666                    } else if (info.getState() == NetworkInfo.State.SUSPENDED) {
667                        // TODO: need to think this over.
668                        // the logic here is, handle SUSPENDED the same as DISCONNECTED. The
669                        // only difference being we are broadcasting an intent with NetworkInfo
670                        // that's suspended. This allows the applications an opportunity to
671                        // handle DISCONNECTED and SUSPENDED differently, or not.
672                        handleDisconnect(info);
673                    } else if (info.getState() == NetworkInfo.State.CONNECTED) {
674                        handleConnect(info);
675                    }
676                    handleConnectivityChange();
677                    break;
678
679                case NetworkStateTracker.EVENT_SCAN_RESULTS_AVAILABLE:
680                    info = (NetworkInfo) msg.obj;
681                    handleScanResultsAvailable(info);
682                    break;
683
684                case NetworkStateTracker.EVENT_NOTIFICATION_CHANGED:
685                    handleNotificationChange(msg.arg1 == 1, msg.arg2, (Notification) msg.obj);
686
687                case NetworkStateTracker.EVENT_CONFIGURATION_CHANGED:
688                    handleConfigurationChange();
689                    break;
690            }
691        }
692    }
693}
694