ConnectivityService.java revision 9066cfe9886ac131c34d59ed0e2d287b0e3c0087
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.provider.Sync;
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 WifiStateTracker mWifiStateTracker;
63    private MobileDataStateTracker mMobileDataStateTracker;
64    private WifiWatchdogService mWifiWatchdogService;
65
66    private Context mContext;
67    private int mNetworkPreference;
68    private NetworkStateTracker mActiveNetwork;
69
70    private int mNumDnsEntries;
71    private static int sDnsChangeCounter;
72
73    private boolean mTestMode;
74    private static ConnectivityService sServiceInstance;
75
76    private static class ConnectivityThread extends Thread {
77        private Context mContext;
78
79        private ConnectivityThread(Context context) {
80            super("ConnectivityThread");
81            mContext = context;
82        }
83
84        @Override
85        public void run() {
86            Looper.prepare();
87            synchronized (this) {
88                sServiceInstance = new ConnectivityService(mContext);
89                notifyAll();
90            }
91            Looper.loop();
92        }
93
94        public static ConnectivityService getServiceInstance(Context context) {
95            ConnectivityThread thread = new ConnectivityThread(context);
96            thread.start();
97
98            synchronized (thread) {
99                while (sServiceInstance == null) {
100                    try {
101                        // Wait until sServiceInstance has been initialized.
102                        thread.wait();
103                    } catch (InterruptedException ignore) {
104                        Log.e(TAG,
105                            "Unexpected InterruptedException while waiting for ConnectivityService thread");
106                    }
107                }
108            }
109
110            return sServiceInstance;
111        }
112    }
113
114    public static ConnectivityService getInstance(Context context) {
115        return ConnectivityThread.getServiceInstance(context);
116    }
117
118    private ConnectivityService(Context context) {
119        if (DBG) Log.v(TAG, "ConnectivityService starting up");
120        mContext = context;
121        mNetTrackers = new NetworkStateTracker[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        mNetTrackers[ConnectivityManager.TYPE_WIFI] = mWifiStateTracker;
138
139        mMobileDataStateTracker = new MobileDataStateTracker(context, handler);
140        mNetTrackers[ConnectivityManager.TYPE_MOBILE] = mMobileDataStateTracker;
141
142        mActiveNetwork = null;
143        mNumDnsEntries = 0;
144
145        mTestMode = SystemProperties.get("cm.test.mode").equals("true")
146                && SystemProperties.get("ro.build.type").equals("eng");
147
148        for (NetworkStateTracker t : mNetTrackers)
149            t.startMonitoring();
150
151        // Constructing this starts it too
152        mWifiWatchdogService = new WifiWatchdogService(context, mWifiStateTracker);
153    }
154
155    /**
156     * Sets the preferred network.
157     * @param preference the new preference
158     */
159    public synchronized void setNetworkPreference(int preference) {
160        enforceChangePermission();
161        if (ConnectivityManager.isNetworkTypeValid(preference)) {
162            if (mNetworkPreference != preference) {
163                persistNetworkPreference(preference);
164                mNetworkPreference = preference;
165                enforcePreference();
166            }
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.Secure.putInt(cr, Settings.Secure.NETWORK_PREFERENCE, networkPreference);
178    }
179
180    private int getPersistedNetworkPreference() {
181        final ContentResolver cr = mContext.getContentResolver();
182
183        final int networkPrefSetting = Settings.Secure
184                .getInt(cr, Settings.Secure.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            netTracker.setTeardownRequested(true);
223            return true;
224        } else {
225            return false;
226        }
227    }
228
229    /**
230     * Return NetworkInfo for the active (i.e., connected) network interface.
231     * It is assumed that at most one network is active at a time. If more
232     * than one is active, it is indeterminate 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    public int stopUsingNetworkFeature(int networkType, String feature) {
297        enforceChangePermission();
298        if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
299            return -1;
300        }
301        NetworkStateTracker tracker = mNetTrackers[networkType];
302        if (tracker != null) {
303            return tracker.stopUsingNetworkFeature(feature, getCallingPid(), getCallingUid());
304        }
305        return -1;
306    }
307
308    /**
309     * Ensure that a network route exists to deliver traffic to the specified
310     * host via the specified network interface.
311     * @param networkType the type of the network over which traffic to the specified
312     * host is to be routed
313     * @param hostAddress the IP address of the host to which the route is desired
314     * @return {@code true} on success, {@code false} on failure
315     */
316    public boolean requestRouteToHost(int networkType, int hostAddress) {
317        enforceChangePermission();
318        if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
319            return false;
320        }
321        NetworkStateTracker tracker = mNetTrackers[networkType];
322        /*
323         * If there's only one connected network, and it's the one requested,
324         * then we don't have to do anything - the requested route already
325         * exists. If it's not the requested network, then it's not possible
326         * to establish the requested route. Finally, if there is more than
327         * one connected network, then we must insert an entry in the routing
328         * table.
329         */
330        if (getNumConnectedNetworks() > 1) {
331            return tracker.requestRouteToHost(hostAddress);
332        } else {
333            return tracker.getNetworkInfo().getType() == networkType;
334        }
335    }
336
337    /**
338     * @see ConnectivityManager#getBackgroundDataSetting()
339     */
340    public boolean getBackgroundDataSetting() {
341        return Settings.Secure.getInt(mContext.getContentResolver(),
342                Settings.Secure.BACKGROUND_DATA, 1) == 1;
343    }
344
345    /**
346     * @see ConnectivityManager#setBackgroundDataSetting(boolean)
347     */
348    public void setBackgroundDataSetting(boolean allowBackgroundDataUsage) {
349        mContext.enforceCallingOrSelfPermission(
350                android.Manifest.permission.CHANGE_BACKGROUND_DATA_SETTING,
351                "ConnectivityService");
352
353        if (getBackgroundDataSetting() == allowBackgroundDataUsage) return;
354
355        Settings.Secure.putInt(mContext.getContentResolver(),
356                Settings.Secure.BACKGROUND_DATA, allowBackgroundDataUsage ? 1 : 0);
357
358        Intent broadcast = new Intent(
359                ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED);
360        mContext.sendBroadcast(broadcast);
361    }
362
363    private int getNumConnectedNetworks() {
364        int numConnectedNets = 0;
365
366        for (NetworkStateTracker nt : mNetTrackers) {
367            if (nt.getNetworkInfo().isConnected() && !nt.isTeardownRequested()) {
368                ++numConnectedNets;
369            }
370        }
371        return numConnectedNets;
372    }
373
374    private void enforceAccessPermission() {
375        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NETWORK_STATE,
376                                          "ConnectivityService");
377    }
378
379    private void enforceChangePermission() {
380        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_NETWORK_STATE,
381                                          "ConnectivityService");
382
383    }
384
385    /**
386     * Handle a {@code DISCONNECTED} event. If this pertains to the non-active network,
387     * we ignore it. If it is for the active network, we send out a broadcast.
388     * But first, we check whether it might be possible to connect to a different
389     * network.
390     * @param info the {@code NetworkInfo} for the network
391     */
392    private void handleDisconnect(NetworkInfo info) {
393
394        if (DBG) Log.v(TAG, "Handle DISCONNECT for " + info.getTypeName());
395
396        mNetTrackers[info.getType()].setTeardownRequested(false);
397        /*
398         * If the disconnected network is not the active one, then don't report
399         * this as a loss of connectivity. What probably happened is that we're
400         * getting the disconnect for a network that we explicitly disabled
401         * in accordance with network preference policies.
402         */
403        if (mActiveNetwork == null ||  info.getType() != mActiveNetwork.getNetworkInfo().getType())
404            return;
405
406        NetworkStateTracker newNet;
407        if (info.getType() == ConnectivityManager.TYPE_MOBILE) {
408            newNet = mWifiStateTracker;
409        } else /* info().getType() == TYPE_WIFI */ {
410            newNet = mMobileDataStateTracker;
411        }
412
413        /**
414         * See if the other network is available to fail over to.
415         * If is not available, we enable it anyway, so that it
416         * will be able to connect when it does become available,
417         * but we report a total loss of connectivity rather than
418         * report that we are attempting to fail over.
419         */
420        NetworkInfo switchTo = null;
421        if (newNet.isAvailable()) {
422            mActiveNetwork = newNet;
423            switchTo = newNet.getNetworkInfo();
424            switchTo.setFailover(true);
425            if (!switchTo.isConnectedOrConnecting()) {
426                newNet.reconnect();
427            }
428        } else {
429            newNet.reconnect();
430        }
431
432        boolean otherNetworkConnected = false;
433        Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
434        intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
435        if (info.isFailover()) {
436            intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
437            info.setFailover(false);
438        }
439        if (info.getReason() != null) {
440            intent.putExtra(ConnectivityManager.EXTRA_REASON, info.getReason());
441        }
442        if (info.getExtraInfo() != null) {
443            intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, info.getExtraInfo());
444        }
445        if (switchTo != null) {
446            otherNetworkConnected = switchTo.isConnected();
447            if (DBG) {
448                if (otherNetworkConnected) {
449                    Log.v(TAG, "Switching to already connected " + switchTo.getTypeName());
450                } else {
451                    Log.v(TAG, "Attempting to switch to " + switchTo.getTypeName());
452                }
453            }
454            intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO, switchTo);
455        } else {
456            intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
457        }
458        if (DBG) Log.v(TAG, "Sending DISCONNECT bcast for " + info.getTypeName() +
459                (switchTo == null ? "" : " other=" + switchTo.getTypeName()));
460
461        mContext.sendStickyBroadcast(intent);
462        /*
463         * If the failover network is already connected, then immediately send out
464         * a followup broadcast indicating successful failover
465         */
466        if (switchTo != null && otherNetworkConnected)
467            sendConnectedBroadcast(switchTo);
468    }
469
470    private void sendConnectedBroadcast(NetworkInfo info) {
471        Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
472        intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
473        if (info.isFailover()) {
474            intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
475            info.setFailover(false);
476        }
477        if (info.getReason() != null) {
478            intent.putExtra(ConnectivityManager.EXTRA_REASON, info.getReason());
479        }
480        if (info.getExtraInfo() != null) {
481            intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, info.getExtraInfo());
482        }
483        mContext.sendStickyBroadcast(intent);
484    }
485
486    /**
487     * Called when an attempt to fail over to another network has failed.
488     * @param info the {@link NetworkInfo} for the failed network
489     */
490    private void handleConnectionFailure(NetworkInfo info) {
491        mNetTrackers[info.getType()].setTeardownRequested(false);
492        if (getActiveNetworkInfo() == null) {
493            String reason = info.getReason();
494            String extraInfo = info.getExtraInfo();
495
496            if (DBG) {
497                String reasonText;
498                if (reason == null) {
499                    reasonText = ".";
500                } else {
501                    reasonText = " (" + reason + ").";
502                }
503                Log.v(TAG, "Attempt to connect to " + info.getTypeName() + " failed" + reasonText);
504            }
505
506            Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
507            intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
508            intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
509            if (reason != null) {
510                intent.putExtra(ConnectivityManager.EXTRA_REASON, reason);
511            }
512            if (extraInfo != null) {
513                intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, extraInfo);
514            }
515            if (info.isFailover()) {
516                intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
517                info.setFailover(false);
518            }
519            mContext.sendStickyBroadcast(intent);
520        }
521    }
522
523    private void handleConnect(NetworkInfo info) {
524        if (DBG) Log.v(TAG, "Handle CONNECT for " + info.getTypeName());
525
526        // snapshot isFailover, because sendConnectedBroadcast() resets it
527        boolean isFailover = info.isFailover();
528        NetworkStateTracker thisNet = mNetTrackers[info.getType()];
529        NetworkStateTracker deadnet = null;
530        NetworkStateTracker otherNet;
531        if (info.getType() == ConnectivityManager.TYPE_MOBILE) {
532            otherNet = mWifiStateTracker;
533        } else /* info().getType() == TYPE_WIFI */ {
534            otherNet = mMobileDataStateTracker;
535        }
536        /*
537         * Check policy to see whether we are connected to a non-preferred
538         * network that now needs to be torn down.
539         */
540        NetworkInfo wifiInfo = mWifiStateTracker.getNetworkInfo();
541        NetworkInfo mobileInfo = mMobileDataStateTracker.getNetworkInfo();
542        if (wifiInfo.isConnected() && mobileInfo.isConnected()) {
543            if (mNetworkPreference == ConnectivityManager.TYPE_WIFI)
544                deadnet = mMobileDataStateTracker;
545            else
546                deadnet = mWifiStateTracker;
547        }
548
549        boolean toredown = false;
550        thisNet.setTeardownRequested(false);
551        if (!mTestMode && deadnet != null) {
552            if (DBG) Log.v(TAG, "Policy requires " +
553                  deadnet.getNetworkInfo().getTypeName() + " teardown");
554            toredown = teardown(deadnet);
555            if (DBG && !toredown) {
556                Log.d(TAG, "Network declined teardown request");
557            }
558        }
559
560        /*
561         * Note that if toredown is true, deadnet cannot be null, so there is
562         * no danger of a null pointer exception here..
563         */
564        if (!toredown || deadnet.getNetworkInfo().getType() != info.getType()) {
565            mActiveNetwork = thisNet;
566            if (DBG) Log.v(TAG, "Sending CONNECT bcast for " + info.getTypeName());
567            thisNet.updateNetworkSettings();
568            sendConnectedBroadcast(info);
569            if (isFailover) {
570                otherNet.releaseWakeLock();
571            }
572        } else {
573            if (DBG) Log.v(TAG, "Not broadcasting CONNECT_ACTION to torn down network " +
574                info.getTypeName());
575        }
576    }
577
578    private void handleScanResultsAvailable(NetworkInfo info) {
579        int networkType = info.getType();
580        if (networkType != ConnectivityManager.TYPE_WIFI) {
581            if (DBG) Log.v(TAG, "Got ScanResultsAvailable for " + info.getTypeName() + " network."
582                + " Don't know how to handle.");
583        }
584
585        mNetTrackers[networkType].interpretScanResultsAvailable();
586    }
587
588    private void handleNotificationChange(boolean visible, int id, Notification notification) {
589        NotificationManager notificationManager = (NotificationManager) mContext
590                .getSystemService(Context.NOTIFICATION_SERVICE);
591
592        if (visible) {
593            notificationManager.notify(id, notification);
594        } else {
595            notificationManager.cancel(id);
596        }
597    }
598
599    /**
600     * After any kind of change in the connectivity state of any network,
601     * make sure that anything that depends on the connectivity state of
602     * more than one network is set up correctly. We're mainly concerned
603     * with making sure that the list of DNS servers is set up  according
604     * to which networks are connected, and ensuring that the right routing
605     * table entries exist.
606     */
607    private void handleConnectivityChange() {
608        /*
609         * If both mobile and wifi are enabled, add the host routes that
610         * will allow MMS traffic to pass on the mobile network. But
611         * remove the default route for the mobile network, so that there
612         * will be only one default route, to ensure that all traffic
613         * except MMS will travel via Wi-Fi.
614         */
615        int numConnectedNets = handleConfigurationChange();
616        if (numConnectedNets > 1) {
617            mMobileDataStateTracker.addPrivateRoutes();
618            mMobileDataStateTracker.removeDefaultRoute();
619        } else if (mMobileDataStateTracker.getNetworkInfo().isConnected()) {
620            mMobileDataStateTracker.removePrivateRoutes();
621            mMobileDataStateTracker.restoreDefaultRoute();
622        }
623    }
624
625    private int handleConfigurationChange() {
626        /*
627         * Set DNS properties. Always put Wi-Fi entries at the front of
628         * the list if it is active.
629         */
630        int index = 1;
631        String lastDns = "";
632        int numConnectedNets = 0;
633        int incrValue = ConnectivityManager.TYPE_MOBILE - ConnectivityManager.TYPE_WIFI;
634        int stopValue = ConnectivityManager.TYPE_MOBILE + incrValue;
635
636        for (int netType = ConnectivityManager.TYPE_WIFI; netType != stopValue; netType += incrValue) {
637            NetworkStateTracker nt = mNetTrackers[netType];
638            if (nt.getNetworkInfo().isConnected() && !nt.isTeardownRequested()) {
639                ++numConnectedNets;
640                String[] dnsList = nt.getNameServers();
641                for (int i = 0; i < dnsList.length && dnsList[i] != null; i++) {
642                    // skip duplicate entries
643                    if (!dnsList[i].equals(lastDns)) {
644                        SystemProperties.set("net.dns" + index++, dnsList[i]);
645                        lastDns = dnsList[i];
646                    }
647                }
648            }
649        }
650        // Null out any DNS properties that are no longer used
651        for (int i = index; i <= mNumDnsEntries; i++) {
652            SystemProperties.set("net.dns" + i, "");
653        }
654        mNumDnsEntries = index - 1;
655        // Notify the name resolver library of the change
656        SystemProperties.set("net.dnschange", String.valueOf(sDnsChangeCounter++));
657        return numConnectedNets;
658    }
659
660    @Override
661    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
662        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
663                != PackageManager.PERMISSION_GRANTED) {
664            pw.println("Permission Denial: can't dump ConnectivityService from from pid="
665                    + Binder.getCallingPid()
666                    + ", uid=" + Binder.getCallingUid());
667            return;
668        }
669        if (mActiveNetwork == null) {
670            pw.println("No active network");
671        } else {
672            pw.println("Active network: " + mActiveNetwork.getNetworkInfo().getTypeName());
673        }
674        pw.println();
675        for (NetworkStateTracker nst : mNetTrackers) {
676            pw.println(nst.getNetworkInfo());
677            pw.println(nst);
678            pw.println();
679        }
680    }
681
682    private class MyHandler extends Handler {
683        @Override
684        public void handleMessage(Message msg) {
685            NetworkInfo info;
686            switch (msg.what) {
687                case NetworkStateTracker.EVENT_STATE_CHANGED:
688                    info = (NetworkInfo) msg.obj;
689                    if (DBG) Log.v(TAG, "ConnectivityChange for " + info.getTypeName() + ": " +
690                            info.getState() + "/" + info.getDetailedState());
691
692                    // Connectivity state changed:
693                    // [31-13] Reserved for future use
694                    // [12-9] Network subtype (for mobile network, as defined by TelephonyManager)
695                    // [8-3] Detailed state ordinal (as defined by NetworkInfo.DetailedState)
696                    // [2-0] Network type (as defined by ConnectivityManager)
697                    int eventLogParam = (info.getType() & 0x7) |
698                            ((info.getDetailedState().ordinal() & 0x3f) << 3) |
699                            (info.getSubtype() << 9);
700                    EventLog.writeEvent(EVENTLOG_CONNECTIVITY_STATE_CHANGED, eventLogParam);
701
702                    if (info.getDetailedState() == NetworkInfo.DetailedState.FAILED) {
703                        handleConnectionFailure(info);
704                    } else if (info.getState() == NetworkInfo.State.DISCONNECTED) {
705                        handleDisconnect(info);
706                    } else if (info.getState() == NetworkInfo.State.SUSPENDED) {
707                        // TODO: need to think this over.
708                        // the logic here is, handle SUSPENDED the same as DISCONNECTED. The
709                        // only difference being we are broadcasting an intent with NetworkInfo
710                        // that's suspended. This allows the applications an opportunity to
711                        // handle DISCONNECTED and SUSPENDED differently, or not.
712                        handleDisconnect(info);
713                    } else if (info.getState() == NetworkInfo.State.CONNECTED) {
714                        handleConnect(info);
715                    }
716                    handleConnectivityChange();
717                    break;
718
719                case NetworkStateTracker.EVENT_SCAN_RESULTS_AVAILABLE:
720                    info = (NetworkInfo) msg.obj;
721                    handleScanResultsAvailable(info);
722                    break;
723
724                case NetworkStateTracker.EVENT_NOTIFICATION_CHANGED:
725                    handleNotificationChange(msg.arg1 == 1, msg.arg2, (Notification) msg.obj);
726
727                case NetworkStateTracker.EVENT_CONFIGURATION_CHANGED:
728                    handleConfigurationChange();
729                    break;
730
731                case NetworkStateTracker.EVENT_ROAMING_CHANGED:
732                    // fill me in
733                    break;
734
735                case NetworkStateTracker.EVENT_NETWORK_SUBTYPE_CHANGED:
736                    // fill me in
737                    break;
738            }
739        }
740    }
741}
742