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.bluetooth;
18
19import android.content.Context;
20import android.net.ConnectivityManager;
21import android.net.DhcpInfoInternal;
22import android.net.LinkCapabilities;
23import android.net.LinkProperties;
24import android.net.NetworkInfo;
25import android.net.NetworkInfo.DetailedState;
26import android.net.NetworkStateTracker;
27import android.net.NetworkUtils;
28import android.os.Handler;
29import android.os.Message;
30import android.util.Log;
31
32import java.util.concurrent.atomic.AtomicBoolean;
33import java.util.concurrent.atomic.AtomicInteger;
34
35/**
36 * This class tracks the data connection associated with Bluetooth
37 * reverse tethering. This is a singleton class and an instance will be
38 * created by ConnectivityService. BluetoothService will call into this
39 * when a reverse tethered connection needs to be activated.
40 *
41 * @hide
42 */
43public class BluetoothTetheringDataTracker implements NetworkStateTracker {
44    private static final String NETWORKTYPE = "BLUETOOTH_TETHER";
45    private static final String TAG = "BluetoothTethering";
46
47    private AtomicBoolean mTeardownRequested = new AtomicBoolean(false);
48    private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false);
49    private AtomicInteger mDefaultGatewayAddr = new AtomicInteger(0);
50    private AtomicBoolean mDefaultRouteSet = new AtomicBoolean(false);
51
52    private LinkProperties mLinkProperties;
53    private LinkCapabilities mLinkCapabilities;
54    private NetworkInfo mNetworkInfo;
55
56    private BluetoothPan mBluetoothPan;
57    private BluetoothDevice mDevice;
58    private static String mIface;
59
60    /* For sending events to connectivity service handler */
61    private Handler mCsHandler;
62    private Context mContext;
63    public static BluetoothTetheringDataTracker sInstance;
64
65    private BluetoothTetheringDataTracker() {
66        mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_BLUETOOTH, 0, NETWORKTYPE, "");
67        mLinkProperties = new LinkProperties();
68        mLinkCapabilities = new LinkCapabilities();
69
70        mNetworkInfo.setIsAvailable(false);
71        setTeardownRequested(false);
72    }
73
74    public static synchronized BluetoothTetheringDataTracker getInstance() {
75        if (sInstance == null) sInstance = new BluetoothTetheringDataTracker();
76        return sInstance;
77    }
78
79    public Object Clone() throws CloneNotSupportedException {
80        throw new CloneNotSupportedException();
81    }
82
83    public void setTeardownRequested(boolean isRequested) {
84        mTeardownRequested.set(isRequested);
85    }
86
87    public boolean isTeardownRequested() {
88        return mTeardownRequested.get();
89    }
90
91    /**
92     * Begin monitoring connectivity
93     */
94    public void startMonitoring(Context context, Handler target) {
95        mContext = context;
96        mCsHandler = target;
97        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
98        if (adapter != null) {
99            adapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.PAN);
100        }
101    }
102
103    private BluetoothProfile.ServiceListener mProfileServiceListener =
104        new BluetoothProfile.ServiceListener() {
105        public void onServiceConnected(int profile, BluetoothProfile proxy) {
106            mBluetoothPan = (BluetoothPan) proxy;
107        }
108        public void onServiceDisconnected(int profile) {
109            mBluetoothPan = null;
110        }
111    };
112
113    /**
114     * Disable connectivity to a network
115     * TODO: do away with return value after making MobileDataStateTracker async
116     */
117    public boolean teardown() {
118        mTeardownRequested.set(true);
119        if (mBluetoothPan != null) {
120            for (BluetoothDevice device: mBluetoothPan.getConnectedDevices()) {
121                mBluetoothPan.disconnect(device);
122            }
123        }
124        return true;
125    }
126
127    /**
128     * Re-enable connectivity to a network after a {@link #teardown()}.
129     */
130    public boolean reconnect() {
131        mTeardownRequested.set(false);
132        //Ignore
133        return true;
134    }
135
136    /**
137     * Turn the wireless radio off for a network.
138     * @param turnOn {@code true} to turn the radio on, {@code false}
139     */
140    public boolean setRadio(boolean turnOn) {
141        return true;
142    }
143
144    /**
145     * @return true - If are we currently tethered with another device.
146     */
147    public synchronized boolean isAvailable() {
148        return mNetworkInfo.isAvailable();
149    }
150
151    /**
152     * Tells the underlying networking system that the caller wants to
153     * begin using the named feature. The interpretation of {@code feature}
154     * is completely up to each networking implementation.
155     * @param feature the name of the feature to be used
156     * @param callingPid the process ID of the process that is issuing this request
157     * @param callingUid the user ID of the process that is issuing this request
158     * @return an integer value representing the outcome of the request.
159     * The interpretation of this value is specific to each networking
160     * implementation+feature combination, except that the value {@code -1}
161     * always indicates failure.
162     * TODO: needs to go away
163     */
164    public int startUsingNetworkFeature(String feature, int callingPid, int callingUid) {
165        return -1;
166    }
167
168    /**
169     * Tells the underlying networking system that the caller is finished
170     * using the named feature. The interpretation of {@code feature}
171     * is completely up to each networking implementation.
172     * @param feature the name of the feature that is no longer needed.
173     * @param callingPid the process ID of the process that is issuing this request
174     * @param callingUid the user ID of the process that is issuing this request
175     * @return an integer value representing the outcome of the request.
176     * The interpretation of this value is specific to each networking
177     * implementation+feature combination, except that the value {@code -1}
178     * always indicates failure.
179     * TODO: needs to go away
180     */
181    public int stopUsingNetworkFeature(String feature, int callingPid, int callingUid) {
182        return -1;
183    }
184
185    @Override
186    public void setUserDataEnable(boolean enabled) {
187        Log.w(TAG, "ignoring setUserDataEnable(" + enabled + ")");
188    }
189
190    @Override
191    public void setPolicyDataEnable(boolean enabled) {
192        Log.w(TAG, "ignoring setPolicyDataEnable(" + enabled + ")");
193    }
194
195    /**
196     * Check if private DNS route is set for the network
197     */
198    public boolean isPrivateDnsRouteSet() {
199        return mPrivateDnsRouteSet.get();
200    }
201
202    /**
203     * Set a flag indicating private DNS route is set
204     */
205    public void privateDnsRouteSet(boolean enabled) {
206        mPrivateDnsRouteSet.set(enabled);
207    }
208
209    /**
210     * Fetch NetworkInfo for the network
211     */
212    public synchronized NetworkInfo getNetworkInfo() {
213        return mNetworkInfo;
214    }
215
216    /**
217     * Fetch LinkProperties for the network
218     */
219    public synchronized LinkProperties getLinkProperties() {
220        return new LinkProperties(mLinkProperties);
221    }
222
223   /**
224     * A capability is an Integer/String pair, the capabilities
225     * are defined in the class LinkSocket#Key.
226     *
227     * @return a copy of this connections capabilities, may be empty but never null.
228     */
229    public LinkCapabilities getLinkCapabilities() {
230        return new LinkCapabilities(mLinkCapabilities);
231    }
232
233    /**
234     * Fetch default gateway address for the network
235     */
236    public int getDefaultGatewayAddr() {
237        return mDefaultGatewayAddr.get();
238    }
239
240    /**
241     * Check if default route is set
242     */
243    public boolean isDefaultRouteSet() {
244        return mDefaultRouteSet.get();
245    }
246
247    /**
248     * Set a flag indicating default route is set for the network
249     */
250    public void defaultRouteSet(boolean enabled) {
251        mDefaultRouteSet.set(enabled);
252    }
253
254    /**
255     * Return the system properties name associated with the tcp buffer sizes
256     * for this network.
257     */
258    public String getTcpBufferSizesPropName() {
259        return "net.tcp.buffersize.wifi";
260    }
261
262
263    public synchronized void startReverseTether(String iface, BluetoothDevice device) {
264        mIface = iface;
265        mDevice = device;
266        Thread dhcpThread = new Thread(new Runnable() {
267            public void run() {
268                //TODO(): Add callbacks for failure and success case.
269                //Currently this thread runs independently.
270                DhcpInfoInternal dhcpInfoInternal = new DhcpInfoInternal();
271                if (!NetworkUtils.runDhcp(mIface, dhcpInfoInternal)) {
272                    Log.e(TAG, "DHCP request error:" + NetworkUtils.getDhcpError());
273                    return;
274                }
275                mLinkProperties = dhcpInfoInternal.makeLinkProperties();
276                mLinkProperties.setInterfaceName(mIface);
277
278                mNetworkInfo.setIsAvailable(true);
279                mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null);
280
281                Message msg = mCsHandler.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo);
282                msg.sendToTarget();
283
284                msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo);
285                msg.sendToTarget();
286            }
287        });
288        dhcpThread.start();
289    }
290
291    public synchronized void stopReverseTether(String iface) {
292        NetworkUtils.stopDhcp(iface);
293
294        mLinkProperties.clear();
295        mNetworkInfo.setIsAvailable(false);
296        mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null);
297
298        Message msg = mCsHandler.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo);
299        msg.sendToTarget();
300
301        msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo);
302        msg.sendToTarget();
303    }
304
305    public void setDependencyMet(boolean met) {
306        // not supported on this network
307    }
308}
309