Home | History | Annotate | Download | only in pan
      1 /*
      2  * Copyright (C) 2014 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 com.android.bluetooth.pan;
     18 
     19 import android.bluetooth.BluetoothDevice;
     20 import android.content.Context;
     21 import android.net.ConnectivityManager;
     22 import android.net.DhcpResults;
     23 import android.net.LinkProperties;
     24 import android.net.NetworkAgent;
     25 import android.net.NetworkCapabilities;
     26 import android.net.NetworkFactory;
     27 import android.net.NetworkInfo;
     28 import android.net.NetworkInfo.DetailedState;
     29 import android.net.NetworkRequest;
     30 import android.net.NetworkStateTracker;
     31 import android.net.NetworkUtils;
     32 import android.os.Looper;
     33 import android.os.Message;
     34 import android.os.Messenger;
     35 import android.os.RemoteException;
     36 import android.text.TextUtils;
     37 import android.util.Slog;
     38 
     39 import com.android.bluetooth.pan.PanService;
     40 import com.android.internal.util.AsyncChannel;
     41 
     42 /**
     43  * This class tracks the data connection associated with Bluetooth
     44  * reverse tethering. PanService calls it when a reverse tethered
     45  * connection needs to be activated or deactivated.
     46  *
     47  * @hide
     48  */
     49 public class BluetoothTetheringNetworkFactory extends NetworkFactory {
     50     private static final String NETWORK_TYPE = "Bluetooth Tethering";
     51     private static final String TAG = "BluetoothTetheringNetworkFactory";
     52     private static final int NETWORK_SCORE = 69;
     53 
     54     private final NetworkCapabilities mNetworkCapabilities;
     55     private final Context mContext;
     56     private final PanService mPanService;
     57 
     58     // All accesses to these must be synchronized(this).
     59     private final NetworkInfo mNetworkInfo;
     60     private LinkProperties mLinkProperties;
     61     private NetworkAgent mNetworkAgent;
     62 
     63     public BluetoothTetheringNetworkFactory(Context context, Looper looper, PanService panService) {
     64         super(looper, context, NETWORK_TYPE, new NetworkCapabilities());
     65 
     66         mContext = context;
     67         mPanService = panService;
     68 
     69         mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_BLUETOOTH, 0, NETWORK_TYPE, "");
     70         mLinkProperties = new LinkProperties();
     71         mNetworkCapabilities = new NetworkCapabilities();
     72         initNetworkCapabilities();
     73         setCapabilityFilter(mNetworkCapabilities);
     74     }
     75 
     76     // Called by NetworkFactory when PanService and NetworkFactory both desire a Bluetooth
     77     // reverse-tether connection.  A network interface for Bluetooth reverse-tethering can be
     78     // assumed to be available because we only register our NetworkFactory when it is so.
     79     @Override
     80     protected void startNetwork() {
     81         // TODO: Handle DHCP renew.
     82         Thread dhcpThread = new Thread(new Runnable() {
     83             public void run() {
     84                 LinkProperties linkProperties;
     85                 synchronized (BluetoothTetheringNetworkFactory.this) {
     86                     linkProperties = mLinkProperties;
     87                     if (linkProperties.getInterfaceName() == null) {
     88                         Slog.e(TAG, "attempted to reverse tether without interface name");
     89                         return;
     90                     }
     91                     log("dhcpThread(+" + linkProperties.getInterfaceName() +
     92                             "): mNetworkInfo=" + mNetworkInfo);
     93                 }
     94 
     95                 DhcpResults dhcpResults = new DhcpResults();
     96                 // TODO: Handle DHCP renewals better.
     97                 // In general runDhcp handles DHCP renewals for us, because
     98                 // the dhcp client stays running, but if the renewal fails,
     99                 // we will lose our IP address and connectivity without
    100                 // noticing.
    101                 if (!NetworkUtils.runDhcp(linkProperties.getInterfaceName(), dhcpResults)) {
    102                     Slog.e(TAG, "DHCP request error:" + NetworkUtils.getDhcpError());
    103                     synchronized(BluetoothTetheringNetworkFactory.this) {
    104                         setScoreFilter(-1);
    105                     }
    106                     return;
    107                 }
    108 
    109                 synchronized(BluetoothTetheringNetworkFactory.this) {
    110                     mLinkProperties = dhcpResults.toLinkProperties(
    111                             linkProperties.getInterfaceName());
    112                     mNetworkInfo.setIsAvailable(true);
    113                     mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null);
    114 
    115                     // Create our NetworkAgent.
    116                     mNetworkAgent = new NetworkAgent(getLooper(), mContext, NETWORK_TYPE,
    117                             mNetworkInfo, mNetworkCapabilities, mLinkProperties, NETWORK_SCORE) {
    118                         public void unwanted() {
    119                             BluetoothTetheringNetworkFactory.this.onCancelRequest();
    120                         };
    121                     };
    122                 }
    123             }
    124         });
    125         dhcpThread.start();
    126     }
    127 
    128     // Called from NetworkFactory to indicate ConnectivityService no longer desires a Bluetooth
    129     // reverse-tether network.
    130     @Override
    131     protected void stopNetwork() {
    132         // Let NetworkAgent disconnect do the teardown.
    133     }
    134 
    135     // Called by the NetworkFactory, NetworkAgent or PanService to tear down network.
    136     private synchronized void onCancelRequest() {
    137         if (!TextUtils.isEmpty(mLinkProperties.getInterfaceName())) {
    138             NetworkUtils.stopDhcp(mLinkProperties.getInterfaceName());
    139         }
    140         mLinkProperties.clear();
    141         mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null);
    142         if (mNetworkAgent != null) {
    143             mNetworkAgent.sendNetworkInfo(mNetworkInfo);
    144             mNetworkAgent = null;
    145         }
    146         for (BluetoothDevice device : mPanService.getConnectedDevices()) {
    147              mPanService.disconnect(device);
    148         }
    149     }
    150 
    151     // Called by PanService when a network interface for Bluetooth reverse-tethering
    152     // becomes available.  We register our NetworkFactory at this point.
    153     public void startReverseTether(final String iface) {
    154         if (iface == null || TextUtils.isEmpty(iface)) {
    155             Slog.e(TAG, "attempted to reverse tether with empty interface");
    156             return;
    157         }
    158         synchronized(this) {
    159             if (mLinkProperties.getInterfaceName() != null) {
    160                 Slog.e(TAG, "attempted to reverse tether while already in process");
    161                 return;
    162             }
    163             mLinkProperties = new LinkProperties();
    164             mLinkProperties.setInterfaceName(iface);
    165             // Advertise ourselves to ConnectivityService.
    166             register();
    167             setScoreFilter(NETWORK_SCORE);
    168         }
    169     }
    170 
    171     // Called by PanService when a network interface for Bluetooth reverse-tethering
    172     // goes away.  We stop advertising ourselves to ConnectivityService at this point.
    173     public synchronized void stopReverseTether() {
    174         if (TextUtils.isEmpty(mLinkProperties.getInterfaceName())) {
    175             Slog.e(TAG, "attempted to stop reverse tether with nothing tethered");
    176             return;
    177         }
    178         onCancelRequest();
    179         setScoreFilter(-1);
    180         unregister();
    181     }
    182 
    183     private void initNetworkCapabilities() {
    184         mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_BLUETOOTH);
    185         mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
    186         mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
    187         // Bluetooth v3 and v4 go up to 24 Mbps.
    188         // TODO: Adjust this to actual connection bandwidth.
    189         mNetworkCapabilities.setLinkUpstreamBandwidthKbps(24 * 1000);
    190         mNetworkCapabilities.setLinkDownstreamBandwidthKbps(24 * 1000);
    191     }
    192 }
    193