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