ConnectivityService.java revision 42acef37339afe6ac608c842f1637870ee9c4f6c
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.IBinder;
34import android.os.Looper;
35import android.os.Message;
36import android.os.RemoteException;
37import android.os.ServiceManager;
38import android.os.SystemProperties;
39import android.provider.Settings;
40import android.text.TextUtils;
41import android.util.EventLog;
42import android.util.Log;
43
44import com.android.internal.telephony.Phone;
45
46import java.io.FileDescriptor;
47import java.io.PrintWriter;
48import java.util.ArrayList;
49import java.util.List;
50
51/**
52 * @hide
53 */
54public class ConnectivityService extends IConnectivityManager.Stub {
55
56    private static final boolean DBG = true;
57    private static final String TAG = "ConnectivityService";
58
59    // Event log tags (must be in sync with event-log-tags)
60    private static final int EVENTLOG_CONNECTIVITY_STATE_CHANGED = 50020;
61
62    // how long to wait before switching back to a radio's default network
63    private static final int RESTORE_DEFAULT_NETWORK_DELAY = 1 * 60 * 1000;
64    // system property that can override the above value
65    private static final String NETWORK_RESTORE_DELAY_PROP_NAME =
66            "android.telephony.apn-restore";
67
68    /**
69     * Sometimes we want to refer to the individual network state
70     * trackers separately, and sometimes we just want to treat them
71     * abstractly.
72     */
73    private NetworkStateTracker mNetTrackers[];
74
75    /**
76     * A per Net list of the PID's that requested access to the net
77     * used both as a refcount and for per-PID DNS selection
78     */
79    private List mNetRequestersPids[];
80
81    private WifiWatchdogService mWifiWatchdogService;
82
83    // priority order of the nettrackers
84    // (excluding dynamically set mNetworkPreference)
85    // TODO - move mNetworkTypePreference into this
86    private int[] mPriorityList;
87
88    private Context mContext;
89    private int mNetworkPreference;
90    private int mActiveDefaultNetwork = -1;
91
92    private int mNumDnsEntries;
93
94    private boolean mTestMode;
95    private static ConnectivityService sServiceInstance;
96
97    private Handler mHandler;
98
99    // list of DeathRecipients used to make sure features are turned off when
100    // a process dies
101    private List mFeatureUsers;
102
103    private class NetworkAttributes {
104        /**
105         * Class for holding settings read from resources.
106         */
107        public String mName;
108        public int mType;
109        public int mRadio;
110        public int mPriority;
111        public NetworkAttributes(String init) {
112            String fragments[] = init.split(",");
113            mName = fragments[0].toLowerCase();
114            if (fragments[1].toLowerCase().equals("wifi")) {
115                mRadio = ConnectivityManager.TYPE_WIFI;
116            } else {
117                mRadio = ConnectivityManager.TYPE_MOBILE;
118            }
119            if (mName.equals("default")) {
120                mType = mRadio;
121            } else if (mName.equals("mms")) {
122                mType = ConnectivityManager.TYPE_MOBILE_MMS;
123            } else if (mName.equals("supl")) {
124                mType = ConnectivityManager.TYPE_MOBILE_SUPL;
125            } else if (mName.equals("dun")) {
126                mType = ConnectivityManager.TYPE_MOBILE_DUN;
127            } else if (mName.equals("hipri")) {
128                mType = ConnectivityManager.TYPE_MOBILE_HIPRI;
129            }
130            mPriority = Integer.parseInt(fragments[2]);
131        }
132        public boolean isDefault() {
133            return (mType == mRadio);
134        }
135    }
136    NetworkAttributes[] mNetAttributes;
137
138    private class RadioAttributes {
139        public String mName;
140        public int mPriority;
141        public int mSimultaneity;
142        public int mType;
143        public RadioAttributes(String init) {
144            String fragments[] = init.split(",");
145            mName = fragments[0].toLowerCase();
146            mPriority = Integer.parseInt(fragments[1]);
147            mSimultaneity = Integer.parseInt(fragments[2]);
148            if (mName.equals("wifi")) {
149                mType = ConnectivityManager.TYPE_WIFI;
150            } else {
151                mType = ConnectivityManager.TYPE_MOBILE;
152            }
153        }
154    }
155    RadioAttributes[] mRadioAttributes;
156
157    private static class ConnectivityThread extends Thread {
158        private Context mContext;
159
160        private ConnectivityThread(Context context) {
161            super("ConnectivityThread");
162            mContext = context;
163        }
164
165        @Override
166        public void run() {
167            Looper.prepare();
168            synchronized (this) {
169                sServiceInstance = new ConnectivityService(mContext);
170                notifyAll();
171            }
172            Looper.loop();
173        }
174
175        public static ConnectivityService getServiceInstance(Context context) {
176            ConnectivityThread thread = new ConnectivityThread(context);
177            thread.start();
178
179            synchronized (thread) {
180                while (sServiceInstance == null) {
181                    try {
182                        // Wait until sServiceInstance has been initialized.
183                        thread.wait();
184                    } catch (InterruptedException ignore) {
185                        Log.e(TAG,
186                            "Unexpected InterruptedException while waiting"+
187                            " for ConnectivityService thread");
188                    }
189                }
190            }
191
192            return sServiceInstance;
193        }
194    }
195
196    public static ConnectivityService getInstance(Context context) {
197        return ConnectivityThread.getServiceInstance(context);
198    }
199
200    private ConnectivityService(Context context) {
201        if (DBG) Log.v(TAG, "ConnectivityService starting up");
202        mContext = context;
203        mNetTrackers = new NetworkStateTracker[
204                ConnectivityManager.MAX_NETWORK_TYPE+1];
205        mHandler = new MyHandler();
206
207        mNetworkPreference = getPersistedNetworkPreference();
208
209        // Load device network attributes from resources
210        mNetAttributes = new NetworkAttributes[
211                ConnectivityManager.MAX_NETWORK_TYPE+1];
212        mRadioAttributes = new RadioAttributes[
213                ConnectivityManager.MAX_RADIO_TYPE+1];
214        String[] naStrings = context.getResources().getStringArray(
215                com.android.internal.R.array.networkAttributes);
216        // TODO - what if the setting has gaps/unknown types?
217        for (String a : naStrings) {
218            NetworkAttributes n = new NetworkAttributes(a);
219            mNetAttributes[n.mType] = n;
220        }
221        String[] raStrings = context.getResources().getStringArray(
222                com.android.internal.R.array.radioAttributes);
223        for (String a : raStrings) {
224            RadioAttributes r = new RadioAttributes(a);
225            mRadioAttributes[r.mType] = r;
226        }
227
228        // high priority first
229        mPriorityList = new int[naStrings.length];
230        {
231            int priority = 0; //lowest
232            int nextPos = naStrings.length-1;
233            while (nextPos>-1) {
234                for (int i = 0; i < mNetAttributes.length; i++) {
235                    if(mNetAttributes[i].mPriority == priority) {
236                        mPriorityList[nextPos--] = i;
237                    }
238                }
239                priority++;
240            }
241        }
242
243        mNetRequestersPids =
244                new ArrayList[ConnectivityManager.MAX_NETWORK_TYPE+1];
245        for (int i=0; i<=ConnectivityManager.MAX_NETWORK_TYPE; i++) {
246            mNetRequestersPids[i] = new ArrayList();
247        }
248
249        mFeatureUsers = new ArrayList();
250
251        /*
252         * Create the network state trackers for Wi-Fi and mobile
253         * data. Maybe this could be done with a factory class,
254         * but it's not clear that it's worth it, given that
255         * the number of different network types is not going
256         * to change very often.
257         */
258        if (DBG) Log.v(TAG, "Starting Wifi Service.");
259        WifiStateTracker wst = new WifiStateTracker(context, mHandler);
260        WifiService wifiService = new WifiService(context, wst);
261        ServiceManager.addService(Context.WIFI_SERVICE, wifiService);
262        mNetTrackers[ConnectivityManager.TYPE_WIFI] = wst;
263
264        mNetTrackers[ConnectivityManager.TYPE_MOBILE] =
265                new MobileDataStateTracker(context, mHandler,
266                ConnectivityManager.TYPE_MOBILE, Phone.APN_TYPE_DEFAULT,
267                "MOBILE");
268
269        mNetTrackers[ConnectivityManager.TYPE_MOBILE_MMS] =
270                new MobileDataStateTracker(context, mHandler,
271                ConnectivityManager.TYPE_MOBILE_MMS, Phone.APN_TYPE_MMS,
272                "MOBILE_MMS");
273
274        mNetTrackers[ConnectivityManager.TYPE_MOBILE_SUPL] =
275                new MobileDataStateTracker(context, mHandler,
276                ConnectivityManager.TYPE_MOBILE_SUPL, Phone.APN_TYPE_SUPL,
277                "MOBILE_SUPL");
278
279        mNetTrackers[ConnectivityManager.TYPE_MOBILE_DUN] =
280                new MobileDataStateTracker(context, mHandler,
281                ConnectivityManager.TYPE_MOBILE_DUN, Phone.APN_TYPE_DUN,
282                "MOBILE_DUN");
283
284        mNetTrackers[ConnectivityManager.TYPE_MOBILE_HIPRI] =
285                new MobileDataStateTracker(context, mHandler,
286                ConnectivityManager.TYPE_MOBILE_HIPRI, Phone.APN_TYPE_HIPRI,
287                "MOBILE_HIPRI");
288
289        mNumDnsEntries = 0;
290
291        mTestMode = SystemProperties.get("cm.test.mode").equals("true")
292                && SystemProperties.get("ro.build.type").equals("eng");
293
294        for (NetworkStateTracker t : mNetTrackers)
295            t.startMonitoring();
296
297        // Constructing this starts it too
298        mWifiWatchdogService = new WifiWatchdogService(context, wst);
299    }
300
301    /**
302     * Sets the preferred network.
303     * @param preference the new preference
304     */
305    public synchronized void setNetworkPreference(int preference) {
306        enforceChangePermission();
307        if (ConnectivityManager.isNetworkTypeValid(preference) &&
308                mNetAttributes[preference].isDefault()) {
309            if (mNetworkPreference != preference) {
310                persistNetworkPreference(preference);
311                mNetworkPreference = preference;
312                enforcePreference();
313            }
314        }
315    }
316
317    public int getNetworkPreference() {
318        enforceAccessPermission();
319        return mNetworkPreference;
320    }
321
322    private void persistNetworkPreference(int networkPreference) {
323        final ContentResolver cr = mContext.getContentResolver();
324        Settings.Secure.putInt(cr, Settings.Secure.NETWORK_PREFERENCE,
325                networkPreference);
326    }
327
328    private int getPersistedNetworkPreference() {
329        final ContentResolver cr = mContext.getContentResolver();
330
331        final int networkPrefSetting = Settings.Secure
332                .getInt(cr, Settings.Secure.NETWORK_PREFERENCE, -1);
333        if (networkPrefSetting != -1) {
334            return networkPrefSetting;
335        }
336
337        return ConnectivityManager.DEFAULT_NETWORK_PREFERENCE;
338    }
339
340    /**
341     * Make the state of network connectivity conform to the preference settings
342     * In this method, we only tear down a non-preferred network. Establishing
343     * a connection to the preferred network is taken care of when we handle
344     * the disconnect event from the non-preferred network
345     * (see {@link #handleDisconnect(NetworkInfo)}).
346     */
347    private void enforcePreference() {
348        if (mNetTrackers[mNetworkPreference].getNetworkInfo().isConnected())
349            return;
350
351        if (!mNetTrackers[mNetworkPreference].isAvailable())
352            return;
353
354        for (int t=0; t <= ConnectivityManager.MAX_RADIO_TYPE; t++) {
355            if (t != mNetworkPreference &&
356                    mNetTrackers[t].getNetworkInfo().isConnected()) {
357                teardown(mNetTrackers[t]);
358            }
359        }
360    }
361
362    private boolean teardown(NetworkStateTracker netTracker) {
363        if (netTracker.teardown()) {
364            netTracker.setTeardownRequested(true);
365            return true;
366        } else {
367            return false;
368        }
369    }
370
371    /**
372     * Return NetworkInfo for the active (i.e., connected) network interface.
373     * It is assumed that at most one network is active at a time. If more
374     * than one is active, it is indeterminate which will be returned.
375     * @return the info for the active network, or {@code null} if none is
376     * active
377     */
378    public NetworkInfo getActiveNetworkInfo() {
379        enforceAccessPermission();
380        for (int type=0; type <= ConnectivityManager.MAX_NETWORK_TYPE; type++) {
381            if (!mNetAttributes[type].isDefault()) {
382                continue;
383            }
384            NetworkStateTracker t = mNetTrackers[type];
385            NetworkInfo info = t.getNetworkInfo();
386            if (info.isConnected()) {
387                if (DBG && type != mActiveDefaultNetwork) Log.e(TAG,
388                        "connected default network is not " +
389                        "mActiveDefaultNetwork!");
390                return info;
391            }
392        }
393        return null;
394    }
395
396    public NetworkInfo getNetworkInfo(int networkType) {
397        enforceAccessPermission();
398        if (ConnectivityManager.isNetworkTypeValid(networkType)) {
399            NetworkStateTracker t = mNetTrackers[networkType];
400            if (t != null)
401                return t.getNetworkInfo();
402        }
403        return null;
404    }
405
406    public NetworkInfo[] getAllNetworkInfo() {
407        enforceAccessPermission();
408        NetworkInfo[] result = new NetworkInfo[mNetTrackers.length];
409        int i = 0;
410        for (NetworkStateTracker t : mNetTrackers) {
411            result[i++] = t.getNetworkInfo();
412        }
413        return result;
414    }
415
416    public boolean setRadios(boolean turnOn) {
417        boolean result = true;
418        enforceChangePermission();
419        for (NetworkStateTracker t : mNetTrackers) {
420            result = t.setRadio(turnOn) && result;
421        }
422        return result;
423    }
424
425    public boolean setRadio(int netType, boolean turnOn) {
426        enforceChangePermission();
427        if (!ConnectivityManager.isNetworkTypeValid(netType)) {
428            return false;
429        }
430        NetworkStateTracker tracker = mNetTrackers[netType];
431        return tracker != null && tracker.setRadio(turnOn);
432    }
433
434    private class FeatureUser implements IBinder.DeathRecipient {
435        int mNetworkType;
436        String mFeature;
437        IBinder mBinder;
438        int mPid;
439        int mUid;
440
441        FeatureUser(int type, String feature, IBinder binder) {
442            super();
443            mNetworkType = type;
444            mFeature = feature;
445            mBinder = binder;
446            mPid = getCallingPid();
447            mUid = getCallingUid();
448            try {
449                mBinder.linkToDeath(this, 0);
450            } catch (RemoteException e) {
451                binderDied();
452            }
453        }
454
455        void unlinkDeathRecipient() {
456            mBinder.unlinkToDeath(this, 0);
457        }
458
459        public void binderDied() {
460            Log.d(TAG, "ConnectivityService FeatureUser binderDied(" +
461                    mNetworkType + ", " + mFeature + ", " + mBinder);
462            stopUsingNetworkFeature(mNetworkType, mFeature, mPid, mUid);
463        }
464
465    }
466
467    public int startUsingNetworkFeature(int networkType, String feature,
468            IBinder binder) {
469        if (DBG) {
470            Log.d(TAG, "startUsingNetworkFeature for net " + networkType +
471                    ": " + feature);
472        }
473        enforceChangePermission();
474        if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
475            return Phone.APN_REQUEST_FAILED;
476        }
477
478        synchronized (mFeatureUsers) {
479            mFeatureUsers.add(new FeatureUser(networkType, feature, binder));
480        }
481
482        // TODO - move this into the MobileDataStateTracker
483        int usedNetworkType = networkType;
484        if(networkType == ConnectivityManager.TYPE_MOBILE) {
485            if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_MMS)) {
486                usedNetworkType = ConnectivityManager.TYPE_MOBILE_MMS;
487            } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_SUPL)) {
488                usedNetworkType = ConnectivityManager.TYPE_MOBILE_SUPL;
489            } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_DUN)) {
490                usedNetworkType = ConnectivityManager.TYPE_MOBILE_DUN;
491            } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_HIPRI)) {
492                usedNetworkType = ConnectivityManager.TYPE_MOBILE_HIPRI;
493            }
494        }
495        NetworkStateTracker network = mNetTrackers[usedNetworkType];
496        if (network != null) {
497            if (usedNetworkType != networkType) {
498                Integer currentPid = new Integer(getCallingPid());
499
500                NetworkStateTracker radio = mNetTrackers[networkType];
501                NetworkInfo ni = network.getNetworkInfo();
502
503                if (ni.isAvailable() == false) {
504                    if (DBG) Log.d(TAG, "special network not available");
505                    return Phone.APN_TYPE_NOT_AVAILABLE;
506                }
507
508                if (!mNetRequestersPids[usedNetworkType].contains(currentPid)) {
509                    // this gets used for per-pid dns when connected
510                    mNetRequestersPids[usedNetworkType].add(currentPid);
511                }
512
513                if (ni.isConnectedOrConnecting() == true) {
514                    if (ni.isConnected() == true) {
515                        // add the pid-specific dns
516                        handleDnsConfigurationChange();
517                        if (DBG) Log.d(TAG, "special network already active");
518                        return Phone.APN_ALREADY_ACTIVE;
519                    }
520                    if (DBG) Log.d(TAG, "special network already connecting");
521                    return Phone.APN_REQUEST_STARTED;
522                }
523
524                // check if the radio in play can make another contact
525                // assume if cannot for now
526
527                // since we have to drop the default on this radio, setup
528                // an automatic event to switch back
529                if(mHandler.hasMessages(NetworkStateTracker.
530                        EVENT_RESTORE_DEFAULT_NETWORK, radio) ||
531                        radio.getNetworkInfo().isConnectedOrConnecting()) {
532                    mHandler.removeMessages(NetworkStateTracker.
533                            EVENT_RESTORE_DEFAULT_NETWORK,
534                            radio);
535                    mHandler.sendMessageDelayed(mHandler.obtainMessage(
536                            NetworkStateTracker.EVENT_RESTORE_DEFAULT_NETWORK,
537                            radio), getRestoreDefaultNetworkDelay());
538                }
539                if (DBG) Log.d(TAG, "reconnecting to special network");
540                network.reconnect();
541                return Phone.APN_REQUEST_STARTED;
542            } else {
543                return network.startUsingNetworkFeature(feature,
544                        getCallingPid(), getCallingUid());
545            }
546        }
547        return Phone.APN_TYPE_NOT_AVAILABLE;
548    }
549
550    public int stopUsingNetworkFeature(int networkType, String feature) {
551        return stopUsingNetworkFeature(networkType, feature, getCallingPid(),
552                getCallingUid());
553    }
554
555    private int stopUsingNetworkFeature(int networkType, String feature,
556            int pid, int uid) {
557        if (DBG) {
558            Log.d(TAG, "stopUsingNetworkFeature for net " + networkType +
559                    ": " + feature);
560        }
561        enforceChangePermission();
562        if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
563            return -1;
564        }
565
566        synchronized (mFeatureUsers) {
567            for (int i=0; i < mFeatureUsers.size(); i++) {
568                FeatureUser u = (FeatureUser)mFeatureUsers.get(i);
569                if (uid == u.mUid && pid == u.mPid &&
570                        networkType == u.mNetworkType &&
571                        TextUtils.equals(feature, u.mFeature)) {
572                    u.unlinkDeathRecipient();
573                    mFeatureUsers.remove(i);
574                    break;
575                }
576            }
577        }
578
579        // TODO - move to MobileDataStateTracker
580        int usedNetworkType = networkType;
581        if (networkType == ConnectivityManager.TYPE_MOBILE) {
582            if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_MMS)) {
583                usedNetworkType = ConnectivityManager.TYPE_MOBILE_MMS;
584            } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_SUPL)) {
585                usedNetworkType = ConnectivityManager.TYPE_MOBILE_SUPL;
586            } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_DUN)) {
587                usedNetworkType = ConnectivityManager.TYPE_MOBILE_DUN;
588            } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_HIPRI)) {
589                usedNetworkType = ConnectivityManager.TYPE_MOBILE_HIPRI;
590            }
591        }
592        NetworkStateTracker tracker =  mNetTrackers[usedNetworkType];
593        if(usedNetworkType != networkType) {
594            Integer currentPid = new Integer(pid);
595            if (mNetRequestersPids[usedNetworkType].remove(currentPid)) {
596                reassessPidDns(pid, true);
597            }
598            if (mNetRequestersPids[usedNetworkType].size() != 0) {
599                if (DBG) Log.d(TAG, "not tearing down special network - " +
600                        "others still using it");
601                return 1;
602            }
603
604            tracker.teardown();
605            NetworkStateTracker radio = mNetTrackers[networkType];
606            // Check if we want to revert to the default
607            if (mHandler.hasMessages(NetworkStateTracker.
608                    EVENT_RESTORE_DEFAULT_NETWORK, radio)) {
609                mHandler.removeMessages(NetworkStateTracker.
610                        EVENT_RESTORE_DEFAULT_NETWORK, radio);
611                radio.reconnect();
612            }
613            return 1;
614        } else {
615            return tracker.stopUsingNetworkFeature(feature, pid, uid);
616        }
617    }
618
619    /**
620     * Ensure that a network route exists to deliver traffic to the specified
621     * host via the specified network interface.
622     * @param networkType the type of the network over which traffic to the
623     * specified host is to be routed
624     * @param hostAddress the IP address of the host to which the route is
625     * desired
626     * @return {@code true} on success, {@code false} on failure
627     */
628    public boolean requestRouteToHost(int networkType, int hostAddress) {
629        enforceChangePermission();
630        if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
631            return false;
632        }
633        NetworkStateTracker tracker = mNetTrackers[networkType];
634        /*
635         * If there's only one connected network, and it's the one requested,
636         * then we don't have to do anything - the requested route already
637         * exists. If it's not the requested network, then it's not possible
638         * to establish the requested route. Finally, if there is more than
639         * one connected network, then we must insert an entry in the routing
640         * table.
641         */
642        if (getNumConnectedNetworks() > 1) {
643            return tracker.requestRouteToHost(hostAddress);
644        } else {
645            return (mNetAttributes[networkType].isDefault() &&
646                    tracker.getNetworkInfo().isConnected());
647        }
648    }
649
650    /**
651     * @see ConnectivityManager#getBackgroundDataSetting()
652     */
653    public boolean getBackgroundDataSetting() {
654        return Settings.Secure.getInt(mContext.getContentResolver(),
655                Settings.Secure.BACKGROUND_DATA, 1) == 1;
656    }
657
658    /**
659     * @see ConnectivityManager#setBackgroundDataSetting(boolean)
660     */
661    public void setBackgroundDataSetting(boolean allowBackgroundDataUsage) {
662        mContext.enforceCallingOrSelfPermission(
663                android.Manifest.permission.CHANGE_BACKGROUND_DATA_SETTING,
664                "ConnectivityService");
665
666        if (getBackgroundDataSetting() == allowBackgroundDataUsage) return;
667
668        Settings.Secure.putInt(mContext.getContentResolver(),
669                Settings.Secure.BACKGROUND_DATA,
670                allowBackgroundDataUsage ? 1 : 0);
671
672        Intent broadcast = new Intent(
673                ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED);
674        mContext.sendBroadcast(broadcast);
675    }
676
677    private int getNumConnectedNetworks() {
678        int numConnectedNets = 0;
679
680        for (NetworkStateTracker nt : mNetTrackers) {
681            if (nt.getNetworkInfo().isConnected() &&
682                    !nt.isTeardownRequested()) {
683                ++numConnectedNets;
684            }
685        }
686        return numConnectedNets;
687    }
688
689    private void enforceAccessPermission() {
690        mContext.enforceCallingOrSelfPermission(
691                android.Manifest.permission.ACCESS_NETWORK_STATE,
692                "ConnectivityService");
693    }
694
695    private void enforceChangePermission() {
696        mContext.enforceCallingOrSelfPermission(
697                android.Manifest.permission.CHANGE_NETWORK_STATE,
698                "ConnectivityService");
699    }
700
701    /**
702     * Handle a {@code DISCONNECTED} event. If this pertains to the non-active
703     * network, we ignore it. If it is for the active network, we send out a
704     * broadcast. But first, we check whether it might be possible to connect
705     * to a different network.
706     * @param info the {@code NetworkInfo} for the network
707     */
708    private void handleDisconnect(NetworkInfo info) {
709
710        if (DBG) Log.v(TAG, "Handle DISCONNECT for " + info.getTypeName());
711        int prevNetType = info.getType();
712
713        mNetTrackers[prevNetType].setTeardownRequested(false);
714        /*
715         * If the disconnected network is not the active one, then don't report
716         * this as a loss of connectivity. What probably happened is that we're
717         * getting the disconnect for a network that we explicitly disabled
718         * in accordance with network preference policies.
719         */
720        if (!mNetAttributes[prevNetType].isDefault()) {
721            List pids = mNetRequestersPids[prevNetType];
722            for (int i = 0; i<pids.size(); i++) {
723                Integer pid = (Integer)pids.get(i);
724                // will remove them because the net's no longer connected
725                // need to do this now as only now do we know the pids and
726                // can properly null things that are no longer referenced.
727                reassessPidDns(pid.intValue(), false);
728            }
729        }
730
731        Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
732        intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
733        if (info.isFailover()) {
734            intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
735            info.setFailover(false);
736        }
737        if (info.getReason() != null) {
738            intent.putExtra(ConnectivityManager.EXTRA_REASON, info.getReason());
739        }
740        if (info.getExtraInfo() != null) {
741            intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO,
742                    info.getExtraInfo());
743        }
744
745        /*
746         * If this is a default network, check if other defaults are available
747         * or active
748         */
749        NetworkStateTracker newNet = null;
750        if (mNetAttributes[prevNetType].isDefault()) {
751            if (DBG) Log.d(TAG, "disconnecting a default network");
752            if (mActiveDefaultNetwork == prevNetType) {
753                mActiveDefaultNetwork = -1;
754            }
755
756            int newType = -1;
757            int newPriority = -1;
758            for (int checkType=0; checkType <=
759                    ConnectivityManager.MAX_NETWORK_TYPE; checkType++) {
760                if (checkType == prevNetType) {
761                    continue;
762                }
763                if (mNetAttributes[checkType].isDefault()) {
764                    /* TODO - if we have multiple nets we could use
765                     * we may want to put more thought into which we choose
766                     */
767                    if (checkType == mNetworkPreference) {
768                        newType = checkType;
769                        break;
770                    }
771                    if (mRadioAttributes[mNetAttributes[checkType].mRadio].
772                            mPriority > newPriority) {
773                        newType = checkType;
774                        newPriority = mRadioAttributes[mNetAttributes[newType].
775                                mRadio].mPriority;
776                    }
777                }
778            }
779
780            if (newType != -1) {
781                newNet = mNetTrackers[newType];
782                /**
783                 * See if the other network is available to fail over to.
784                 * If is not available, we enable it anyway, so that it
785                 * will be able to connect when it does become available,
786                 * but we report a total loss of connectivity rather than
787                 * report that we are attempting to fail over.
788                 */
789                if (newNet.isAvailable()) {
790                    NetworkInfo switchTo = newNet.getNetworkInfo();
791                    switchTo.setFailover(true);
792                    if (!switchTo.isConnectedOrConnecting()) {
793                        newNet.reconnect();
794                    }
795                    if (DBG) {
796                        if (switchTo.isConnected()) {
797                            Log.v(TAG, "Switching to already connected " +
798                                    switchTo.getTypeName());
799                        } else {
800                            Log.v(TAG, "Attempting to switch to " +
801                                    switchTo.getTypeName());
802                        }
803                    }
804                    intent.putExtra(ConnectivityManager.
805                            EXTRA_OTHER_NETWORK_INFO, switchTo);
806                } else {
807                    newNet.reconnect();
808                }
809            } else {
810                intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY,
811                        true);
812            }
813        }
814
815        // do this before we broadcast the change
816        handleConnectivityChange();
817
818        if (DBG) Log.v(TAG, "Sending DISCONNECT bcast for " +
819                info.getTypeName() +
820                (newNet == null || !newNet.isAvailable() ? "" : " other=" +
821                newNet.getNetworkInfo().getTypeName()));
822
823        mContext.sendStickyBroadcast(intent);
824        /*
825         * If the failover network is already connected, then immediately send
826         * out a followup broadcast indicating successful failover
827         */
828        if (newNet != null && newNet.getNetworkInfo().isConnected())
829            sendConnectedBroadcast(newNet.getNetworkInfo());
830    }
831
832    private void sendConnectedBroadcast(NetworkInfo info) {
833        Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
834        intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
835        if (info.isFailover()) {
836            intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
837            info.setFailover(false);
838        }
839        if (info.getReason() != null) {
840            intent.putExtra(ConnectivityManager.EXTRA_REASON, info.getReason());
841        }
842        if (info.getExtraInfo() != null) {
843            intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO,
844                    info.getExtraInfo());
845        }
846        mContext.sendStickyBroadcast(intent);
847    }
848
849    /**
850     * Called when an attempt to fail over to another network has failed.
851     * @param info the {@link NetworkInfo} for the failed network
852     */
853    private void handleConnectionFailure(NetworkInfo info) {
854        mNetTrackers[info.getType()].setTeardownRequested(false);
855
856        String reason = info.getReason();
857        String extraInfo = info.getExtraInfo();
858
859        if (DBG) {
860            String reasonText;
861            if (reason == null) {
862                reasonText = ".";
863            } else {
864                reasonText = " (" + reason + ").";
865            }
866            Log.v(TAG, "Attempt to connect to " + info.getTypeName() +
867                    " failed" + reasonText);
868        }
869
870        Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
871        intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
872        if (getActiveNetworkInfo() == null) {
873            intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
874        }
875        if (reason != null) {
876            intent.putExtra(ConnectivityManager.EXTRA_REASON, reason);
877        }
878        if (extraInfo != null) {
879            intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, extraInfo);
880        }
881        if (info.isFailover()) {
882            intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
883            info.setFailover(false);
884        }
885        mContext.sendStickyBroadcast(intent);
886    }
887
888    private void handleConnect(NetworkInfo info) {
889        if (DBG) Log.d(TAG, "Handle CONNECT for " + info.getTypeName());
890
891        int type = info.getType();
892
893        // snapshot isFailover, because sendConnectedBroadcast() resets it
894        boolean isFailover = info.isFailover();
895        NetworkStateTracker thisNet = mNetTrackers[type];
896
897        // if this is a default net and other default is running
898        // kill the one not preferred
899        if (mNetAttributes[type].isDefault()) {
900            if (DBG) Log.d(TAG, "connecting to a default network");
901            if (mActiveDefaultNetwork != -1 && mActiveDefaultNetwork != type) {
902                if ((type != mNetworkPreference &&
903                        mNetAttributes[mActiveDefaultNetwork].mPriority >
904                        mNetAttributes[type].mPriority) ||
905                        mNetworkPreference == mActiveDefaultNetwork) {
906                        // don't accept this one
907                        if (DBG) Log.v(TAG, "Not broadcasting CONNECT_ACTION " +
908                                "to torn down network " + info.getTypeName());
909                        teardown(thisNet);
910                        return;
911                } else {
912                    // tear down the other
913                    NetworkStateTracker otherNet =
914                            mNetTrackers[mActiveDefaultNetwork];
915                    if (DBG) Log.v(TAG, "Policy requires " +
916                            otherNet.getNetworkInfo().getTypeName() +
917                            " teardown");
918                    if (!teardown(otherNet)) {
919                        Log.e(TAG, "Network declined teardown request");
920                        return;
921                    }
922                    if (isFailover) {
923                        otherNet.releaseWakeLock();
924                    }
925                }
926            }
927            mActiveDefaultNetwork = type;
928        }
929        thisNet.setTeardownRequested(false);
930        if (DBG) Log.d(TAG, "Sending CONNECT bcast for " + info.getTypeName());
931        thisNet.updateNetworkSettings();
932        handleConnectivityChange();
933        sendConnectedBroadcast(info);
934    }
935
936    private void handleScanResultsAvailable(NetworkInfo info) {
937        int networkType = info.getType();
938        if (networkType != ConnectivityManager.TYPE_WIFI) {
939            if (DBG) Log.v(TAG, "Got ScanResultsAvailable for " +
940                    info.getTypeName() + " network. Don't know how to handle.");
941        }
942
943        mNetTrackers[networkType].interpretScanResultsAvailable();
944    }
945
946    private void handleNotificationChange(boolean visible, int id,
947            Notification notification) {
948        NotificationManager notificationManager = (NotificationManager) mContext
949                .getSystemService(Context.NOTIFICATION_SERVICE);
950
951        if (visible) {
952            notificationManager.notify(id, notification);
953        } else {
954            notificationManager.cancel(id);
955        }
956    }
957
958    /**
959     * After any kind of change in the connectivity state of any network,
960     * make sure that anything that depends on the connectivity state of
961     * more than one network is set up correctly. We're mainly concerned
962     * with making sure that the list of DNS servers is set up  according
963     * to which networks are connected, and ensuring that the right routing
964     * table entries exist.
965     */
966    private void handleConnectivityChange() {
967        if (DBG) Log.d(TAG, "handleConnectivityChange");
968        /*
969         * If a non-default network is enabled, add the host routes that
970         * will allow it's DNS servers to be accessed.  Only
971         * If both mobile and wifi are enabled, add the host routes that
972         * will allow MMS traffic to pass on the mobile network. But
973         * remove the default route for the mobile network, so that there
974         * will be only one default route, to ensure that all traffic
975         * except MMS will travel via Wi-Fi.
976         */
977        handleDnsConfigurationChange();
978
979        for (int netType : mPriorityList) {
980            if (mNetTrackers[netType].getNetworkInfo().isConnected()) {
981                if (mNetAttributes[netType].isDefault()) {
982                    mNetTrackers[netType].addDefaultRoute();
983                } else {
984                    mNetTrackers[netType].addPrivateDnsRoutes();
985                }
986            } else {
987                if (mNetAttributes[netType].isDefault()) {
988                    mNetTrackers[netType].removeDefaultRoute();
989                } else {
990                    mNetTrackers[netType].removePrivateDnsRoutes();
991                }
992            }
993        }
994    }
995
996    /**
997     * Adjust the per-process dns entries (net.dns<x>.<pid>) based
998     * on the highest priority active net which this process requested.
999     * If there aren't any, clear it out
1000     */
1001    private void reassessPidDns(int myPid, boolean doBump)
1002    {
1003        if (DBG) Log.d(TAG, "reassessPidDns for pid " + myPid);
1004        for(int i : mPriorityList) {
1005            if (mNetAttributes[i].isDefault()) {
1006                continue;
1007            }
1008            NetworkStateTracker nt = mNetTrackers[i];
1009            if (nt.getNetworkInfo().isConnected() &&
1010                    !nt.isTeardownRequested()) {
1011                List pids = mNetRequestersPids[i];
1012                for (int j=0; j<pids.size(); j++) {
1013                    Integer pid = (Integer)pids.get(j);
1014                    if (pid.intValue() == myPid) {
1015                        String[] dnsList = nt.getNameServers();
1016                        writePidDns(dnsList, myPid);
1017                        if (doBump) {
1018                            bumpDns();
1019                        }
1020                        return;
1021                    }
1022                }
1023           }
1024        }
1025        // nothing found - delete
1026        for (int i = 1; ; i++) {
1027            String prop = "net.dns" + i + "." + myPid;
1028            if (SystemProperties.get(prop).length() == 0) {
1029                if (doBump) {
1030                    bumpDns();
1031                }
1032                return;
1033            }
1034            SystemProperties.set(prop, "");
1035        }
1036    }
1037
1038    private void writePidDns(String[] dnsList, int pid) {
1039        int j = 1;
1040        for (String dns : dnsList) {
1041            if (dns != null && !TextUtils.equals(dns, "0.0.0.0")) {
1042                SystemProperties.set("net.dns" + j++ + "." + pid, dns);
1043            }
1044        }
1045    }
1046
1047    private void bumpDns() {
1048        /*
1049         * Bump the property that tells the name resolver library to reread
1050         * the DNS server list from the properties.
1051         */
1052        String propVal = SystemProperties.get("net.dnschange");
1053        int n = 0;
1054        if (propVal.length() != 0) {
1055            try {
1056                n = Integer.parseInt(propVal);
1057            } catch (NumberFormatException e) {}
1058        }
1059        SystemProperties.set("net.dnschange", "" + (n+1));
1060    }
1061
1062    private void handleDnsConfigurationChange() {
1063        if (DBG) Log.d(TAG, "handleDnsConfig Change");
1064        // add default net's dns entries
1065        for (int x = mPriorityList.length-1; x>= 0; x--) {
1066            int netType = mPriorityList[x];
1067            NetworkStateTracker nt = mNetTrackers[netType];
1068            if (DBG) Log.d(TAG, " checking " + nt);
1069            if (nt != null && nt.getNetworkInfo().isConnected() &&
1070                    !nt.isTeardownRequested()) {
1071                if (DBG) Log.d(TAG, "  connected");
1072                String[] dnsList = nt.getNameServers();
1073                if (mNetAttributes[netType].isDefault()) {
1074                    int j = 1;
1075                    for (String dns : dnsList) {
1076                        if (dns != null && !TextUtils.equals(dns, "0.0.0.0")) {
1077                            SystemProperties.set("net.dns" + j++, dns);
1078                        }
1079                    }
1080                    for (int k=j ; k<mNumDnsEntries; k++) {
1081                        SystemProperties.set("net.dns" + j, "");
1082                    }
1083                    mNumDnsEntries = j;
1084                } else {
1085                    // set per-pid dns for attached secondary nets
1086                    List pids = mNetRequestersPids[netType];
1087                    for (int y=0; y< pids.size(); y++) {
1088                        Integer pid = (Integer)pids.get(y);
1089                        writePidDns(dnsList, pid.intValue());
1090                    }
1091                }
1092            }
1093        }
1094
1095        bumpDns();
1096    }
1097
1098    private int getRestoreDefaultNetworkDelay() {
1099        String restoreDefaultNetworkDelayStr = SystemProperties.get(
1100                NETWORK_RESTORE_DELAY_PROP_NAME);
1101        if(restoreDefaultNetworkDelayStr != null &&
1102                restoreDefaultNetworkDelayStr.length() != 0) {
1103            try {
1104                return Integer.valueOf(restoreDefaultNetworkDelayStr);
1105            } catch (NumberFormatException e) {
1106            }
1107        }
1108        return RESTORE_DEFAULT_NETWORK_DELAY;
1109    }
1110
1111    @Override
1112    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1113        if (mContext.checkCallingOrSelfPermission(
1114                android.Manifest.permission.DUMP)
1115                != PackageManager.PERMISSION_GRANTED) {
1116            pw.println("Permission Denial: can't dump ConnectivityService " +
1117                    "from from pid=" + Binder.getCallingPid() + ", uid=" +
1118                    Binder.getCallingUid());
1119            return;
1120        }
1121        pw.println();
1122        for (NetworkStateTracker nst : mNetTrackers) {
1123            if (nst.getNetworkInfo().isConnected()) {
1124                pw.println("Active network: " + nst.getNetworkInfo().
1125                        getTypeName());
1126            }
1127            pw.println(nst.getNetworkInfo());
1128            pw.println(nst);
1129            pw.println();
1130        }
1131    }
1132
1133    // must be stateless - things change under us.
1134    private class MyHandler extends Handler {
1135        @Override
1136        public void handleMessage(Message msg) {
1137            NetworkInfo info;
1138            switch (msg.what) {
1139                case NetworkStateTracker.EVENT_STATE_CHANGED:
1140                    info = (NetworkInfo) msg.obj;
1141                    if (DBG) Log.d(TAG, "ConnectivityChange for " +
1142                            info.getTypeName() + ": " +
1143                            info.getState() + "/" + info.getDetailedState());
1144
1145                    // Connectivity state changed:
1146                    // [31-13] Reserved for future use
1147                    // [12-9] Network subtype (for mobile network, as defined
1148                    //         by TelephonyManager)
1149                    // [8-3] Detailed state ordinal (as defined by
1150                    //         NetworkInfo.DetailedState)
1151                    // [2-0] Network type (as defined by ConnectivityManager)
1152                    int eventLogParam = (info.getType() & 0x7) |
1153                            ((info.getDetailedState().ordinal() & 0x3f) << 3) |
1154                            (info.getSubtype() << 9);
1155                    EventLog.writeEvent(EVENTLOG_CONNECTIVITY_STATE_CHANGED,
1156                            eventLogParam);
1157
1158                    if (info.getDetailedState() ==
1159                            NetworkInfo.DetailedState.FAILED) {
1160                        handleConnectionFailure(info);
1161                    } else if (info.getState() ==
1162                            NetworkInfo.State.DISCONNECTED) {
1163                        handleDisconnect(info);
1164                    } else if (info.getState() == NetworkInfo.State.SUSPENDED) {
1165                        // TODO: need to think this over.
1166                        // the logic here is, handle SUSPENDED the same as
1167                        // DISCONNECTED. The only difference being we are
1168                        // broadcasting an intent with NetworkInfo that's
1169                        // suspended. This allows the applications an
1170                        // opportunity to handle DISCONNECTED and SUSPENDED
1171                        // differently, or not.
1172                        handleDisconnect(info);
1173                    } else if (info.getState() == NetworkInfo.State.CONNECTED) {
1174                        handleConnect(info);
1175                    }
1176                    break;
1177
1178                case NetworkStateTracker.EVENT_SCAN_RESULTS_AVAILABLE:
1179                    info = (NetworkInfo) msg.obj;
1180                    handleScanResultsAvailable(info);
1181                    break;
1182
1183                case NetworkStateTracker.EVENT_NOTIFICATION_CHANGED:
1184                    handleNotificationChange(msg.arg1 == 1, msg.arg2,
1185                            (Notification) msg.obj);
1186
1187                case NetworkStateTracker.EVENT_CONFIGURATION_CHANGED:
1188                    handleDnsConfigurationChange();
1189                    break;
1190
1191                case NetworkStateTracker.EVENT_ROAMING_CHANGED:
1192                    // fill me in
1193                    break;
1194
1195                case NetworkStateTracker.EVENT_NETWORK_SUBTYPE_CHANGED:
1196                    // fill me in
1197                    break;
1198                case NetworkStateTracker.EVENT_RESTORE_DEFAULT_NETWORK:
1199                    for (NetworkStateTracker net : mNetTrackers) {
1200                        NetworkInfo i = net.getNetworkInfo();
1201                        if (i.isConnected() &&
1202                                !mNetAttributes[i.getType()].isDefault()) {
1203                            teardown(net);
1204                        }
1205                    }
1206                    break;
1207            }
1208        }
1209    }
1210}
1211