ConnectivityService.java revision f434dd1660f222a1ee85e77c98893b90857805c2
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.NetworkUtils;
31import android.net.wifi.WifiStateTracker;
32import android.os.Binder;
33import android.os.Handler;
34import android.os.IBinder;
35import android.os.Looper;
36import android.os.Message;
37import android.os.RemoteException;
38import android.os.ServiceManager;
39import android.os.SystemProperties;
40import android.provider.Settings;
41import android.text.TextUtils;
42import android.util.EventLog;
43import android.util.Slog;
44
45import com.android.internal.telephony.Phone;
46
47import com.android.server.connectivity.Tethering;
48
49import java.io.FileDescriptor;
50import java.io.PrintWriter;
51import java.util.ArrayList;
52import java.util.List;
53import java.net.InetAddress;
54import java.net.UnknownHostException;
55
56/**
57 * @hide
58 */
59public class ConnectivityService extends IConnectivityManager.Stub {
60
61    private static final boolean DBG = true;
62    private static final String TAG = "ConnectivityService";
63
64    // how long to wait before switching back to a radio's default network
65    private static final int RESTORE_DEFAULT_NETWORK_DELAY = 1 * 60 * 1000;
66    // system property that can override the above value
67    private static final String NETWORK_RESTORE_DELAY_PROP_NAME =
68            "android.telephony.apn-restore";
69
70
71    private Tethering mTethering;
72    private boolean mTetheringConfigValid = false;
73
74    /**
75     * Sometimes we want to refer to the individual network state
76     * trackers separately, and sometimes we just want to treat them
77     * abstractly.
78     */
79    private NetworkStateTracker mNetTrackers[];
80
81    /**
82     * A per Net list of the PID's that requested access to the net
83     * used both as a refcount and for per-PID DNS selection
84     */
85    private List mNetRequestersPids[];
86
87    // priority order of the nettrackers
88    // (excluding dynamically set mNetworkPreference)
89    // TODO - move mNetworkTypePreference into this
90    private int[] mPriorityList;
91
92    private Context mContext;
93    private int mNetworkPreference;
94    private int mActiveDefaultNetwork = -1;
95    // 0 is full bad, 100 is full good
96    private int mDefaultInetCondition = 0;
97    private int mDefaultInetConditionPublished = 0;
98    private boolean mInetConditionChangeInFlight = false;
99    private int mDefaultConnectionSequence = 0;
100
101    private int mNumDnsEntries;
102
103    private boolean mTestMode;
104    private static ConnectivityService sServiceInstance;
105
106    private Handler mHandler;
107
108    // list of DeathRecipients used to make sure features are turned off when
109    // a process dies
110    private List mFeatureUsers;
111
112    private boolean mSystemReady;
113    private Intent mInitialBroadcast;
114
115    private static class NetworkAttributes {
116        /**
117         * Class for holding settings read from resources.
118         */
119        public String mName;
120        public int mType;
121        public int mRadio;
122        public int mPriority;
123        public NetworkInfo.State mLastState;
124        public NetworkAttributes(String init) {
125            String fragments[] = init.split(",");
126            mName = fragments[0].toLowerCase();
127            mType = Integer.parseInt(fragments[1]);
128            mRadio = Integer.parseInt(fragments[2]);
129            mPriority = Integer.parseInt(fragments[3]);
130            mLastState = NetworkInfo.State.UNKNOWN;
131        }
132        public boolean isDefault() {
133            return (mType == mRadio);
134        }
135    }
136    NetworkAttributes[] mNetAttributes;
137    int mNetworksDefined;
138
139    private static class RadioAttributes {
140        public int mSimultaneity;
141        public int mType;
142        public RadioAttributes(String init) {
143            String fragments[] = init.split(",");
144            mType = Integer.parseInt(fragments[0]);
145            mSimultaneity = Integer.parseInt(fragments[1]);
146        }
147    }
148    RadioAttributes[] mRadioAttributes;
149
150    private static class ConnectivityThread extends Thread {
151        private Context mContext;
152
153        private ConnectivityThread(Context context) {
154            super("ConnectivityThread");
155            mContext = context;
156        }
157
158        @Override
159        public void run() {
160            Looper.prepare();
161            synchronized (this) {
162                sServiceInstance = new ConnectivityService(mContext);
163                notifyAll();
164            }
165            Looper.loop();
166        }
167
168        public static ConnectivityService getServiceInstance(Context context) {
169            ConnectivityThread thread = new ConnectivityThread(context);
170            thread.start();
171
172            synchronized (thread) {
173                while (sServiceInstance == null) {
174                    try {
175                        // Wait until sServiceInstance has been initialized.
176                        thread.wait();
177                    } catch (InterruptedException ignore) {
178                        Slog.e(TAG,
179                            "Unexpected InterruptedException while waiting"+
180                            " for ConnectivityService thread");
181                    }
182                }
183            }
184
185            return sServiceInstance;
186        }
187    }
188
189    public static ConnectivityService getInstance(Context context) {
190        return ConnectivityThread.getServiceInstance(context);
191    }
192
193    private ConnectivityService(Context context) {
194        if (DBG) Slog.v(TAG, "ConnectivityService starting up");
195
196        // setup our unique device name
197        String id = Settings.Secure.getString(context.getContentResolver(),
198                Settings.Secure.ANDROID_ID);
199        if (id != null && id.length() > 0) {
200            String name = new String("android_").concat(id);
201            SystemProperties.set("net.hostname", name);
202        }
203
204        mContext = context;
205        mNetTrackers = new NetworkStateTracker[
206                ConnectivityManager.MAX_NETWORK_TYPE+1];
207        mHandler = new MyHandler();
208
209        mNetworkPreference = getPersistedNetworkPreference();
210
211        mRadioAttributes = new RadioAttributes[ConnectivityManager.MAX_RADIO_TYPE+1];
212        mNetAttributes = new NetworkAttributes[ConnectivityManager.MAX_NETWORK_TYPE+1];
213
214        // Load device network attributes from resources
215        String[] raStrings = context.getResources().getStringArray(
216                com.android.internal.R.array.radioAttributes);
217        for (String raString : raStrings) {
218            RadioAttributes r = new RadioAttributes(raString);
219            if (r.mType > ConnectivityManager.MAX_RADIO_TYPE) {
220                Slog.e(TAG, "Error in radioAttributes - ignoring attempt to define type " + r.mType);
221                continue;
222            }
223            if (mRadioAttributes[r.mType] != null) {
224                Slog.e(TAG, "Error in radioAttributes - ignoring attempt to redefine type " +
225                        r.mType);
226                continue;
227            }
228            mRadioAttributes[r.mType] = r;
229        }
230
231        String[] naStrings = context.getResources().getStringArray(
232                com.android.internal.R.array.networkAttributes);
233        for (String naString : naStrings) {
234            try {
235                NetworkAttributes n = new NetworkAttributes(naString);
236                if (n.mType > ConnectivityManager.MAX_NETWORK_TYPE) {
237                    Slog.e(TAG, "Error in networkAttributes - ignoring attempt to define type " +
238                            n.mType);
239                    continue;
240                }
241                if (mNetAttributes[n.mType] != null) {
242                    Slog.e(TAG, "Error in networkAttributes - ignoring attempt to redefine type " +
243                            n.mType);
244                    continue;
245                }
246                if (mRadioAttributes[n.mRadio] == null) {
247                    Slog.e(TAG, "Error in networkAttributes - ignoring attempt to use undefined " +
248                            "radio " + n.mRadio + " in network type " + n.mType);
249                    continue;
250                }
251                mNetAttributes[n.mType] = n;
252                mNetworksDefined++;
253            } catch(Exception e) {
254                // ignore it - leave the entry null
255            }
256        }
257
258        // high priority first
259        mPriorityList = new int[mNetworksDefined];
260        {
261            int insertionPoint = mNetworksDefined-1;
262            int currentLowest = 0;
263            int nextLowest = 0;
264            while (insertionPoint > -1) {
265                for (NetworkAttributes na : mNetAttributes) {
266                    if (na == null) continue;
267                    if (na.mPriority < currentLowest) continue;
268                    if (na.mPriority > currentLowest) {
269                        if (na.mPriority < nextLowest || nextLowest == 0) {
270                            nextLowest = na.mPriority;
271                        }
272                        continue;
273                    }
274                    mPriorityList[insertionPoint--] = na.mType;
275                }
276                currentLowest = nextLowest;
277                nextLowest = 0;
278            }
279        }
280
281        mNetRequestersPids = new ArrayList[ConnectivityManager.MAX_NETWORK_TYPE+1];
282        for (int i : mPriorityList) {
283            mNetRequestersPids[i] = new ArrayList();
284        }
285
286        mFeatureUsers = new ArrayList();
287
288        mNumDnsEntries = 0;
289
290        mTestMode = SystemProperties.get("cm.test.mode").equals("true")
291                && SystemProperties.get("ro.build.type").equals("eng");
292        /*
293         * Create the network state trackers for Wi-Fi and mobile
294         * data. Maybe this could be done with a factory class,
295         * but it's not clear that it's worth it, given that
296         * the number of different network types is not going
297         * to change very often.
298         */
299        boolean noMobileData = !getMobileDataEnabled();
300        for (int netType : mPriorityList) {
301            switch (mNetAttributes[netType].mRadio) {
302            case ConnectivityManager.TYPE_WIFI:
303                if (DBG) Slog.v(TAG, "Starting Wifi Service.");
304                WifiStateTracker wst = new WifiStateTracker(context, mHandler);
305                WifiService wifiService = new WifiService(context, wst);
306                ServiceManager.addService(Context.WIFI_SERVICE, wifiService);
307                wifiService.startWifi();
308                mNetTrackers[ConnectivityManager.TYPE_WIFI] = wst;
309                wst.startMonitoring();
310
311                break;
312            case ConnectivityManager.TYPE_MOBILE:
313                mNetTrackers[netType] = new MobileDataStateTracker(context, mHandler,
314                    netType, mNetAttributes[netType].mName);
315                mNetTrackers[netType].startMonitoring();
316                if (noMobileData) {
317                    if (DBG) Slog.d(TAG, "tearing down Mobile networks due to setting");
318                    mNetTrackers[netType].teardown();
319                }
320                break;
321            default:
322                Slog.e(TAG, "Trying to create a DataStateTracker for an unknown radio type " +
323                        mNetAttributes[netType].mRadio);
324                continue;
325            }
326        }
327
328        mTethering = new Tethering(mContext, mHandler.getLooper());
329        mTetheringConfigValid = (((mNetTrackers[ConnectivityManager.TYPE_MOBILE_DUN] != null) ||
330                                  !mTethering.isDunRequired()) &&
331                                 (mTethering.getTetherableUsbRegexs().length != 0 ||
332                                  mTethering.getTetherableWifiRegexs().length != 0) &&
333                                 mTethering.getUpstreamIfaceRegexs().length != 0);
334
335    }
336
337
338    /**
339     * Sets the preferred network.
340     * @param preference the new preference
341     */
342    public synchronized void setNetworkPreference(int preference) {
343        enforceChangePermission();
344        if (ConnectivityManager.isNetworkTypeValid(preference) &&
345                mNetAttributes[preference] != null &&
346                mNetAttributes[preference].isDefault()) {
347            if (mNetworkPreference != preference) {
348                persistNetworkPreference(preference);
349                mNetworkPreference = preference;
350                enforcePreference();
351            }
352        }
353    }
354
355    public int getNetworkPreference() {
356        enforceAccessPermission();
357        return mNetworkPreference;
358    }
359
360    private void persistNetworkPreference(int networkPreference) {
361        final ContentResolver cr = mContext.getContentResolver();
362        Settings.Secure.putInt(cr, Settings.Secure.NETWORK_PREFERENCE,
363                networkPreference);
364    }
365
366    private int getPersistedNetworkPreference() {
367        final ContentResolver cr = mContext.getContentResolver();
368
369        final int networkPrefSetting = Settings.Secure
370                .getInt(cr, Settings.Secure.NETWORK_PREFERENCE, -1);
371        if (networkPrefSetting != -1) {
372            return networkPrefSetting;
373        }
374
375        return ConnectivityManager.DEFAULT_NETWORK_PREFERENCE;
376    }
377
378    /**
379     * Make the state of network connectivity conform to the preference settings
380     * In this method, we only tear down a non-preferred network. Establishing
381     * a connection to the preferred network is taken care of when we handle
382     * the disconnect event from the non-preferred network
383     * (see {@link #handleDisconnect(NetworkInfo)}).
384     */
385    private void enforcePreference() {
386        if (mNetTrackers[mNetworkPreference].getNetworkInfo().isConnected())
387            return;
388
389        if (!mNetTrackers[mNetworkPreference].isAvailable())
390            return;
391
392        for (int t=0; t <= ConnectivityManager.MAX_RADIO_TYPE; t++) {
393            if (t != mNetworkPreference && mNetTrackers[t] != null &&
394                    mNetTrackers[t].getNetworkInfo().isConnected()) {
395                if (DBG) {
396                    Slog.d(TAG, "tearing down " +
397                            mNetTrackers[t].getNetworkInfo() +
398                            " in enforcePreference");
399                }
400                teardown(mNetTrackers[t]);
401            }
402        }
403    }
404
405    private boolean teardown(NetworkStateTracker netTracker) {
406        if (netTracker.teardown()) {
407            netTracker.setTeardownRequested(true);
408            return true;
409        } else {
410            return false;
411        }
412    }
413
414    /**
415     * Return NetworkInfo for the active (i.e., connected) network interface.
416     * It is assumed that at most one network is active at a time. If more
417     * than one is active, it is indeterminate which will be returned.
418     * @return the info for the active network, or {@code null} if none is
419     * active
420     */
421    public NetworkInfo getActiveNetworkInfo() {
422        enforceAccessPermission();
423        for (int type=0; type <= ConnectivityManager.MAX_NETWORK_TYPE; type++) {
424            if (mNetAttributes[type] == null || !mNetAttributes[type].isDefault()) {
425                continue;
426            }
427            NetworkStateTracker t = mNetTrackers[type];
428            NetworkInfo info = t.getNetworkInfo();
429            if (info.isConnected()) {
430                if (DBG && type != mActiveDefaultNetwork) Slog.e(TAG,
431                        "connected default network is not " +
432                        "mActiveDefaultNetwork!");
433                return info;
434            }
435        }
436        return null;
437    }
438
439    public NetworkInfo getNetworkInfo(int networkType) {
440        enforceAccessPermission();
441        if (ConnectivityManager.isNetworkTypeValid(networkType)) {
442            NetworkStateTracker t = mNetTrackers[networkType];
443            if (t != null)
444                return t.getNetworkInfo();
445        }
446        return null;
447    }
448
449    public NetworkInfo[] getAllNetworkInfo() {
450        enforceAccessPermission();
451        NetworkInfo[] result = new NetworkInfo[mNetworksDefined];
452        int i = 0;
453        for (NetworkStateTracker t : mNetTrackers) {
454            if(t != null) result[i++] = t.getNetworkInfo();
455        }
456        return result;
457    }
458
459    public boolean setRadios(boolean turnOn) {
460        boolean result = true;
461        enforceChangePermission();
462        for (NetworkStateTracker t : mNetTrackers) {
463            if (t != null) result = t.setRadio(turnOn) && result;
464        }
465        return result;
466    }
467
468    public boolean setRadio(int netType, boolean turnOn) {
469        enforceChangePermission();
470        if (!ConnectivityManager.isNetworkTypeValid(netType)) {
471            return false;
472        }
473        NetworkStateTracker tracker = mNetTrackers[netType];
474        return tracker != null && tracker.setRadio(turnOn);
475    }
476
477    /**
478     * Used to notice when the calling process dies so we can self-expire
479     *
480     * Also used to know if the process has cleaned up after itself when
481     * our auto-expire timer goes off.  The timer has a link to an object.
482     *
483     */
484    private class FeatureUser implements IBinder.DeathRecipient {
485        int mNetworkType;
486        String mFeature;
487        IBinder mBinder;
488        int mPid;
489        int mUid;
490        long mCreateTime;
491
492        FeatureUser(int type, String feature, IBinder binder) {
493            super();
494            mNetworkType = type;
495            mFeature = feature;
496            mBinder = binder;
497            mPid = getCallingPid();
498            mUid = getCallingUid();
499            mCreateTime = System.currentTimeMillis();
500
501            try {
502                mBinder.linkToDeath(this, 0);
503            } catch (RemoteException e) {
504                binderDied();
505            }
506        }
507
508        void unlinkDeathRecipient() {
509            mBinder.unlinkToDeath(this, 0);
510        }
511
512        public void binderDied() {
513            Slog.d(TAG, "ConnectivityService FeatureUser binderDied(" +
514                    mNetworkType + ", " + mFeature + ", " + mBinder + "), created " +
515                    (System.currentTimeMillis() - mCreateTime) + " mSec ago");
516            stopUsingNetworkFeature(this, false);
517        }
518
519        public void expire() {
520            Slog.d(TAG, "ConnectivityService FeatureUser expire(" +
521                    mNetworkType + ", " + mFeature + ", " + mBinder +"), created " +
522                    (System.currentTimeMillis() - mCreateTime) + " mSec ago");
523            stopUsingNetworkFeature(this, false);
524        }
525
526        public String toString() {
527            return "FeatureUser("+mNetworkType+","+mFeature+","+mPid+","+mUid+"), created " +
528                    (System.currentTimeMillis() - mCreateTime) + " mSec ago";
529        }
530    }
531
532    // javadoc from interface
533    public int startUsingNetworkFeature(int networkType, String feature,
534            IBinder binder) {
535        if (DBG) {
536            Slog.d(TAG, "startUsingNetworkFeature for net " + networkType +
537                    ": " + feature);
538        }
539        enforceChangePermission();
540        if (!ConnectivityManager.isNetworkTypeValid(networkType) ||
541                mNetAttributes[networkType] == null) {
542            return Phone.APN_REQUEST_FAILED;
543        }
544
545        FeatureUser f = new FeatureUser(networkType, feature, binder);
546
547        // TODO - move this into the MobileDataStateTracker
548        int usedNetworkType = networkType;
549        if(networkType == ConnectivityManager.TYPE_MOBILE) {
550            if (!getMobileDataEnabled()) {
551                if (DBG) Slog.d(TAG, "requested special network with data disabled - rejected");
552                return Phone.APN_TYPE_NOT_AVAILABLE;
553            }
554            if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_MMS)) {
555                usedNetworkType = ConnectivityManager.TYPE_MOBILE_MMS;
556            } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_SUPL)) {
557                usedNetworkType = ConnectivityManager.TYPE_MOBILE_SUPL;
558            } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_DUN)) {
559                usedNetworkType = ConnectivityManager.TYPE_MOBILE_DUN;
560            } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_HIPRI)) {
561                usedNetworkType = ConnectivityManager.TYPE_MOBILE_HIPRI;
562            }
563        }
564        NetworkStateTracker network = mNetTrackers[usedNetworkType];
565        if (network != null) {
566            if (usedNetworkType != networkType) {
567                Integer currentPid = new Integer(getCallingPid());
568
569                NetworkStateTracker radio = mNetTrackers[networkType];
570                NetworkInfo ni = network.getNetworkInfo();
571
572                if (ni.isAvailable() == false) {
573                    if (DBG) Slog.d(TAG, "special network not available");
574                    return Phone.APN_TYPE_NOT_AVAILABLE;
575                }
576
577                synchronized(this) {
578                    mFeatureUsers.add(f);
579                    if (!mNetRequestersPids[usedNetworkType].contains(currentPid)) {
580                        // this gets used for per-pid dns when connected
581                        mNetRequestersPids[usedNetworkType].add(currentPid);
582                    }
583                }
584                mHandler.sendMessageDelayed(mHandler.obtainMessage(
585                        NetworkStateTracker.EVENT_RESTORE_DEFAULT_NETWORK,
586                        f), getRestoreDefaultNetworkDelay());
587
588
589                if ((ni.isConnectedOrConnecting() == true) &&
590                        !network.isTeardownRequested()) {
591                    if (ni.isConnected() == true) {
592                        // add the pid-specific dns
593                        handleDnsConfigurationChange(networkType);
594                        if (DBG) Slog.d(TAG, "special network already active");
595                        return Phone.APN_ALREADY_ACTIVE;
596                    }
597                    if (DBG) Slog.d(TAG, "special network already connecting");
598                    return Phone.APN_REQUEST_STARTED;
599                }
600
601                // check if the radio in play can make another contact
602                // assume if cannot for now
603
604                if (DBG) Slog.d(TAG, "reconnecting to special network");
605                network.reconnect();
606                return Phone.APN_REQUEST_STARTED;
607            } else {
608                synchronized(this) {
609                    mFeatureUsers.add(f);
610                }
611                mHandler.sendMessageDelayed(mHandler.obtainMessage(
612                        NetworkStateTracker.EVENT_RESTORE_DEFAULT_NETWORK,
613                        f), getRestoreDefaultNetworkDelay());
614
615                return network.startUsingNetworkFeature(feature,
616                        getCallingPid(), getCallingUid());
617            }
618        }
619        return Phone.APN_TYPE_NOT_AVAILABLE;
620    }
621
622    // javadoc from interface
623    public int stopUsingNetworkFeature(int networkType, String feature) {
624        enforceChangePermission();
625
626        int pid = getCallingPid();
627        int uid = getCallingUid();
628
629        FeatureUser u = null;
630        boolean found = false;
631
632        synchronized(this) {
633            for (int i = 0; i < mFeatureUsers.size() ; i++) {
634                u = (FeatureUser)mFeatureUsers.get(i);
635                if (uid == u.mUid && pid == u.mPid &&
636                        networkType == u.mNetworkType &&
637                        TextUtils.equals(feature, u.mFeature)) {
638                    found = true;
639                    break;
640                }
641            }
642        }
643        if (found && u != null) {
644            // stop regardless of how many other time this proc had called start
645            return stopUsingNetworkFeature(u, true);
646        } else {
647            // none found!
648            if (DBG) Slog.d(TAG, "ignoring stopUsingNetworkFeature - not a live request");
649            return 1;
650        }
651    }
652
653    private int stopUsingNetworkFeature(FeatureUser u, boolean ignoreDups) {
654        int networkType = u.mNetworkType;
655        String feature = u.mFeature;
656        int pid = u.mPid;
657        int uid = u.mUid;
658
659        NetworkStateTracker tracker = null;
660        boolean callTeardown = false;  // used to carry our decision outside of sync block
661
662        if (DBG) {
663            Slog.d(TAG, "stopUsingNetworkFeature for net " + networkType +
664                    ": " + feature);
665        }
666
667        if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
668            return -1;
669        }
670
671        // need to link the mFeatureUsers list with the mNetRequestersPids state in this
672        // sync block
673        synchronized(this) {
674            // check if this process still has an outstanding start request
675            if (!mFeatureUsers.contains(u)) {
676                if (DBG) Slog.d(TAG, "ignoring - this process has no outstanding requests");
677                return 1;
678            }
679            u.unlinkDeathRecipient();
680            mFeatureUsers.remove(mFeatureUsers.indexOf(u));
681            // If we care about duplicate requests, check for that here.
682            //
683            // This is done to support the extension of a request - the app
684            // can request we start the network feature again and renew the
685            // auto-shutoff delay.  Normal "stop" calls from the app though
686            // do not pay attention to duplicate requests - in effect the
687            // API does not refcount and a single stop will counter multiple starts.
688            if (ignoreDups == false) {
689                for (int i = 0; i < mFeatureUsers.size() ; i++) {
690                    FeatureUser x = (FeatureUser)mFeatureUsers.get(i);
691                    if (x.mUid == u.mUid && x.mPid == u.mPid &&
692                            x.mNetworkType == u.mNetworkType &&
693                            TextUtils.equals(x.mFeature, u.mFeature)) {
694                        if (DBG) Slog.d(TAG, "ignoring stopUsingNetworkFeature as dup is found");
695                        return 1;
696                    }
697                }
698            }
699
700            // TODO - move to MobileDataStateTracker
701            int usedNetworkType = networkType;
702            if (networkType == ConnectivityManager.TYPE_MOBILE) {
703                if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_MMS)) {
704                    usedNetworkType = ConnectivityManager.TYPE_MOBILE_MMS;
705                } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_SUPL)) {
706                    usedNetworkType = ConnectivityManager.TYPE_MOBILE_SUPL;
707                } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_DUN)) {
708                    usedNetworkType = ConnectivityManager.TYPE_MOBILE_DUN;
709                } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_HIPRI)) {
710                    usedNetworkType = ConnectivityManager.TYPE_MOBILE_HIPRI;
711                }
712            }
713            tracker =  mNetTrackers[usedNetworkType];
714            if (tracker == null) {
715                if (DBG) Slog.d(TAG, "ignoring - no known tracker for net type " + usedNetworkType);
716                return -1;
717            }
718            if (usedNetworkType != networkType) {
719                Integer currentPid = new Integer(pid);
720                mNetRequestersPids[usedNetworkType].remove(currentPid);
721                reassessPidDns(pid, true);
722                if (mNetRequestersPids[usedNetworkType].size() != 0) {
723                    if (DBG) Slog.d(TAG, "not tearing down special network - " +
724                           "others still using it");
725                    return 1;
726                }
727                callTeardown = true;
728            }
729        }
730        if (DBG) Slog.d(TAG, "Doing network teardown");
731        if (callTeardown) {
732            tracker.teardown();
733            return 1;
734        } else {
735            // do it the old fashioned way
736            return tracker.stopUsingNetworkFeature(feature, pid, uid);
737        }
738    }
739
740    /**
741     * @deprecated use requestRouteToHostAddress instead
742     *
743     * Ensure that a network route exists to deliver traffic to the specified
744     * host via the specified network interface.
745     * @param networkType the type of the network over which traffic to the
746     * specified host is to be routed
747     * @param hostAddress the IP address of the host to which the route is
748     * desired
749     * @return {@code true} on success, {@code false} on failure
750     */
751    public boolean requestRouteToHost(int networkType, int hostAddress) {
752        InetAddress inetAddress = NetworkUtils.intToInetAddress(hostAddress);
753
754        if (inetAddress == null) {
755            return false;
756        }
757
758        return requestRouteToHostAddress(networkType, inetAddress.getAddress());
759    }
760
761    /**
762     * Ensure that a network route exists to deliver traffic to the specified
763     * host via the specified network interface.
764     * @param networkType the type of the network over which traffic to the
765     * specified host is to be routed
766     * @param hostAddress the IP address of the host to which the route is
767     * desired
768     * @return {@code true} on success, {@code false} on failure
769     */
770    public boolean requestRouteToHostAddress(int networkType, byte[] hostAddress) {
771        enforceChangePermission();
772        if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
773            return false;
774        }
775        NetworkStateTracker tracker = mNetTrackers[networkType];
776
777        if (tracker == null || !tracker.getNetworkInfo().isConnected() ||
778                tracker.isTeardownRequested()) {
779            if (DBG) {
780                Slog.d(TAG, "requestRouteToHostAddress on down network " +
781                           "(" + networkType + ") - dropped");
782            }
783            return false;
784        }
785
786        try {
787            InetAddress inetAddress = InetAddress.getByAddress(hostAddress);
788            return tracker.requestRouteToHost(inetAddress);
789        } catch (UnknownHostException e) {
790            return false;
791        }
792    }
793
794    /**
795     * @see ConnectivityManager#getBackgroundDataSetting()
796     */
797    public boolean getBackgroundDataSetting() {
798        return Settings.Secure.getInt(mContext.getContentResolver(),
799                Settings.Secure.BACKGROUND_DATA, 1) == 1;
800    }
801
802    /**
803     * @see ConnectivityManager#setBackgroundDataSetting(boolean)
804     */
805    public void setBackgroundDataSetting(boolean allowBackgroundDataUsage) {
806        mContext.enforceCallingOrSelfPermission(
807                android.Manifest.permission.CHANGE_BACKGROUND_DATA_SETTING,
808                "ConnectivityService");
809
810        if (getBackgroundDataSetting() == allowBackgroundDataUsage) return;
811
812        Settings.Secure.putInt(mContext.getContentResolver(),
813                Settings.Secure.BACKGROUND_DATA,
814                allowBackgroundDataUsage ? 1 : 0);
815
816        Intent broadcast = new Intent(
817                ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED);
818        mContext.sendBroadcast(broadcast);
819    }
820
821    /**
822     * @see ConnectivityManager#getMobileDataEnabled()
823     */
824    public boolean getMobileDataEnabled() {
825        enforceAccessPermission();
826        boolean retVal = Settings.Secure.getInt(mContext.getContentResolver(),
827                Settings.Secure.MOBILE_DATA, 1) == 1;
828        if (DBG) Slog.d(TAG, "getMobileDataEnabled returning " + retVal);
829        return retVal;
830    }
831
832    /**
833     * @see ConnectivityManager#setMobileDataEnabled(boolean)
834     */
835    public synchronized void setMobileDataEnabled(boolean enabled) {
836        enforceChangePermission();
837        if (DBG) Slog.d(TAG, "setMobileDataEnabled(" + enabled + ")");
838
839        if (getMobileDataEnabled() == enabled) return;
840
841        Settings.Secure.putInt(mContext.getContentResolver(),
842                Settings.Secure.MOBILE_DATA, enabled ? 1 : 0);
843
844        if (enabled) {
845            if (mNetTrackers[ConnectivityManager.TYPE_MOBILE] != null) {
846                if (DBG) Slog.d(TAG, "starting up " + mNetTrackers[ConnectivityManager.TYPE_MOBILE]);
847                mNetTrackers[ConnectivityManager.TYPE_MOBILE].reconnect();
848            }
849        } else {
850            for (NetworkStateTracker nt : mNetTrackers) {
851                if (nt == null) continue;
852                int netType = nt.getNetworkInfo().getType();
853                if (mNetAttributes[netType].mRadio == ConnectivityManager.TYPE_MOBILE) {
854                    if (DBG) Slog.d(TAG, "tearing down " + nt);
855                    nt.teardown();
856                }
857            }
858        }
859    }
860
861    private int getNumConnectedNetworks() {
862        int numConnectedNets = 0;
863
864        for (NetworkStateTracker nt : mNetTrackers) {
865            if (nt != null && nt.getNetworkInfo().isConnected() &&
866                    !nt.isTeardownRequested()) {
867                ++numConnectedNets;
868            }
869        }
870        return numConnectedNets;
871    }
872
873    private void enforceAccessPermission() {
874        mContext.enforceCallingOrSelfPermission(
875                android.Manifest.permission.ACCESS_NETWORK_STATE,
876                "ConnectivityService");
877    }
878
879    private void enforceChangePermission() {
880        mContext.enforceCallingOrSelfPermission(
881                android.Manifest.permission.CHANGE_NETWORK_STATE,
882                "ConnectivityService");
883    }
884
885    // TODO Make this a special check when it goes public
886    private void enforceTetherChangePermission() {
887        mContext.enforceCallingOrSelfPermission(
888                android.Manifest.permission.CHANGE_NETWORK_STATE,
889                "ConnectivityService");
890    }
891
892    private void enforceTetherAccessPermission() {
893        mContext.enforceCallingOrSelfPermission(
894                android.Manifest.permission.ACCESS_NETWORK_STATE,
895                "ConnectivityService");
896    }
897
898    /**
899     * Handle a {@code DISCONNECTED} event. If this pertains to the non-active
900     * network, we ignore it. If it is for the active network, we send out a
901     * broadcast. But first, we check whether it might be possible to connect
902     * to a different network.
903     * @param info the {@code NetworkInfo} for the network
904     */
905    private void handleDisconnect(NetworkInfo info) {
906
907        int prevNetType = info.getType();
908
909        mNetTrackers[prevNetType].setTeardownRequested(false);
910        /*
911         * If the disconnected network is not the active one, then don't report
912         * this as a loss of connectivity. What probably happened is that we're
913         * getting the disconnect for a network that we explicitly disabled
914         * in accordance with network preference policies.
915         */
916        if (!mNetAttributes[prevNetType].isDefault()) {
917            List pids = mNetRequestersPids[prevNetType];
918            for (int i = 0; i<pids.size(); i++) {
919                Integer pid = (Integer)pids.get(i);
920                // will remove them because the net's no longer connected
921                // need to do this now as only now do we know the pids and
922                // can properly null things that are no longer referenced.
923                reassessPidDns(pid.intValue(), false);
924            }
925        }
926
927        Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
928        intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
929        if (info.isFailover()) {
930            intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
931            info.setFailover(false);
932        }
933        if (info.getReason() != null) {
934            intent.putExtra(ConnectivityManager.EXTRA_REASON, info.getReason());
935        }
936        if (info.getExtraInfo() != null) {
937            intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO,
938                    info.getExtraInfo());
939        }
940
941        NetworkStateTracker newNet = null;
942        if (mNetAttributes[prevNetType].isDefault()) {
943            newNet = tryFailover(prevNetType);
944            if (newNet != null) {
945                NetworkInfo switchTo = newNet.getNetworkInfo();
946                intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO, switchTo);
947            } else {
948                intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
949            }
950        }
951        // do this before we broadcast the change
952        handleConnectivityChange(prevNetType);
953
954        sendStickyBroadcast(intent);
955        /*
956         * If the failover network is already connected, then immediately send
957         * out a followup broadcast indicating successful failover
958         */
959        if (newNet != null && newNet.getNetworkInfo().isConnected()) {
960            sendConnectedBroadcast(newNet.getNetworkInfo());
961        }
962    }
963
964    // returns null if no failover available
965    private NetworkStateTracker tryFailover(int prevNetType) {
966        /*
967         * If this is a default network, check if other defaults are available
968         * or active
969         */
970        NetworkStateTracker newNet = null;
971        if (mNetAttributes[prevNetType].isDefault()) {
972            if (mActiveDefaultNetwork == prevNetType) {
973                mActiveDefaultNetwork = -1;
974            }
975
976            int newType = -1;
977            int newPriority = -1;
978            boolean noMobileData = !getMobileDataEnabled();
979            for (int checkType=0; checkType <= ConnectivityManager.MAX_NETWORK_TYPE; checkType++) {
980                if (checkType == prevNetType) continue;
981                if (mNetAttributes[checkType] == null) continue;
982                if (mNetAttributes[checkType].mRadio == ConnectivityManager.TYPE_MOBILE &&
983                        noMobileData) {
984                    if (DBG) {
985                        Slog.d(TAG, "not failing over to mobile type " + checkType +
986                                " because Mobile Data Disabled");
987                    }
988                    continue;
989                }
990                if (mNetAttributes[checkType].isDefault()) {
991                    /* TODO - if we have multiple nets we could use
992                     * we may want to put more thought into which we choose
993                     */
994                    if (checkType == mNetworkPreference) {
995                        newType = checkType;
996                        break;
997                    }
998                    if (mNetAttributes[checkType].mPriority > newPriority) {
999                        newType = checkType;
1000                        newPriority = mNetAttributes[newType].mPriority;
1001                    }
1002                }
1003            }
1004
1005            if (newType != -1) {
1006                newNet = mNetTrackers[newType];
1007                /**
1008                 * See if the other network is available to fail over to.
1009                 * If is not available, we enable it anyway, so that it
1010                 * will be able to connect when it does become available,
1011                 * but we report a total loss of connectivity rather than
1012                 * report that we are attempting to fail over.
1013                 */
1014                if (newNet.isAvailable()) {
1015                    NetworkInfo switchTo = newNet.getNetworkInfo();
1016                    switchTo.setFailover(true);
1017                    if (!switchTo.isConnectedOrConnecting() ||
1018                            newNet.isTeardownRequested()) {
1019                        newNet.reconnect();
1020                    }
1021                    if (DBG) {
1022                        if (switchTo.isConnected()) {
1023                            Slog.v(TAG, "Switching to already connected " +
1024                                    switchTo.getTypeName());
1025                        } else {
1026                            Slog.v(TAG, "Attempting to switch to " +
1027                                    switchTo.getTypeName());
1028                        }
1029                    }
1030                } else {
1031                    newNet.reconnect();
1032                    newNet = null; // not officially avail..  try anyway, but
1033                                   // report no failover
1034                }
1035            }
1036        }
1037
1038        return newNet;
1039    }
1040
1041    private void sendConnectedBroadcast(NetworkInfo info) {
1042        sendGeneralBroadcast(info, ConnectivityManager.CONNECTIVITY_ACTION);
1043    }
1044
1045    private void sendInetConditionBroadcast(NetworkInfo info) {
1046        sendGeneralBroadcast(info, ConnectivityManager.INET_CONDITION_ACTION);
1047    }
1048
1049    private void sendGeneralBroadcast(NetworkInfo info, String bcastType) {
1050        Intent intent = new Intent(bcastType);
1051        intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
1052        if (info.isFailover()) {
1053            intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
1054            info.setFailover(false);
1055        }
1056        if (info.getReason() != null) {
1057            intent.putExtra(ConnectivityManager.EXTRA_REASON, info.getReason());
1058        }
1059        if (info.getExtraInfo() != null) {
1060            intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO,
1061                    info.getExtraInfo());
1062        }
1063        intent.putExtra(ConnectivityManager.EXTRA_INET_CONDITION, mDefaultInetConditionPublished);
1064        sendStickyBroadcast(intent);
1065    }
1066
1067    /**
1068     * Called when an attempt to fail over to another network has failed.
1069     * @param info the {@link NetworkInfo} for the failed network
1070     */
1071    private void handleConnectionFailure(NetworkInfo info) {
1072        mNetTrackers[info.getType()].setTeardownRequested(false);
1073
1074        String reason = info.getReason();
1075        String extraInfo = info.getExtraInfo();
1076
1077        if (DBG) {
1078            String reasonText;
1079            if (reason == null) {
1080                reasonText = ".";
1081            } else {
1082                reasonText = " (" + reason + ").";
1083            }
1084            Slog.v(TAG, "Attempt to connect to " + info.getTypeName() +
1085                    " failed" + reasonText);
1086        }
1087
1088        Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
1089        intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
1090        if (getActiveNetworkInfo() == null) {
1091            intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
1092        }
1093        if (reason != null) {
1094            intent.putExtra(ConnectivityManager.EXTRA_REASON, reason);
1095        }
1096        if (extraInfo != null) {
1097            intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, extraInfo);
1098        }
1099        if (info.isFailover()) {
1100            intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
1101            info.setFailover(false);
1102        }
1103
1104        NetworkStateTracker newNet = null;
1105        if (mNetAttributes[info.getType()].isDefault()) {
1106            newNet = tryFailover(info.getType());
1107            if (newNet != null) {
1108                NetworkInfo switchTo = newNet.getNetworkInfo();
1109                intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO, switchTo);
1110            } else {
1111                intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
1112            }
1113        }
1114
1115        sendStickyBroadcast(intent);
1116        /*
1117         * If the failover network is already connected, then immediately send
1118         * out a followup broadcast indicating successful failover
1119         */
1120        if (newNet != null && newNet.getNetworkInfo().isConnected()) {
1121            sendConnectedBroadcast(newNet.getNetworkInfo());
1122        }
1123    }
1124
1125    private void sendStickyBroadcast(Intent intent) {
1126        synchronized(this) {
1127            if (!mSystemReady) {
1128                mInitialBroadcast = new Intent(intent);
1129            }
1130            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1131            mContext.sendStickyBroadcast(intent);
1132        }
1133    }
1134
1135    void systemReady() {
1136        synchronized(this) {
1137            mSystemReady = true;
1138            if (mInitialBroadcast != null) {
1139                mContext.sendStickyBroadcast(mInitialBroadcast);
1140                mInitialBroadcast = null;
1141            }
1142        }
1143    }
1144
1145    private void handleConnect(NetworkInfo info) {
1146        int type = info.getType();
1147
1148        // snapshot isFailover, because sendConnectedBroadcast() resets it
1149        boolean isFailover = info.isFailover();
1150        NetworkStateTracker thisNet = mNetTrackers[type];
1151
1152        // if this is a default net and other default is running
1153        // kill the one not preferred
1154        if (mNetAttributes[type].isDefault()) {
1155            if (mActiveDefaultNetwork != -1 && mActiveDefaultNetwork != type) {
1156                if ((type != mNetworkPreference &&
1157                        mNetAttributes[mActiveDefaultNetwork].mPriority >
1158                        mNetAttributes[type].mPriority) ||
1159                        mNetworkPreference == mActiveDefaultNetwork) {
1160                        // don't accept this one
1161                        if (DBG) Slog.v(TAG, "Not broadcasting CONNECT_ACTION " +
1162                                "to torn down network " + info.getTypeName());
1163                        teardown(thisNet);
1164                        return;
1165                } else {
1166                    // tear down the other
1167                    NetworkStateTracker otherNet =
1168                            mNetTrackers[mActiveDefaultNetwork];
1169                    if (DBG) Slog.v(TAG, "Policy requires " +
1170                            otherNet.getNetworkInfo().getTypeName() +
1171                            " teardown");
1172                    if (!teardown(otherNet)) {
1173                        Slog.e(TAG, "Network declined teardown request");
1174                        return;
1175                    }
1176                    if (isFailover) {
1177                        otherNet.releaseWakeLock();
1178                    }
1179                }
1180            }
1181            mActiveDefaultNetwork = type;
1182            // this will cause us to come up initially as unconnected and switching
1183            // to connected after our normal pause unless somebody reports us as reall
1184            // disconnected
1185            mDefaultInetConditionPublished = 0;
1186            mDefaultConnectionSequence++;
1187            mInetConditionChangeInFlight = false;
1188            // Don't do this - if we never sign in stay, grey
1189            //reportNetworkCondition(mActiveDefaultNetwork, 100);
1190        }
1191        thisNet.setTeardownRequested(false);
1192        thisNet.updateNetworkSettings();
1193        handleConnectivityChange(type);
1194        sendConnectedBroadcast(info);
1195    }
1196
1197    private void handleScanResultsAvailable(NetworkInfo info) {
1198        int networkType = info.getType();
1199        if (networkType != ConnectivityManager.TYPE_WIFI) {
1200            if (DBG) Slog.v(TAG, "Got ScanResultsAvailable for " +
1201                    info.getTypeName() + " network. Don't know how to handle.");
1202        }
1203
1204        mNetTrackers[networkType].interpretScanResultsAvailable();
1205    }
1206
1207    private void handleNotificationChange(boolean visible, int id,
1208            Notification notification) {
1209        NotificationManager notificationManager = (NotificationManager) mContext
1210                .getSystemService(Context.NOTIFICATION_SERVICE);
1211
1212        if (visible) {
1213            notificationManager.notify(id, notification);
1214        } else {
1215            notificationManager.cancel(id);
1216        }
1217    }
1218
1219    /**
1220     * After a change in the connectivity state of any network, We're mainly
1221     * concerned with making sure that the list of DNS servers is setupup
1222     * according to which networks are connected, and ensuring that the
1223     * right routing table entries exist.
1224     */
1225    private void handleConnectivityChange(int netType) {
1226        /*
1227         * If a non-default network is enabled, add the host routes that
1228         * will allow it's DNS servers to be accessed.
1229         */
1230        handleDnsConfigurationChange(netType);
1231
1232        if (mNetTrackers[netType].getNetworkInfo().isConnected()) {
1233            if (mNetAttributes[netType].isDefault()) {
1234                mNetTrackers[netType].addDefaultRoute();
1235            } else {
1236                mNetTrackers[netType].addPrivateDnsRoutes();
1237            }
1238        } else {
1239            if (mNetAttributes[netType].isDefault()) {
1240                mNetTrackers[netType].removeDefaultRoute();
1241            } else {
1242                mNetTrackers[netType].removePrivateDnsRoutes();
1243            }
1244        }
1245    }
1246
1247    /**
1248     * Adjust the per-process dns entries (net.dns<x>.<pid>) based
1249     * on the highest priority active net which this process requested.
1250     * If there aren't any, clear it out
1251     */
1252    private void reassessPidDns(int myPid, boolean doBump)
1253    {
1254        if (DBG) Slog.d(TAG, "reassessPidDns for pid " + myPid);
1255        for(int i : mPriorityList) {
1256            if (mNetAttributes[i].isDefault()) {
1257                continue;
1258            }
1259            NetworkStateTracker nt = mNetTrackers[i];
1260            if (nt.getNetworkInfo().isConnected() &&
1261                    !nt.isTeardownRequested()) {
1262                List pids = mNetRequestersPids[i];
1263                for (int j=0; j<pids.size(); j++) {
1264                    Integer pid = (Integer)pids.get(j);
1265                    if (pid.intValue() == myPid) {
1266                        String[] dnsList = nt.getNameServers();
1267                        writePidDns(dnsList, myPid);
1268                        if (doBump) {
1269                            bumpDns();
1270                        }
1271                        return;
1272                    }
1273                }
1274           }
1275        }
1276        // nothing found - delete
1277        for (int i = 1; ; i++) {
1278            String prop = "net.dns" + i + "." + myPid;
1279            if (SystemProperties.get(prop).length() == 0) {
1280                if (doBump) {
1281                    bumpDns();
1282                }
1283                return;
1284            }
1285            SystemProperties.set(prop, "");
1286        }
1287    }
1288
1289    private void writePidDns(String[] dnsList, int pid) {
1290        int j = 1;
1291        for (String dns : dnsList) {
1292            if (dns != null && !TextUtils.equals(dns, "0.0.0.0")) {
1293                SystemProperties.set("net.dns" + j++ + "." + pid, dns);
1294            }
1295        }
1296    }
1297
1298    private void bumpDns() {
1299        /*
1300         * Bump the property that tells the name resolver library to reread
1301         * the DNS server list from the properties.
1302         */
1303        String propVal = SystemProperties.get("net.dnschange");
1304        int n = 0;
1305        if (propVal.length() != 0) {
1306            try {
1307                n = Integer.parseInt(propVal);
1308            } catch (NumberFormatException e) {}
1309        }
1310        SystemProperties.set("net.dnschange", "" + (n+1));
1311    }
1312
1313    private void handleDnsConfigurationChange(int netType) {
1314        // add default net's dns entries
1315        NetworkStateTracker nt = mNetTrackers[netType];
1316        if (nt != null && nt.getNetworkInfo().isConnected() && !nt.isTeardownRequested()) {
1317            String[] dnsList = nt.getNameServers();
1318            if (mNetAttributes[netType].isDefault()) {
1319                int j = 1;
1320                for (String dns : dnsList) {
1321                    if (dns != null && !TextUtils.equals(dns, "0.0.0.0")) {
1322                        if (DBG) {
1323                            Slog.d(TAG, "adding dns " + dns + " for " +
1324                                    nt.getNetworkInfo().getTypeName());
1325                        }
1326                        SystemProperties.set("net.dns" + j++, dns);
1327                    }
1328                }
1329                for (int k=j ; k<mNumDnsEntries; k++) {
1330                    if (DBG) Slog.d(TAG, "erasing net.dns" + k);
1331                    SystemProperties.set("net.dns" + k, "");
1332                }
1333                mNumDnsEntries = j;
1334            } else {
1335                // set per-pid dns for attached secondary nets
1336                List pids = mNetRequestersPids[netType];
1337                for (int y=0; y< pids.size(); y++) {
1338                    Integer pid = (Integer)pids.get(y);
1339                    writePidDns(dnsList, pid.intValue());
1340                }
1341            }
1342        }
1343        bumpDns();
1344    }
1345
1346    private int getRestoreDefaultNetworkDelay() {
1347        String restoreDefaultNetworkDelayStr = SystemProperties.get(
1348                NETWORK_RESTORE_DELAY_PROP_NAME);
1349        if(restoreDefaultNetworkDelayStr != null &&
1350                restoreDefaultNetworkDelayStr.length() != 0) {
1351            try {
1352                return Integer.valueOf(restoreDefaultNetworkDelayStr);
1353            } catch (NumberFormatException e) {
1354            }
1355        }
1356        return RESTORE_DEFAULT_NETWORK_DELAY;
1357    }
1358
1359    @Override
1360    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1361        if (mContext.checkCallingOrSelfPermission(
1362                android.Manifest.permission.DUMP)
1363                != PackageManager.PERMISSION_GRANTED) {
1364            pw.println("Permission Denial: can't dump ConnectivityService " +
1365                    "from from pid=" + Binder.getCallingPid() + ", uid=" +
1366                    Binder.getCallingUid());
1367            return;
1368        }
1369        pw.println();
1370        for (NetworkStateTracker nst : mNetTrackers) {
1371            if (nst != null) {
1372                if (nst.getNetworkInfo().isConnected()) {
1373                    pw.println("Active network: " + nst.getNetworkInfo().
1374                            getTypeName());
1375                }
1376                pw.println(nst.getNetworkInfo());
1377                pw.println(nst);
1378                pw.println();
1379            }
1380        }
1381
1382        pw.println("Network Requester Pids:");
1383        for (int net : mPriorityList) {
1384            String pidString = net + ": ";
1385            for (Object pid : mNetRequestersPids[net]) {
1386                pidString = pidString + pid.toString() + ", ";
1387            }
1388            pw.println(pidString);
1389        }
1390        pw.println();
1391
1392        pw.println("FeatureUsers:");
1393        for (Object requester : mFeatureUsers) {
1394            pw.println(requester.toString());
1395        }
1396        pw.println();
1397
1398        mTethering.dump(fd, pw, args);
1399    }
1400
1401    // must be stateless - things change under us.
1402    private class MyHandler extends Handler {
1403        @Override
1404        public void handleMessage(Message msg) {
1405            NetworkInfo info;
1406            switch (msg.what) {
1407                case NetworkStateTracker.EVENT_STATE_CHANGED:
1408                    info = (NetworkInfo) msg.obj;
1409                    int type = info.getType();
1410                    NetworkInfo.State state = info.getState();
1411                    // only do this optimization for wifi.  It going into scan mode for location
1412                    // services generates alot of noise.  Meanwhile the mms apn won't send out
1413                    // subsequent notifications when on default cellular because it never
1414                    // disconnects..  so only do this to wifi notifications.  Fixed better when the
1415                    // APN notifications are standardized.
1416                    if (mNetAttributes[type].mLastState == state &&
1417                            mNetAttributes[type].mRadio == ConnectivityManager.TYPE_WIFI) {
1418                        if (DBG) {
1419                            // TODO - remove this after we validate the dropping doesn't break
1420                            // anything
1421                            Slog.d(TAG, "Dropping ConnectivityChange for " +
1422                                    info.getTypeName() + ": " +
1423                                    state + "/" + info.getDetailedState());
1424                        }
1425                        return;
1426                    }
1427                    mNetAttributes[type].mLastState = state;
1428
1429                    if (DBG) Slog.d(TAG, "ConnectivityChange for " +
1430                            info.getTypeName() + ": " +
1431                            state + "/" + info.getDetailedState());
1432
1433                    // Connectivity state changed:
1434                    // [31-13] Reserved for future use
1435                    // [12-9] Network subtype (for mobile network, as defined
1436                    //         by TelephonyManager)
1437                    // [8-3] Detailed state ordinal (as defined by
1438                    //         NetworkInfo.DetailedState)
1439                    // [2-0] Network type (as defined by ConnectivityManager)
1440                    int eventLogParam = (info.getType() & 0x7) |
1441                            ((info.getDetailedState().ordinal() & 0x3f) << 3) |
1442                            (info.getSubtype() << 9);
1443                    EventLog.writeEvent(EventLogTags.CONNECTIVITY_STATE_CHANGED,
1444                            eventLogParam);
1445
1446                    if (info.getDetailedState() ==
1447                            NetworkInfo.DetailedState.FAILED) {
1448                        handleConnectionFailure(info);
1449                    } else if (state == NetworkInfo.State.DISCONNECTED) {
1450                        handleDisconnect(info);
1451                    } else if (state == NetworkInfo.State.SUSPENDED) {
1452                        // TODO: need to think this over.
1453                        // the logic here is, handle SUSPENDED the same as
1454                        // DISCONNECTED. The only difference being we are
1455                        // broadcasting an intent with NetworkInfo that's
1456                        // suspended. This allows the applications an
1457                        // opportunity to handle DISCONNECTED and SUSPENDED
1458                        // differently, or not.
1459                        handleDisconnect(info);
1460                    } else if (state == NetworkInfo.State.CONNECTED) {
1461                        handleConnect(info);
1462                    }
1463                    break;
1464
1465                case NetworkStateTracker.EVENT_SCAN_RESULTS_AVAILABLE:
1466                    info = (NetworkInfo) msg.obj;
1467                    handleScanResultsAvailable(info);
1468                    break;
1469
1470                case NetworkStateTracker.EVENT_NOTIFICATION_CHANGED:
1471                    handleNotificationChange(msg.arg1 == 1, msg.arg2,
1472                            (Notification) msg.obj);
1473                    break;
1474
1475                case NetworkStateTracker.EVENT_CONFIGURATION_CHANGED:
1476                    info = (NetworkInfo) msg.obj;
1477                    type = info.getType();
1478                    handleDnsConfigurationChange(type);
1479                    break;
1480
1481                case NetworkStateTracker.EVENT_ROAMING_CHANGED:
1482                    // fill me in
1483                    break;
1484
1485                case NetworkStateTracker.EVENT_NETWORK_SUBTYPE_CHANGED:
1486                    // fill me in
1487                    break;
1488                case NetworkStateTracker.EVENT_RESTORE_DEFAULT_NETWORK:
1489                    FeatureUser u = (FeatureUser)msg.obj;
1490                    u.expire();
1491                    break;
1492                case NetworkStateTracker.EVENT_INET_CONDITION_CHANGE:
1493                    if (DBG) {
1494                        Slog.d(TAG, "Inet connectivity change, net=" +
1495                                msg.arg1 + ", condition=" + msg.arg2 +
1496                                ",mActiveDefaultNetwork=" + mActiveDefaultNetwork);
1497                    }
1498                    if (mActiveDefaultNetwork == -1) {
1499                        if (DBG) Slog.d(TAG, "no active default network - aborting");
1500                        break;
1501                    }
1502                    if (mActiveDefaultNetwork != msg.arg1) {
1503                        if (DBG) Slog.d(TAG, "given net not default - aborting");
1504                        break;
1505                    }
1506                    mDefaultInetCondition = msg.arg2;
1507                    int delay;
1508                    if (mInetConditionChangeInFlight == false) {
1509                        if (DBG) Slog.d(TAG, "starting a change hold");
1510                        // setup a new hold to debounce this
1511                        if (mDefaultInetCondition > 50) {
1512                            delay = Settings.Secure.getInt(mContext.getContentResolver(),
1513                                    Settings.Secure.INET_CONDITION_DEBOUNCE_UP_DELAY, 500);
1514                        } else {
1515                            delay = Settings.Secure.getInt(mContext.getContentResolver(),
1516                                    Settings.Secure.INET_CONDITION_DEBOUNCE_DOWN_DELAY, 3000);
1517                        }
1518                        mInetConditionChangeInFlight = true;
1519                        sendMessageDelayed(obtainMessage(
1520                                NetworkStateTracker.EVENT_INET_CONDITION_HOLD_END,
1521                                mActiveDefaultNetwork, mDefaultConnectionSequence), delay);
1522                    } else {
1523                        // we've set the new condition, when this hold ends that will get
1524                        // picked up
1525                        if (DBG) Slog.d(TAG, "currently in hold - not setting new end evt");
1526                    }
1527                    break;
1528                case NetworkStateTracker.EVENT_INET_CONDITION_HOLD_END:
1529                    if (DBG) {
1530                        Slog.d(TAG, "Inet hold end, net=" + msg.arg1 +
1531                                ", condition =" + mDefaultInetCondition +
1532                                ", published condition =" + mDefaultInetConditionPublished);
1533                    }
1534                    mInetConditionChangeInFlight = false;
1535
1536                    if (mActiveDefaultNetwork == -1) {
1537                        if (DBG) Slog.d(TAG, "no active default network - aborting");
1538                        break;
1539                    }
1540                    if (mDefaultConnectionSequence != msg.arg2) {
1541                        if (DBG) Slog.d(TAG, "event hold for obsolete network - aborting");
1542                        break;
1543                    }
1544                    if (mDefaultInetConditionPublished == mDefaultInetCondition) {
1545                        if (DBG) Slog.d(TAG, "no change in condition - aborting");
1546                        break;
1547                    }
1548                    NetworkInfo networkInfo = mNetTrackers[mActiveDefaultNetwork].getNetworkInfo();
1549                    if (networkInfo.isConnected() == false) {
1550                        if (DBG) Slog.d(TAG, "default network not connected - aborting");
1551                        break;
1552                    }
1553                    mDefaultInetConditionPublished = mDefaultInetCondition;
1554                    sendInetConditionBroadcast(networkInfo);
1555                    break;
1556            }
1557        }
1558    }
1559
1560    // javadoc from interface
1561    public int tether(String iface) {
1562        enforceTetherChangePermission();
1563
1564        if (isTetheringSupported()) {
1565            return mTethering.tether(iface);
1566        } else {
1567            return ConnectivityManager.TETHER_ERROR_UNSUPPORTED;
1568        }
1569    }
1570
1571    // javadoc from interface
1572    public int untether(String iface) {
1573        enforceTetherChangePermission();
1574
1575        if (isTetheringSupported()) {
1576            return mTethering.untether(iface);
1577        } else {
1578            return ConnectivityManager.TETHER_ERROR_UNSUPPORTED;
1579        }
1580    }
1581
1582    // javadoc from interface
1583    public int getLastTetherError(String iface) {
1584        enforceTetherAccessPermission();
1585
1586        if (isTetheringSupported()) {
1587            return mTethering.getLastTetherError(iface);
1588        } else {
1589            return ConnectivityManager.TETHER_ERROR_UNSUPPORTED;
1590        }
1591    }
1592
1593    // TODO - proper iface API for selection by property, inspection, etc
1594    public String[] getTetherableUsbRegexs() {
1595        enforceTetherAccessPermission();
1596        if (isTetheringSupported()) {
1597            return mTethering.getTetherableUsbRegexs();
1598        } else {
1599            return new String[0];
1600        }
1601    }
1602
1603    public String[] getTetherableWifiRegexs() {
1604        enforceTetherAccessPermission();
1605        if (isTetheringSupported()) {
1606            return mTethering.getTetherableWifiRegexs();
1607        } else {
1608            return new String[0];
1609        }
1610    }
1611
1612    // TODO - move iface listing, queries, etc to new module
1613    // javadoc from interface
1614    public String[] getTetherableIfaces() {
1615        enforceTetherAccessPermission();
1616        return mTethering.getTetherableIfaces();
1617    }
1618
1619    public String[] getTetheredIfaces() {
1620        enforceTetherAccessPermission();
1621        return mTethering.getTetheredIfaces();
1622    }
1623
1624    public String[] getTetheringErroredIfaces() {
1625        enforceTetherAccessPermission();
1626        return mTethering.getErroredIfaces();
1627    }
1628
1629    // if ro.tether.denied = true we default to no tethering
1630    // gservices could set the secure setting to 1 though to enable it on a build where it
1631    // had previously been turned off.
1632    public boolean isTetheringSupported() {
1633        enforceTetherAccessPermission();
1634        int defaultVal = (SystemProperties.get("ro.tether.denied").equals("true") ? 0 : 1);
1635        boolean tetherEnabledInSettings = (Settings.Secure.getInt(mContext.getContentResolver(),
1636                Settings.Secure.TETHER_SUPPORTED, defaultVal) != 0);
1637        return tetherEnabledInSettings && mTetheringConfigValid;
1638    }
1639
1640    // 100 percent is full good, 0 is full bad.
1641    public void reportInetCondition(int networkType, int percentage) {
1642        if (DBG) Slog.d(TAG, "reportNetworkCondition(" + networkType + ", " + percentage + ")");
1643        mContext.enforceCallingOrSelfPermission(
1644                android.Manifest.permission.STATUS_BAR,
1645                "ConnectivityService");
1646
1647        mHandler.sendMessage(mHandler.obtainMessage(
1648            NetworkStateTracker.EVENT_INET_CONDITION_CHANGE, networkType, percentage));
1649    }
1650}
1651