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