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