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