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