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