Home | History | Annotate | Download | only in net
      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 
     17 package android.net;
     18 
     19 import java.io.FileWriter;
     20 import java.io.IOException;
     21 
     22 import android.os.Handler;
     23 import android.os.Message;
     24 import android.os.SystemProperties;
     25 import android.content.Context;
     26 import android.text.TextUtils;
     27 import android.util.Config;
     28 import android.util.Log;
     29 
     30 
     31 /**
     32  * Each subclass of this class keeps track of the state of connectivity
     33  * of a network interface. All state information for a network should
     34  * be kept in a Tracker class. This superclass manages the
     35  * network-type-independent aspects of network state.
     36  *
     37  * {@hide}
     38  */
     39 public abstract class NetworkStateTracker extends Handler {
     40 
     41     protected NetworkInfo mNetworkInfo;
     42     protected Context mContext;
     43     protected Handler mTarget;
     44     protected String mInterfaceName;
     45     protected String[] mDnsPropNames;
     46     private boolean mPrivateDnsRouteSet;
     47     protected int mDefaultGatewayAddr;
     48     private boolean mTeardownRequested;
     49 
     50     private static boolean DBG = false;
     51     private static final String TAG = "NetworkStateTracker";
     52 
     53     // Share the event space with ConnectivityService (which we can't see, but
     54     // must send events to).  If you change these, change ConnectivityService
     55     // too.
     56     private static final int MIN_NETWORK_STATE_TRACKER_EVENT = 1;
     57     private static final int MAX_NETWORK_STATE_TRACKER_EVENT = 100;
     58 
     59     public static final int EVENT_STATE_CHANGED = 1;
     60     public static final int EVENT_SCAN_RESULTS_AVAILABLE = 2;
     61     /**
     62      * arg1: 1 to show, 0 to hide
     63      * arg2: ID of the notification
     64      * obj: Notification (if showing)
     65      */
     66     public static final int EVENT_NOTIFICATION_CHANGED = 3;
     67     public static final int EVENT_CONFIGURATION_CHANGED = 4;
     68     public static final int EVENT_ROAMING_CHANGED = 5;
     69     public static final int EVENT_NETWORK_SUBTYPE_CHANGED = 6;
     70 
     71     public NetworkStateTracker(Context context,
     72             Handler target,
     73             int networkType,
     74             int subType,
     75             String typeName,
     76             String subtypeName) {
     77         super();
     78         mContext = context;
     79         mTarget = target;
     80         mTeardownRequested = false;
     81 
     82         this.mNetworkInfo = new NetworkInfo(networkType, subType, typeName, subtypeName);
     83     }
     84 
     85     public NetworkInfo getNetworkInfo() {
     86         return mNetworkInfo;
     87     }
     88 
     89     /**
     90      * Return the system properties name associated with the tcp buffer sizes
     91      * for this network.
     92      */
     93     public abstract String getTcpBufferSizesPropName();
     94 
     95     /**
     96      * Return the IP addresses of the DNS servers available for the mobile data
     97      * network interface.
     98      * @return a list of DNS addresses, with no holes.
     99      */
    100     public String[] getNameServers() {
    101         return getNameServerList(mDnsPropNames);
    102     }
    103 
    104     /**
    105      * Return the IP addresses of the DNS servers available for this
    106      * network interface.
    107      * @param propertyNames the names of the system properties whose values
    108      * give the IP addresses. Properties with no values are skipped.
    109      * @return an array of {@code String}s containing the IP addresses
    110      * of the DNS servers, in dot-notation. This may have fewer
    111      * non-null entries than the list of names passed in, since
    112      * some of the passed-in names may have empty values.
    113      */
    114     static protected String[] getNameServerList(String[] propertyNames) {
    115         String[] dnsAddresses = new String[propertyNames.length];
    116         int i, j;
    117 
    118         for (i = 0, j = 0; i < propertyNames.length; i++) {
    119             String value = SystemProperties.get(propertyNames[i]);
    120             // The GSM layer sometimes sets a bogus DNS server address of
    121             // 0.0.0.0
    122             if (!TextUtils.isEmpty(value) && !TextUtils.equals(value, "0.0.0.0")) {
    123                 dnsAddresses[j++] = value;
    124             }
    125         }
    126         return dnsAddresses;
    127     }
    128 
    129     public void addPrivateDnsRoutes() {
    130         if (DBG) {
    131             Log.d(TAG, "addPrivateDnsRoutes for " + this +
    132                     "(" + mInterfaceName + ") - mPrivateDnsRouteSet = "+mPrivateDnsRouteSet);
    133         }
    134         if (mInterfaceName != null && !mPrivateDnsRouteSet) {
    135             for (String addrString : getNameServers()) {
    136                 int addr = NetworkUtils.lookupHost(addrString);
    137                 if (addr != -1 && addr != 0) {
    138                     if (DBG) Log.d(TAG, "  adding "+addrString+" ("+addr+")");
    139                     NetworkUtils.addHostRoute(mInterfaceName, addr);
    140                 }
    141             }
    142             mPrivateDnsRouteSet = true;
    143         }
    144     }
    145 
    146     public void removePrivateDnsRoutes() {
    147         // TODO - we should do this explicitly but the NetUtils api doesnt
    148         // support this yet - must remove all.  No worse than before
    149         if (mInterfaceName != null && mPrivateDnsRouteSet) {
    150             if (DBG) {
    151                 Log.d(TAG, "removePrivateDnsRoutes for " + mNetworkInfo.getTypeName() +
    152                         " (" + mInterfaceName + ")");
    153             }
    154             NetworkUtils.removeHostRoutes(mInterfaceName);
    155             mPrivateDnsRouteSet = false;
    156         }
    157     }
    158 
    159     public void addDefaultRoute() {
    160         if ((mInterfaceName != null) && (mDefaultGatewayAddr != 0)) {
    161             if (DBG) {
    162                 Log.d(TAG, "addDefaultRoute for " + mNetworkInfo.getTypeName() +
    163                         " (" + mInterfaceName + "), GatewayAddr=" + mDefaultGatewayAddr);
    164             }
    165             NetworkUtils.addHostRoute(mInterfaceName, mDefaultGatewayAddr);
    166             NetworkUtils.setDefaultRoute(mInterfaceName, mDefaultGatewayAddr);
    167         }
    168     }
    169 
    170     public void removeDefaultRoute() {
    171         if (mInterfaceName != null) {
    172             if (DBG) {
    173                 Log.d(TAG, "removeDefaultRoute for " + mNetworkInfo.getTypeName() + " (" +
    174                         mInterfaceName + ")");
    175             }
    176             NetworkUtils.removeDefaultRoute(mInterfaceName);
    177         }
    178     }
    179 
    180     /**
    181      * Reads the network specific TCP buffer sizes from SystemProperties
    182      * net.tcp.buffersize.[default|wifi|umts|edge|gprs] and set them for system
    183      * wide use
    184      */
    185    public void updateNetworkSettings() {
    186         String key = getTcpBufferSizesPropName();
    187         String bufferSizes = SystemProperties.get(key);
    188 
    189         if (bufferSizes.length() == 0) {
    190             Log.w(TAG, key + " not found in system properties. Using defaults");
    191 
    192             // Setting to default values so we won't be stuck to previous values
    193             key = "net.tcp.buffersize.default";
    194             bufferSizes = SystemProperties.get(key);
    195         }
    196 
    197         // Set values in kernel
    198         if (bufferSizes.length() != 0) {
    199             if (DBG) {
    200                 Log.v(TAG, "Setting TCP values: [" + bufferSizes
    201                         + "] which comes from [" + key + "]");
    202             }
    203             setBufferSize(bufferSizes);
    204         }
    205     }
    206 
    207     /**
    208      * Release the wakelock, if any, that may be held while handling a
    209      * disconnect operation.
    210      */
    211     public void releaseWakeLock() {
    212     }
    213 
    214     /**
    215      * Writes TCP buffer sizes to /sys/kernel/ipv4/tcp_[r/w]mem_[min/def/max]
    216      * which maps to /proc/sys/net/ipv4/tcp_rmem and tcpwmem
    217      *
    218      * @param bufferSizes in the format of "readMin, readInitial, readMax,
    219      *        writeMin, writeInitial, writeMax"
    220      */
    221     private void setBufferSize(String bufferSizes) {
    222         try {
    223             String[] values = bufferSizes.split(",");
    224 
    225             if (values.length == 6) {
    226               final String prefix = "/sys/kernel/ipv4/tcp_";
    227                 stringToFile(prefix + "rmem_min", values[0]);
    228                 stringToFile(prefix + "rmem_def", values[1]);
    229                 stringToFile(prefix + "rmem_max", values[2]);
    230                 stringToFile(prefix + "wmem_min", values[3]);
    231                 stringToFile(prefix + "wmem_def", values[4]);
    232                 stringToFile(prefix + "wmem_max", values[5]);
    233             } else {
    234                 Log.w(TAG, "Invalid buffersize string: " + bufferSizes);
    235             }
    236         } catch (IOException e) {
    237             Log.w(TAG, "Can't set tcp buffer sizes:" + e);
    238         }
    239     }
    240 
    241     /**
    242      * Writes string to file. Basically same as "echo -n $string > $filename"
    243      *
    244      * @param filename
    245      * @param string
    246      * @throws IOException
    247      */
    248     private void stringToFile(String filename, String string) throws IOException {
    249         FileWriter out = new FileWriter(filename);
    250         try {
    251             out.write(string);
    252         } finally {
    253             out.close();
    254         }
    255     }
    256 
    257     /**
    258      * Record the detailed state of a network, and if it is a
    259      * change from the previous state, send a notification to
    260      * any listeners.
    261      * @param state the new @{code DetailedState}
    262      */
    263     public void setDetailedState(NetworkInfo.DetailedState state) {
    264         setDetailedState(state, null, null);
    265     }
    266 
    267     /**
    268      * Record the detailed state of a network, and if it is a
    269      * change from the previous state, send a notification to
    270      * any listeners.
    271      * @param state the new @{code DetailedState}
    272      * @param reason a {@code String} indicating a reason for the state change,
    273      * if one was supplied. May be {@code null}.
    274      * @param extraInfo optional {@code String} providing extra information about the state change
    275      */
    276     public void setDetailedState(NetworkInfo.DetailedState state, String reason, String extraInfo) {
    277         if (DBG) Log.d(TAG, "setDetailed state, old ="+mNetworkInfo.getDetailedState()+" and new state="+state);
    278         if (state != mNetworkInfo.getDetailedState()) {
    279             boolean wasConnecting = (mNetworkInfo.getState() == NetworkInfo.State.CONNECTING);
    280             String lastReason = mNetworkInfo.getReason();
    281             /*
    282              * If a reason was supplied when the CONNECTING state was entered, and no
    283              * reason was supplied for entering the CONNECTED state, then retain the
    284              * reason that was supplied when going to CONNECTING.
    285              */
    286             if (wasConnecting && state == NetworkInfo.DetailedState.CONNECTED && reason == null
    287                     && lastReason != null)
    288                 reason = lastReason;
    289             mNetworkInfo.setDetailedState(state, reason, extraInfo);
    290             Message msg = mTarget.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo);
    291             msg.sendToTarget();
    292         }
    293     }
    294 
    295     protected void setDetailedStateInternal(NetworkInfo.DetailedState state) {
    296         mNetworkInfo.setDetailedState(state, null, null);
    297     }
    298 
    299     public void setTeardownRequested(boolean isRequested) {
    300         mTeardownRequested = isRequested;
    301     }
    302 
    303     public boolean isTeardownRequested() {
    304         return mTeardownRequested;
    305     }
    306 
    307     /**
    308      * Send a  notification that the results of a scan for network access
    309      * points has completed, and results are available.
    310      */
    311     protected void sendScanResultsAvailable() {
    312         Message msg = mTarget.obtainMessage(EVENT_SCAN_RESULTS_AVAILABLE, mNetworkInfo);
    313         msg.sendToTarget();
    314     }
    315 
    316     /**
    317      * Record the roaming status of the device, and if it is a change from the previous
    318      * status, send a notification to any listeners.
    319      * @param isRoaming {@code true} if the device is now roaming, {@code false}
    320      * if it is no longer roaming.
    321      */
    322     protected void setRoamingStatus(boolean isRoaming) {
    323         if (isRoaming != mNetworkInfo.isRoaming()) {
    324             mNetworkInfo.setRoaming(isRoaming);
    325             Message msg = mTarget.obtainMessage(EVENT_ROAMING_CHANGED, mNetworkInfo);
    326             msg.sendToTarget();
    327         }
    328     }
    329 
    330     protected void setSubtype(int subtype, String subtypeName) {
    331         int oldSubtype = mNetworkInfo.getSubtype();
    332         if (subtype != oldSubtype) {
    333             mNetworkInfo.setSubtype(subtype, subtypeName);
    334             if (mNetworkInfo.isConnected()) {
    335                 Message msg = mTarget.obtainMessage(
    336                         EVENT_NETWORK_SUBTYPE_CHANGED, oldSubtype, 0, mNetworkInfo);
    337                 msg.sendToTarget();
    338             }
    339         }
    340     }
    341 
    342     public abstract void startMonitoring();
    343 
    344     /**
    345      * Disable connectivity to a network
    346      * @return {@code true} if a teardown occurred, {@code false} if the
    347      * teardown did not occur.
    348      */
    349     public abstract boolean teardown();
    350 
    351     /**
    352      * Reenable connectivity to a network after a {@link #teardown()}.
    353      */
    354     public abstract boolean reconnect();
    355 
    356     /**
    357      * Turn the wireless radio off for a network.
    358      * @param turnOn {@code true} to turn the radio on, {@code false}
    359      */
    360     public abstract boolean setRadio(boolean turnOn);
    361 
    362     /**
    363      * Returns an indication of whether this network is available for
    364      * connections. A value of {@code false} means that some quasi-permanent
    365      * condition prevents connectivity to this network.
    366      */
    367     public abstract boolean isAvailable();
    368 
    369     /**
    370      * Tells the underlying networking system that the caller wants to
    371      * begin using the named feature. The interpretation of {@code feature}
    372      * is completely up to each networking implementation.
    373      * @param feature the name of the feature to be used
    374      * @param callingPid the process ID of the process that is issuing this request
    375      * @param callingUid the user ID of the process that is issuing this request
    376      * @return an integer value representing the outcome of the request.
    377      * The interpretation of this value is specific to each networking
    378      * implementation+feature combination, except that the value {@code -1}
    379      * always indicates failure.
    380      */
    381     public abstract int startUsingNetworkFeature(String feature, int callingPid, int callingUid);
    382 
    383     /**
    384      * Tells the underlying networking system that the caller is finished
    385      * using the named feature. The interpretation of {@code feature}
    386      * is completely up to each networking implementation.
    387      * @param feature the name of the feature that is no longer needed.
    388      * @param callingPid the process ID of the process that is issuing this request
    389      * @param callingUid the user ID of the process that is issuing this request
    390      * @return an integer value representing the outcome of the request.
    391      * The interpretation of this value is specific to each networking
    392      * implementation+feature combination, except that the value {@code -1}
    393      * always indicates failure.
    394      */
    395     public abstract int stopUsingNetworkFeature(String feature, int callingPid, int callingUid);
    396 
    397     /**
    398      * Ensure that a network route exists to deliver traffic to the specified
    399      * host via this network interface.
    400      * @param hostAddress the IP address of the host to which the route is desired
    401      * @return {@code true} on success, {@code false} on failure
    402      */
    403     public boolean requestRouteToHost(int hostAddress) {
    404         return false;
    405     }
    406 
    407     /**
    408      * Interprets scan results. This will be called at a safe time for
    409      * processing, and from a safe thread.
    410      */
    411     public void interpretScanResultsAvailable() {
    412     }
    413 
    414     public String getInterfaceName() {
    415         return mInterfaceName;
    416     }
    417 }
    418