EthernetDataTracker.java revision 12b933d0d9252decaae9fee2456bb1e1cd94c085
1/*
2 * Copyright (C) 2010 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 android.content.Context;
20import android.net.ConnectivityManager;
21import android.net.DhcpInfoInternal;
22import android.net.LinkAddress;
23import android.net.LinkCapabilities;
24import android.net.LinkProperties;
25import android.net.NetworkInfo;
26import android.net.NetworkInfo.DetailedState;
27import android.net.NetworkStateTracker;
28import android.net.NetworkUtils;
29import android.os.Handler;
30import android.os.IBinder;
31import android.os.INetworkManagementService;
32import android.os.Message;
33import android.os.RemoteException;
34import android.os.ServiceManager;
35import android.util.Log;
36
37import java.net.InetAddress;
38import java.util.concurrent.atomic.AtomicBoolean;
39import java.util.concurrent.atomic.AtomicInteger;
40
41/**
42 * This class tracks the data connection associated with Ethernet
43 * This is a singleton class and an instance will be created by
44 * ConnectivityService.
45 * @hide
46 */
47public class EthernetDataTracker implements NetworkStateTracker {
48    private static final String NETWORKTYPE = "ETHERNET";
49    private static final String TAG = "Ethernet";
50
51    private AtomicBoolean mTeardownRequested = new AtomicBoolean(false);
52    private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false);
53    private AtomicInteger mDefaultGatewayAddr = new AtomicInteger(0);
54    private AtomicBoolean mDefaultRouteSet = new AtomicBoolean(false);
55
56    private static boolean mLinkUp;
57    private LinkProperties mLinkProperties;
58    private LinkCapabilities mLinkCapabilities;
59    private NetworkInfo mNetworkInfo;
60    private InterfaceObserver mInterfaceObserver;
61
62    /* For sending events to connectivity service handler */
63    private Handler mCsHandler;
64    private Context mContext;
65
66    private static EthernetDataTracker sInstance;
67    private static String sIfaceMatch = "";
68    private static String mIface = "";
69
70    private static class InterfaceObserver extends INetworkManagementEventObserver.Stub {
71        private EthernetDataTracker mTracker;
72
73        InterfaceObserver(EthernetDataTracker tracker) {
74            super();
75            mTracker = tracker;
76        }
77
78        public void interfaceStatusChanged(String iface, boolean up) {
79            Log.d(TAG, "Interface status changed: " + iface + (up ? "up" : "down"));
80        }
81
82        public void interfaceLinkStateChanged(String iface, boolean up) {
83            if (mIface.equals(iface) && mLinkUp != up) {
84                Log.d(TAG, "Interface " + iface + " link " + (up ? "up" : "down"));
85                mLinkUp = up;
86
87                // use DHCP
88                if (up) {
89                    mTracker.reconnect();
90                } else {
91                    NetworkUtils.stopDhcp(mIface);
92                    mTracker.mNetworkInfo.setIsAvailable(false);
93                    mTracker.mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED,
94                                                           null, null);
95                }
96            }
97        }
98
99        public void interfaceAdded(String iface) {
100            mTracker.interfaceAdded(iface);
101        }
102
103        public void interfaceRemoved(String iface) {
104            mTracker.interfaceRemoved(iface);
105        }
106
107        public void limitReached(String limitName, String iface) {
108            // Ignored.
109        }
110    }
111
112    private EthernetDataTracker() {
113        mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_ETHERNET, 0, NETWORKTYPE, "");
114        mLinkProperties = new LinkProperties();
115        mLinkCapabilities = new LinkCapabilities();
116        mLinkUp = false;
117
118        mNetworkInfo.setIsAvailable(false);
119        setTeardownRequested(false);
120    }
121
122    private void interfaceAdded(String iface) {
123        if (!iface.matches(sIfaceMatch))
124            return;
125
126        Log.d(TAG, "Adding " + iface);
127
128        synchronized(mIface) {
129            if(!mIface.isEmpty())
130                return;
131            mIface = iface;
132        }
133
134        mNetworkInfo.setIsAvailable(true);
135        Message msg = mCsHandler.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo);
136        msg.sendToTarget();
137
138        runDhcp();
139    }
140
141    private void interfaceRemoved(String iface) {
142        if (!iface.equals(mIface))
143            return;
144
145        Log.d(TAG, "Removing " + iface);
146
147        NetworkUtils.stopDhcp(mIface);
148
149        mLinkProperties.clear();
150        mNetworkInfo.setIsAvailable(false);
151        mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null);
152
153        Message msg = mCsHandler.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo);
154        msg.sendToTarget();
155
156        msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo);
157        msg.sendToTarget();
158
159        mIface = "";
160    }
161
162    private void runDhcp() {
163        Thread dhcpThread = new Thread(new Runnable() {
164            public void run() {
165                DhcpInfoInternal dhcpInfoInternal = new DhcpInfoInternal();
166                if (!NetworkUtils.runDhcp(mIface, dhcpInfoInternal)) {
167                    Log.e(TAG, "DHCP request error:" + NetworkUtils.getDhcpError());
168                    return;
169                }
170                mLinkProperties = dhcpInfoInternal.makeLinkProperties();
171                mLinkProperties.setInterfaceName(mIface);
172
173                mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null);
174                Message msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo);
175                msg.sendToTarget();
176            }
177        });
178        dhcpThread.start();
179    }
180
181    public static synchronized EthernetDataTracker getInstance() {
182        if (sInstance == null) sInstance = new EthernetDataTracker();
183        return sInstance;
184    }
185
186    public Object Clone() throws CloneNotSupportedException {
187        throw new CloneNotSupportedException();
188    }
189
190    public void setTeardownRequested(boolean isRequested) {
191        mTeardownRequested.set(isRequested);
192    }
193
194    public boolean isTeardownRequested() {
195        return mTeardownRequested.get();
196    }
197
198    /**
199     * Begin monitoring connectivity
200     */
201    public void startMonitoring(Context context, Handler target) {
202        mContext = context;
203        mCsHandler = target;
204
205        // register for notifications from NetworkManagement Service
206        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
207        INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
208
209        mInterfaceObserver = new InterfaceObserver(this);
210
211        // enable and try to connect to an ethernet interface that
212        // already exists
213        sIfaceMatch = context.getResources().getString(
214            com.android.internal.R.string.config_ethernet_iface_regex);
215        try {
216            final String[] ifaces = service.listInterfaces();
217            for (String iface : ifaces) {
218                if (iface.matches(sIfaceMatch)) {
219                    mIface = iface;
220                    InterfaceConfiguration config = service.getInterfaceConfig(iface);
221                    mLinkUp = config.isActive();
222                    reconnect();
223                    break;
224                }
225            }
226        } catch (RemoteException e) {
227            Log.e(TAG, "Could not get list of interfaces " + e);
228        }
229
230        try {
231            service.registerObserver(mInterfaceObserver);
232        } catch (RemoteException e) {
233            Log.e(TAG, "Could not register InterfaceObserver " + e);
234        }
235    }
236
237    /**
238     * Disable connectivity to a network
239     * TODO: do away with return value after making MobileDataStateTracker async
240     */
241    public boolean teardown() {
242        mTeardownRequested.set(true);
243        NetworkUtils.stopDhcp(mIface);
244        return true;
245    }
246
247    /**
248     * Re-enable connectivity to a network after a {@link #teardown()}.
249     */
250    public boolean reconnect() {
251        mTeardownRequested.set(false);
252        runDhcp();
253        return true;
254    }
255
256    /**
257     * Turn the wireless radio off for a network.
258     * @param turnOn {@code true} to turn the radio on, {@code false}
259     */
260    public boolean setRadio(boolean turnOn) {
261        return true;
262    }
263
264    /**
265     * @return true - If are we currently tethered with another device.
266     */
267    public synchronized boolean isAvailable() {
268        return mNetworkInfo.isAvailable();
269    }
270
271    /**
272     * Tells the underlying networking system that the caller wants to
273     * begin using the named feature. The interpretation of {@code feature}
274     * is completely up to each networking implementation.
275     * @param feature the name of the feature to be used
276     * @param callingPid the process ID of the process that is issuing this request
277     * @param callingUid the user ID of the process that is issuing this request
278     * @return an integer value representing the outcome of the request.
279     * The interpretation of this value is specific to each networking
280     * implementation+feature combination, except that the value {@code -1}
281     * always indicates failure.
282     * TODO: needs to go away
283     */
284    public int startUsingNetworkFeature(String feature, int callingPid, int callingUid) {
285        return -1;
286    }
287
288    /**
289     * Tells the underlying networking system that the caller is finished
290     * using the named feature. The interpretation of {@code feature}
291     * is completely up to each networking implementation.
292     * @param feature the name of the feature that is no longer needed.
293     * @param callingPid the process ID of the process that is issuing this request
294     * @param callingUid the user ID of the process that is issuing this request
295     * @return an integer value representing the outcome of the request.
296     * The interpretation of this value is specific to each networking
297     * implementation+feature combination, except that the value {@code -1}
298     * always indicates failure.
299     * TODO: needs to go away
300     */
301    public int stopUsingNetworkFeature(String feature, int callingPid, int callingUid) {
302        return -1;
303    }
304
305    /**
306     * @param enabled
307     */
308    public void setDataEnable(boolean enabled) {
309        Log.d(TAG, "setDataEnabled: IGNORING enabled=" + enabled);
310    }
311
312    /**
313     * Check if private DNS route is set for the network
314     */
315    public boolean isPrivateDnsRouteSet() {
316        return mPrivateDnsRouteSet.get();
317    }
318
319    /**
320     * Set a flag indicating private DNS route is set
321     */
322    public void privateDnsRouteSet(boolean enabled) {
323        mPrivateDnsRouteSet.set(enabled);
324    }
325
326    /**
327     * Fetch NetworkInfo for the network
328     */
329    public synchronized NetworkInfo getNetworkInfo() {
330        return mNetworkInfo;
331    }
332
333    /**
334     * Fetch LinkProperties for the network
335     */
336    public synchronized LinkProperties getLinkProperties() {
337        return new LinkProperties(mLinkProperties);
338    }
339
340   /**
341     * A capability is an Integer/String pair, the capabilities
342     * are defined in the class LinkSocket#Key.
343     *
344     * @return a copy of this connections capabilities, may be empty but never null.
345     */
346    public LinkCapabilities getLinkCapabilities() {
347        return new LinkCapabilities(mLinkCapabilities);
348    }
349
350    /**
351     * Fetch default gateway address for the network
352     */
353    public int getDefaultGatewayAddr() {
354        return mDefaultGatewayAddr.get();
355    }
356
357    /**
358     * Check if default route is set
359     */
360    public boolean isDefaultRouteSet() {
361        return mDefaultRouteSet.get();
362    }
363
364    /**
365     * Set a flag indicating default route is set for the network
366     */
367    public void defaultRouteSet(boolean enabled) {
368        mDefaultRouteSet.set(enabled);
369    }
370
371    /**
372     * Return the system properties name associated with the tcp buffer sizes
373     * for this network.
374     */
375    public String getTcpBufferSizesPropName() {
376        return "net.tcp.buffersize.wifi";
377    }
378
379    public void setDependencyMet(boolean met) {
380        // not supported on this network
381    }
382}
383