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