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