NetworkStateTracker.java revision d1aca6aa73b0f1e22229829db89b1f3513a16aa8
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 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) Log.d(TAG, "addPrivateDnsRoutes for " + this +
127                "(" + mInterfaceName + ") - mPrivateDnsRouteSet = "+mPrivateDnsRouteSet);
128        if (mInterfaceName != null && !mPrivateDnsRouteSet) {
129            for (String addrString : getNameServers()) {
130                int addr = NetworkUtils.lookupHost(addrString);
131                if (addr != -1 && addr != 0) {
132                    if (DBG) Log.d(TAG, "  adding "+addrString+" ("+addr+")");
133                    NetworkUtils.addHostRoute(mInterfaceName, addr);
134                }
135            }
136            mPrivateDnsRouteSet = true;
137        }
138    }
139
140    public void removePrivateDnsRoutes() {
141        if (DBG) Log.d(TAG, "removePrivateDnsRoutes for " + this +
142                "(" + mInterfaceName + ")");
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            NetworkUtils.removeHostRoutes(mInterfaceName);
147            mPrivateDnsRouteSet = false;
148        }
149    }
150
151    public void addDefaultRoute() {
152        if (DBG) Log.d(TAG, "addDefaultRoute for " + this + "(" +
153                mInterfaceName + "), GatewayAddr=" + mDefaultGatewayAddr);
154        if ((mInterfaceName != null) && (mDefaultGatewayAddr != 0) &&
155                mDefaultRouteSet == false) {
156            NetworkUtils.setDefaultRoute(mInterfaceName, mDefaultGatewayAddr);
157            mDefaultRouteSet = true;
158        }
159    }
160
161    public void removeDefaultRoute() {
162        if (DBG) Log.d(TAG, "removeDefaultRoute for " + this + "(" +
163                mInterfaceName + ")");
164        if (mInterfaceName != null && mDefaultRouteSet == true) {
165            NetworkUtils.removeDefaultRoute(mInterfaceName);
166            mDefaultRouteSet = false;
167        }
168    }
169
170    /**
171     * Reads the network specific TCP buffer sizes from SystemProperties
172     * net.tcp.buffersize.[default|wifi|umts|edge|gprs] and set them for system
173     * wide use
174     */
175   public void updateNetworkSettings() {
176        String key = getTcpBufferSizesPropName();
177        String bufferSizes = SystemProperties.get(key);
178
179        if (bufferSizes.length() == 0) {
180            Log.e(TAG, key + " not found in system properties. Using defaults");
181
182            // Setting to default values so we won't be stuck to previous values
183            key = "net.tcp.buffersize.default";
184            bufferSizes = SystemProperties.get(key);
185        }
186
187        // Set values in kernel
188        if (bufferSizes.length() != 0) {
189            if (DBG) {
190                Log.v(TAG, "Setting TCP values: [" + bufferSizes
191                        + "] which comes from [" + key + "]");
192            }
193            setBufferSize(bufferSizes);
194        }
195    }
196
197    /**
198     * Release the wakelock, if any, that may be held while handling a
199     * disconnect operation.
200     */
201    public void releaseWakeLock() {
202    }
203
204    /**
205     * Writes TCP buffer sizes to /sys/kernel/ipv4/tcp_[r/w]mem_[min/def/max]
206     * which maps to /proc/sys/net/ipv4/tcp_rmem and tcpwmem
207     *
208     * @param bufferSizes in the format of "readMin, readInitial, readMax,
209     *        writeMin, writeInitial, writeMax"
210     */
211    private void setBufferSize(String bufferSizes) {
212        try {
213            String[] values = bufferSizes.split(",");
214
215            if (values.length == 6) {
216              final String prefix = "/sys/kernel/ipv4/tcp_";
217                stringToFile(prefix + "rmem_min", values[0]);
218                stringToFile(prefix + "rmem_def", values[1]);
219                stringToFile(prefix + "rmem_max", values[2]);
220                stringToFile(prefix + "wmem_min", values[3]);
221                stringToFile(prefix + "wmem_def", values[4]);
222                stringToFile(prefix + "wmem_max", values[5]);
223            } else {
224                Log.e(TAG, "Invalid buffersize string: " + bufferSizes);
225            }
226        } catch (IOException e) {
227            Log.e(TAG, "Can't set tcp buffer sizes:" + e);
228        }
229    }
230
231    /**
232     * Writes string to file. Basically same as "echo -n $string > $filename"
233     *
234     * @param filename
235     * @param string
236     * @throws IOException
237     */
238    private void stringToFile(String filename, String string) throws IOException {
239        FileWriter out = new FileWriter(filename);
240        try {
241            out.write(string);
242        } finally {
243            out.close();
244        }
245    }
246
247    /**
248     * Record the detailed state of a network, and if it is a
249     * change from the previous state, send a notification to
250     * any listeners.
251     * @param state the new @{code DetailedState}
252     */
253    public void setDetailedState(NetworkInfo.DetailedState state) {
254        setDetailedState(state, null, null);
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     * @param reason a {@code String} indicating a reason for the state change,
263     * if one was supplied. May be {@code null}.
264     * @param extraInfo optional {@code String} providing extra information about the state change
265     */
266    public void setDetailedState(NetworkInfo.DetailedState state, String reason, String extraInfo) {
267        if (DBG) Log.d(TAG, "setDetailed state, old ="+mNetworkInfo.getDetailedState()+" and new state="+state);
268        if (state != mNetworkInfo.getDetailedState()) {
269            boolean wasConnecting = (mNetworkInfo.getState() == NetworkInfo.State.CONNECTING);
270            String lastReason = mNetworkInfo.getReason();
271            /*
272             * If a reason was supplied when the CONNECTING state was entered, and no
273             * reason was supplied for entering the CONNECTED state, then retain the
274             * reason that was supplied when going to CONNECTING.
275             */
276            if (wasConnecting && state == NetworkInfo.DetailedState.CONNECTED && reason == null
277                    && lastReason != null)
278                reason = lastReason;
279            mNetworkInfo.setDetailedState(state, reason, extraInfo);
280            Message msg = mTarget.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo);
281            msg.sendToTarget();
282        }
283    }
284
285    protected void setDetailedStateInternal(NetworkInfo.DetailedState state) {
286        mNetworkInfo.setDetailedState(state, null, null);
287    }
288
289    public void setTeardownRequested(boolean isRequested) {
290        mTeardownRequested = isRequested;
291    }
292
293    public boolean isTeardownRequested() {
294        return mTeardownRequested;
295    }
296
297    /**
298     * Send a  notification that the results of a scan for network access
299     * points has completed, and results are available.
300     */
301    protected void sendScanResultsAvailable() {
302        Message msg = mTarget.obtainMessage(EVENT_SCAN_RESULTS_AVAILABLE, mNetworkInfo);
303        msg.sendToTarget();
304    }
305
306    /**
307     * Record the roaming status of the device, and if it is a change from the previous
308     * status, send a notification to any listeners.
309     * @param isRoaming {@code true} if the device is now roaming, {@code false}
310     * if it is no longer roaming.
311     */
312    protected void setRoamingStatus(boolean isRoaming) {
313        if (isRoaming != mNetworkInfo.isRoaming()) {
314            mNetworkInfo.setRoaming(isRoaming);
315            Message msg = mTarget.obtainMessage(EVENT_ROAMING_CHANGED, mNetworkInfo);
316            msg.sendToTarget();
317        }
318    }
319
320    protected void setSubtype(int subtype, String subtypeName) {
321        if (mNetworkInfo.isConnected()) {
322            int oldSubtype = mNetworkInfo.getSubtype();
323            if (subtype != oldSubtype) {
324                mNetworkInfo.setSubtype(subtype, subtypeName);
325                Message msg = mTarget.obtainMessage(
326                        EVENT_NETWORK_SUBTYPE_CHANGED, oldSubtype, 0, mNetworkInfo);
327                msg.sendToTarget();
328            }
329        }
330    }
331
332    public abstract void startMonitoring();
333
334    /**
335     * Disable connectivity to a network
336     * @return {@code true} if a teardown occurred, {@code false} if the
337     * teardown did not occur.
338     */
339    public abstract boolean teardown();
340
341    /**
342     * Reenable connectivity to a network after a {@link #teardown()}.
343     */
344    public abstract boolean reconnect();
345
346    /**
347     * Turn the wireless radio off for a network.
348     * @param turnOn {@code true} to turn the radio on, {@code false}
349     */
350    public abstract boolean setRadio(boolean turnOn);
351
352    /**
353     * Returns an indication of whether this network is available for
354     * connections. A value of {@code false} means that some quasi-permanent
355     * condition prevents connectivity to this network.
356     */
357    public abstract boolean isAvailable();
358
359    /**
360     * Tells the underlying networking system that the caller wants to
361     * begin using the named feature. The interpretation of {@code feature}
362     * is completely up to each networking implementation.
363     * @param feature the name of the feature to be used
364     * @param callingPid the process ID of the process that is issuing this request
365     * @param callingUid the user ID of the process that is issuing this request
366     * @return an integer value representing the outcome of the request.
367     * The interpretation of this value is specific to each networking
368     * implementation+feature combination, except that the value {@code -1}
369     * always indicates failure.
370     */
371    public abstract int startUsingNetworkFeature(String feature, int callingPid, int callingUid);
372
373    /**
374     * Tells the underlying networking system that the caller is finished
375     * using the named feature. The interpretation of {@code feature}
376     * is completely up to each networking implementation.
377     * @param feature the name of the feature that is no longer needed.
378     * @param callingPid the process ID of the process that is issuing this request
379     * @param callingUid the user ID of the process that is issuing this request
380     * @return an integer value representing the outcome of the request.
381     * The interpretation of this value is specific to each networking
382     * implementation+feature combination, except that the value {@code -1}
383     * always indicates failure.
384     */
385    public abstract int stopUsingNetworkFeature(String feature, int callingPid, int callingUid);
386
387    /**
388     * Ensure that a network route exists to deliver traffic to the specified
389     * host via this network interface.
390     * @param hostAddress the IP address of the host to which the route is desired
391     * @return {@code true} on success, {@code false} on failure
392     */
393    public boolean requestRouteToHost(int hostAddress) {
394        return false;
395    }
396
397    /**
398     * Interprets scan results. This will be called at a safe time for
399     * processing, and from a safe thread.
400     */
401    public void interpretScanResultsAvailable() {
402    }
403
404}
405