EthernetDataTracker.java revision e41df2c9d479928c8aa3f1b4a3b01ff447a87116
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                    reconnect();
225                    break;
226                }
227            }
228        } catch (RemoteException e) {
229            Log.e(TAG, "Could not get list of interfaces " + e);
230        }
231
232        try {
233            service.registerObserver(mInterfaceObserver);
234        } catch (RemoteException e) {
235            Log.e(TAG, "Could not register InterfaceObserver " + e);
236        }
237    }
238
239    /**
240     * Disable connectivity to a network
241     * TODO: do away with return value after making MobileDataStateTracker async
242     */
243    public boolean teardown() {
244        mTeardownRequested.set(true);
245        NetworkUtils.stopDhcp(mIface);
246        return true;
247    }
248
249    /**
250     * Re-enable connectivity to a network after a {@link #teardown()}.
251     */
252    public boolean reconnect() {
253        if (mLinkUp) {
254            mTeardownRequested.set(false);
255            runDhcp();
256        }
257        return mLinkUp;
258    }
259
260    /**
261     * Turn the wireless radio off for a network.
262     * @param turnOn {@code true} to turn the radio on, {@code false}
263     */
264    public boolean setRadio(boolean turnOn) {
265        return true;
266    }
267
268    /**
269     * @return true - If are we currently tethered with another device.
270     */
271    public synchronized boolean isAvailable() {
272        return mNetworkInfo.isAvailable();
273    }
274
275    /**
276     * Tells the underlying networking system that the caller wants to
277     * begin using the named feature. The interpretation of {@code feature}
278     * is completely up to each networking implementation.
279     * @param feature the name of the feature to be used
280     * @param callingPid the process ID of the process that is issuing this request
281     * @param callingUid the user ID of the process that is issuing this request
282     * @return an integer value representing the outcome of the request.
283     * The interpretation of this value is specific to each networking
284     * implementation+feature combination, except that the value {@code -1}
285     * always indicates failure.
286     * TODO: needs to go away
287     */
288    public int startUsingNetworkFeature(String feature, int callingPid, int callingUid) {
289        return -1;
290    }
291
292    /**
293     * Tells the underlying networking system that the caller is finished
294     * using the named feature. The interpretation of {@code feature}
295     * is completely up to each networking implementation.
296     * @param feature the name of the feature that is no longer needed.
297     * @param callingPid the process ID of the process that is issuing this request
298     * @param callingUid the user ID of the process that is issuing this request
299     * @return an integer value representing the outcome of the request.
300     * The interpretation of this value is specific to each networking
301     * implementation+feature combination, except that the value {@code -1}
302     * always indicates failure.
303     * TODO: needs to go away
304     */
305    public int stopUsingNetworkFeature(String feature, int callingPid, int callingUid) {
306        return -1;
307    }
308
309    @Override
310    public void setUserDataEnable(boolean enabled) {
311        Log.w(TAG, "ignoring setUserDataEnable(" + enabled + ")");
312    }
313
314    @Override
315    public void setPolicyDataEnable(boolean enabled) {
316        Log.w(TAG, "ignoring setPolicyDataEnable(" + enabled + ")");
317    }
318
319    /**
320     * Check if private DNS route is set for the network
321     */
322    public boolean isPrivateDnsRouteSet() {
323        return mPrivateDnsRouteSet.get();
324    }
325
326    /**
327     * Set a flag indicating private DNS route is set
328     */
329    public void privateDnsRouteSet(boolean enabled) {
330        mPrivateDnsRouteSet.set(enabled);
331    }
332
333    /**
334     * Fetch NetworkInfo for the network
335     */
336    public synchronized NetworkInfo getNetworkInfo() {
337        return mNetworkInfo;
338    }
339
340    /**
341     * Fetch LinkProperties for the network
342     */
343    public synchronized LinkProperties getLinkProperties() {
344        return new LinkProperties(mLinkProperties);
345    }
346
347   /**
348     * A capability is an Integer/String pair, the capabilities
349     * are defined in the class LinkSocket#Key.
350     *
351     * @return a copy of this connections capabilities, may be empty but never null.
352     */
353    public LinkCapabilities getLinkCapabilities() {
354        return new LinkCapabilities(mLinkCapabilities);
355    }
356
357    /**
358     * Fetch default gateway address for the network
359     */
360    public int getDefaultGatewayAddr() {
361        return mDefaultGatewayAddr.get();
362    }
363
364    /**
365     * Check if default route is set
366     */
367    public boolean isDefaultRouteSet() {
368        return mDefaultRouteSet.get();
369    }
370
371    /**
372     * Set a flag indicating default route is set for the network
373     */
374    public void defaultRouteSet(boolean enabled) {
375        mDefaultRouteSet.set(enabled);
376    }
377
378    /**
379     * Return the system properties name associated with the tcp buffer sizes
380     * for this network.
381     */
382    public String getTcpBufferSizesPropName() {
383        return "net.tcp.buffersize.wifi";
384    }
385
386    public void setDependencyMet(boolean met) {
387        // not supported on this network
388    }
389}
390