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