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