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.os.IBinder;
20import android.os.ServiceManager;
21import android.os.INetworkManagementService;
22import android.content.Context;
23import android.net.ConnectivityManager;
24import android.net.DhcpResults;
25import android.net.LinkCapabilities;
26import android.net.LinkProperties;
27import android.net.NetworkInfo;
28import android.net.NetworkInfo.DetailedState;
29import android.net.NetworkStateTracker;
30import android.net.NetworkUtils;
31import android.os.Handler;
32import android.os.Looper;
33import android.os.Message;
34import android.os.Messenger;
35import android.text.TextUtils;
36import android.util.Log;
37import java.net.InterfaceAddress;
38import android.net.LinkAddress;
39import android.net.RouteInfo;
40import java.net.Inet4Address;
41import android.os.SystemProperties;
42
43import com.android.internal.util.AsyncChannel;
44
45import java.util.concurrent.atomic.AtomicBoolean;
46import java.util.concurrent.atomic.AtomicInteger;
47import java.util.concurrent.atomic.AtomicReference;
48
49/**
50 * This class tracks the data connection associated with Bluetooth
51 * reverse tethering. This is a singleton class and an instance will be
52 * created by ConnectivityService. BluetoothService will call into this
53 * when a reverse tethered connection needs to be activated.
54 *
55 * @hide
56 */
57public class BluetoothTetheringDataTracker implements NetworkStateTracker {
58    private static final String NETWORKTYPE = "BLUETOOTH_TETHER";
59    private static final String TAG = "BluetoothTethering";
60    private static final boolean DBG = true;
61    private static final boolean VDBG = true;
62
63    private AtomicBoolean mTeardownRequested = new AtomicBoolean(false);
64    private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false);
65    private AtomicInteger mDefaultGatewayAddr = new AtomicInteger(0);
66    private AtomicBoolean mDefaultRouteSet = new AtomicBoolean(false);
67
68    private final Object mLinkPropertiesLock = new Object();
69    private LinkProperties mLinkProperties;
70
71    private LinkCapabilities mLinkCapabilities;
72
73    private final Object mNetworkInfoLock = new Object();
74    private NetworkInfo mNetworkInfo;
75
76    private BluetoothPan mBluetoothPan;
77    private static String mRevTetheredIface;
78    /* For sending events to connectivity service handler */
79    private Handler mCsHandler;
80    protected Context mContext;
81    private static BluetoothTetheringDataTracker sInstance;
82    private BtdtHandler mBtdtHandler;
83    private AtomicReference<AsyncChannel> mAsyncChannel = new AtomicReference<AsyncChannel>(null);
84
85    private BluetoothTetheringDataTracker() {
86        mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_BLUETOOTH, 0, NETWORKTYPE, "");
87        mLinkProperties = new LinkProperties();
88        mLinkCapabilities = new LinkCapabilities();
89
90        mNetworkInfo.setIsAvailable(false);
91        setTeardownRequested(false);
92    }
93
94    public static synchronized BluetoothTetheringDataTracker getInstance() {
95        if (sInstance == null) sInstance = new BluetoothTetheringDataTracker();
96        return sInstance;
97    }
98
99    public Object Clone() throws CloneNotSupportedException {
100        throw new CloneNotSupportedException();
101    }
102
103    public void setTeardownRequested(boolean isRequested) {
104        mTeardownRequested.set(isRequested);
105    }
106
107    public boolean isTeardownRequested() {
108        return mTeardownRequested.get();
109    }
110
111    /**
112     * Begin monitoring connectivity
113     */
114    public void startMonitoring(Context context, Handler target) {
115        if (DBG) Log.d(TAG, "startMonitoring: target: " + target);
116        mContext = context;
117        mCsHandler = target;
118        if (VDBG) Log.d(TAG, "startMonitoring: mCsHandler: " + mCsHandler);
119        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
120        if (adapter != null) {
121            adapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.PAN);
122        }
123        mBtdtHandler = new BtdtHandler(target.getLooper(), this);
124    }
125
126    private BluetoothProfile.ServiceListener mProfileServiceListener =
127        new BluetoothProfile.ServiceListener() {
128        public void onServiceConnected(int profile, BluetoothProfile proxy) {
129            mBluetoothPan = (BluetoothPan) proxy;
130        }
131        public void onServiceDisconnected(int profile) {
132            mBluetoothPan = null;
133        }
134    };
135
136    /**
137     * Disable connectivity to a network
138     * TODO: do away with return value after making MobileDataStateTracker async
139     */
140    public boolean teardown() {
141        mTeardownRequested.set(true);
142        if (mBluetoothPan != null) {
143            for (BluetoothDevice device: mBluetoothPan.getConnectedDevices()) {
144                mBluetoothPan.disconnect(device);
145            }
146        }
147        return true;
148    }
149
150    @Override
151    public void captivePortalCheckComplete() {
152        // not implemented
153    }
154
155    /**
156     * Re-enable connectivity to a network after a {@link #teardown()}.
157     */
158    public boolean reconnect() {
159        mTeardownRequested.set(false);
160        //Ignore
161        return true;
162    }
163
164    /**
165     * Turn the wireless radio off for a network.
166     * @param turnOn {@code true} to turn the radio on, {@code false}
167     */
168    public boolean setRadio(boolean turnOn) {
169        return true;
170    }
171
172    /**
173     * @return true - If are we currently tethered with another device.
174     */
175    public synchronized boolean isAvailable() {
176        return mNetworkInfo.isAvailable();
177    }
178
179    /**
180     * Tells the underlying networking system that the caller wants to
181     * begin using the named feature. The interpretation of {@code feature}
182     * is completely up to each networking implementation.
183     * @param feature the name of the feature to be used
184     * @param callingPid the process ID of the process that is issuing this request
185     * @param callingUid the user ID of the process that is issuing this request
186     * @return an integer value representing the outcome of the request.
187     * The interpretation of this value is specific to each networking
188     * implementation+feature combination, except that the value {@code -1}
189     * always indicates failure.
190     * TODO: needs to go away
191     */
192    public int startUsingNetworkFeature(String feature, int callingPid, int callingUid) {
193        return -1;
194    }
195
196    /**
197     * Tells the underlying networking system that the caller is finished
198     * using the named feature. The interpretation of {@code feature}
199     * is completely up to each networking implementation.
200     * @param feature the name of the feature that is no longer needed.
201     * @param callingPid the process ID of the process that is issuing this request
202     * @param callingUid the user ID of the process that is issuing this request
203     * @return an integer value representing the outcome of the request.
204     * The interpretation of this value is specific to each networking
205     * implementation+feature combination, except that the value {@code -1}
206     * always indicates failure.
207     * TODO: needs to go away
208     */
209    public int stopUsingNetworkFeature(String feature, int callingPid, int callingUid) {
210        return -1;
211    }
212
213    @Override
214    public void setUserDataEnable(boolean enabled) {
215        Log.w(TAG, "ignoring setUserDataEnable(" + enabled + ")");
216    }
217
218    @Override
219    public void setPolicyDataEnable(boolean enabled) {
220        Log.w(TAG, "ignoring setPolicyDataEnable(" + enabled + ")");
221    }
222
223    /**
224     * Check if private DNS route is set for the network
225     */
226    public boolean isPrivateDnsRouteSet() {
227        return mPrivateDnsRouteSet.get();
228    }
229
230    /**
231     * Set a flag indicating private DNS route is set
232     */
233    public void privateDnsRouteSet(boolean enabled) {
234        mPrivateDnsRouteSet.set(enabled);
235    }
236
237    /**
238     * Fetch NetworkInfo for the network
239     */
240    public NetworkInfo getNetworkInfo() {
241        synchronized (mNetworkInfoLock) {
242            return new NetworkInfo(mNetworkInfo);
243        }
244    }
245
246    /**
247     * Fetch LinkProperties for the network
248     */
249    public LinkProperties getLinkProperties() {
250        synchronized (mLinkPropertiesLock) {
251            return new LinkProperties(mLinkProperties);
252        }
253    }
254
255   /**
256     * A capability is an Integer/String pair, the capabilities
257     * are defined in the class LinkSocket#Key.
258     *
259     * @return a copy of this connections capabilities, may be empty but never null.
260     */
261    public LinkCapabilities getLinkCapabilities() {
262        return new LinkCapabilities(mLinkCapabilities);
263    }
264
265    /**
266     * Fetch default gateway address for the network
267     */
268    public int getDefaultGatewayAddr() {
269        return mDefaultGatewayAddr.get();
270    }
271
272    /**
273     * Check if default route is set
274     */
275    public boolean isDefaultRouteSet() {
276        return mDefaultRouteSet.get();
277    }
278
279    /**
280     * Set a flag indicating default route is set for the network
281     */
282    public void defaultRouteSet(boolean enabled) {
283        mDefaultRouteSet.set(enabled);
284    }
285
286    /**
287     * Return the system properties name associated with the tcp buffer sizes
288     * for this network.
289     */
290    public String getTcpBufferSizesPropName() {
291        return "net.tcp.buffersize.wifi";
292    }
293
294    private static short countPrefixLength(byte [] mask) {
295        short count = 0;
296        for (byte b : mask) {
297            for (int i = 0; i < 8; ++i) {
298                if ((b & (1 << i)) != 0) {
299                    ++count;
300                }
301            }
302        }
303        return count;
304    }
305
306    void startReverseTether(final LinkProperties linkProperties) {
307        if (linkProperties == null || TextUtils.isEmpty(linkProperties.getInterfaceName())) {
308            Log.e(TAG, "attempted to reverse tether with empty interface");
309            return;
310        }
311        synchronized (mLinkPropertiesLock) {
312            if (mLinkProperties.getInterfaceName() != null) {
313                Log.e(TAG, "attempted to reverse tether while already in process");
314                return;
315            }
316            mLinkProperties = linkProperties;
317        }
318        Thread dhcpThread = new Thread(new Runnable() {
319            public void run() {
320                //Currently this thread runs independently.
321                DhcpResults dhcpResults = new DhcpResults();
322                boolean success = NetworkUtils.runDhcp(linkProperties.getInterfaceName(),
323                        dhcpResults);
324                synchronized (mLinkPropertiesLock) {
325                    if (linkProperties.getInterfaceName() != mLinkProperties.getInterfaceName()) {
326                        Log.e(TAG, "obsolete DHCP run aborted");
327                        return;
328                    }
329                    if (!success) {
330                        Log.e(TAG, "DHCP request error:" + NetworkUtils.getDhcpError());
331                        return;
332                    }
333                    mLinkProperties = dhcpResults.linkProperties;
334                    synchronized (mNetworkInfoLock) {
335                        mNetworkInfo.setIsAvailable(true);
336                        mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null);
337                        if (mCsHandler != null) {
338                            Message msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED,
339                                    new NetworkInfo(mNetworkInfo));
340                            msg.sendToTarget();
341                       }
342                    }
343                    return;
344                }
345            }
346        });
347        dhcpThread.start();
348    }
349
350    void stopReverseTether() {
351        synchronized (mLinkPropertiesLock) {
352            if (TextUtils.isEmpty(mLinkProperties.getInterfaceName())) {
353                Log.e(TAG, "attempted to stop reverse tether with nothing tethered");
354                return;
355            }
356            NetworkUtils.stopDhcp(mLinkProperties.getInterfaceName());
357            mLinkProperties.clear();
358            synchronized (mNetworkInfoLock) {
359                mNetworkInfo.setIsAvailable(false);
360                mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null);
361
362                if (mCsHandler != null) {
363                    mCsHandler.obtainMessage(EVENT_STATE_CHANGED, new NetworkInfo(mNetworkInfo)).
364                            sendToTarget();
365                }
366            }
367        }
368    }
369
370    public void setDependencyMet(boolean met) {
371        // not supported on this network
372    }
373
374    @Override
375    public void addStackedLink(LinkProperties link) {
376        mLinkProperties.addStackedLink(link);
377    }
378
379    @Override
380    public void removeStackedLink(LinkProperties link) {
381        mLinkProperties.removeStackedLink(link);
382    }
383
384    static class BtdtHandler extends Handler {
385        private AsyncChannel mStackChannel;
386        private final BluetoothTetheringDataTracker mBtdt;
387
388        BtdtHandler(Looper looper, BluetoothTetheringDataTracker parent) {
389            super(looper);
390            mBtdt = parent;
391        }
392
393        @Override
394        public void handleMessage(Message msg) {
395            switch (msg.what) {
396                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
397                    if (VDBG) Log.d(TAG, "got CMD_CHANNEL_HALF_CONNECTED");
398                    if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
399                        AsyncChannel ac = (AsyncChannel)msg.obj;
400                        if (mBtdt.mAsyncChannel.compareAndSet(null, ac) == false) {
401                            Log.e(TAG, "Trying to set mAsyncChannel twice!");
402                        } else {
403                            ac.sendMessage(
404                                    AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
405                        }
406                    }
407                    break;
408                case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
409                    if (VDBG) Log.d(TAG, "got CMD_CHANNEL_DISCONNECTED");
410                    mBtdt.stopReverseTether();
411                    mBtdt.mAsyncChannel.set(null);
412                    break;
413                case NetworkStateTracker.EVENT_NETWORK_CONNECTED:
414                    LinkProperties linkProperties = (LinkProperties)(msg.obj);
415                    if (VDBG) Log.d(TAG, "got EVENT_NETWORK_CONNECTED, " + linkProperties);
416                    mBtdt.startReverseTether(linkProperties);
417                    break;
418                case NetworkStateTracker.EVENT_NETWORK_DISCONNECTED:
419                    linkProperties = (LinkProperties)(msg.obj);
420                    if (VDBG) Log.d(TAG, "got EVENT_NETWORK_DISCONNECTED, " + linkProperties);
421                    mBtdt.stopReverseTether();
422                    break;
423            }
424        }
425    }
426
427    @Override
428    public void supplyMessenger(Messenger messenger) {
429        if (messenger != null) {
430            new AsyncChannel().connect(mContext, mBtdtHandler, messenger);
431        }
432    }
433}
434