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