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