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