Home | History | Annotate | Download | only in tethering
      1 /*
      2  * Copyright (C) 2017 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.server.connectivity.tethering;
     18 
     19 import static com.android.internal.util.BitUtils.uint16;
     20 
     21 import android.hardware.tetheroffload.control.V1_0.IOffloadControl;
     22 import android.hardware.tetheroffload.control.V1_0.ITetheringOffloadCallback;
     23 import android.hardware.tetheroffload.control.V1_0.NatTimeoutUpdate;
     24 import android.hardware.tetheroffload.control.V1_0.NetworkProtocol;
     25 import android.hardware.tetheroffload.control.V1_0.OffloadCallbackEvent;
     26 import android.os.Handler;
     27 import android.os.RemoteException;
     28 import android.net.util.SharedLog;
     29 import android.system.OsConstants;
     30 
     31 import java.util.ArrayList;
     32 
     33 
     34 /**
     35  * Capture tethering dependencies, for injection.
     36  *
     37  * @hide
     38  */
     39 public class OffloadHardwareInterface {
     40     private static final String TAG = OffloadHardwareInterface.class.getSimpleName();
     41     private static final String YIELDS = " -> ";
     42     // Change this value to control whether tether offload is enabled or
     43     // disabled by default in the absence of an explicit Settings value.
     44     // See accompanying unittest to distinguish 0 from non-0 values.
     45     private static final int DEFAULT_TETHER_OFFLOAD_DISABLED = 0;
     46     private static final String NO_INTERFACE_NAME = "";
     47     private static final String NO_IPV4_ADDRESS = "";
     48     private static final String NO_IPV4_GATEWAY = "";
     49 
     50     private static native boolean configOffload();
     51 
     52     private final Handler mHandler;
     53     private final SharedLog mLog;
     54     private IOffloadControl mOffloadControl;
     55     private TetheringOffloadCallback mTetheringOffloadCallback;
     56     private ControlCallback mControlCallback;
     57 
     58     public static class ControlCallback {
     59         public void onStarted() {}
     60         public void onStoppedError() {}
     61         public void onStoppedUnsupported() {}
     62         public void onSupportAvailable() {}
     63         public void onStoppedLimitReached() {}
     64 
     65         public void onNatTimeoutUpdate(int proto,
     66                                        String srcAddr, int srcPort,
     67                                        String dstAddr, int dstPort) {}
     68     }
     69 
     70     public static class ForwardedStats {
     71         public long rxBytes;
     72         public long txBytes;
     73 
     74         public ForwardedStats() {
     75             rxBytes = 0;
     76             txBytes = 0;
     77         }
     78 
     79         public void add(ForwardedStats other) {
     80             rxBytes += other.rxBytes;
     81             txBytes += other.txBytes;
     82         }
     83 
     84         public String toString() {
     85             return String.format("rx:%s tx:%s", rxBytes, txBytes);
     86         }
     87     }
     88 
     89     public OffloadHardwareInterface(Handler h, SharedLog log) {
     90         mHandler = h;
     91         mLog = log.forSubComponent(TAG);
     92     }
     93 
     94     public int getDefaultTetherOffloadDisabled() {
     95         return DEFAULT_TETHER_OFFLOAD_DISABLED;
     96     }
     97 
     98     public boolean initOffloadConfig() {
     99         return configOffload();
    100     }
    101 
    102     public boolean initOffloadControl(ControlCallback controlCb) {
    103         mControlCallback = controlCb;
    104 
    105         if (mOffloadControl == null) {
    106             try {
    107                 mOffloadControl = IOffloadControl.getService();
    108             } catch (RemoteException e) {
    109                 mLog.e("tethering offload control not supported: " + e);
    110                 return false;
    111             }
    112             if (mOffloadControl == null) {
    113                 mLog.e("tethering IOffloadControl.getService() returned null");
    114                 return false;
    115             }
    116         }
    117 
    118         final String logmsg = String.format("initOffloadControl(%s)",
    119                 (controlCb == null) ? "null"
    120                         : "0x" + Integer.toHexString(System.identityHashCode(controlCb)));
    121 
    122         mTetheringOffloadCallback = new TetheringOffloadCallback(mHandler, mControlCallback, mLog);
    123         final CbResults results = new CbResults();
    124         try {
    125             mOffloadControl.initOffload(
    126                     mTetheringOffloadCallback,
    127                     (boolean success, String errMsg) -> {
    128                         results.success = success;
    129                         results.errMsg = errMsg;
    130                     });
    131         } catch (RemoteException e) {
    132             record(logmsg, e);
    133             return false;
    134         }
    135 
    136         record(logmsg, results);
    137         return results.success;
    138     }
    139 
    140     public void stopOffloadControl() {
    141         if (mOffloadControl != null) {
    142             try {
    143                 mOffloadControl.stopOffload(
    144                         (boolean success, String errMsg) -> {
    145                             if (!success) mLog.e("stopOffload failed: " + errMsg);
    146                         });
    147             } catch (RemoteException e) {
    148                 mLog.e("failed to stopOffload: " + e);
    149             }
    150         }
    151         mOffloadControl = null;
    152         mTetheringOffloadCallback = null;
    153         mControlCallback = null;
    154         mLog.log("stopOffloadControl()");
    155     }
    156 
    157     public ForwardedStats getForwardedStats(String upstream) {
    158         final String logmsg = String.format("getForwardedStats(%s)",  upstream);
    159 
    160         final ForwardedStats stats = new ForwardedStats();
    161         try {
    162             mOffloadControl.getForwardedStats(
    163                     upstream,
    164                     (long rxBytes, long txBytes) -> {
    165                         stats.rxBytes = (rxBytes > 0) ? rxBytes : 0;
    166                         stats.txBytes = (txBytes > 0) ? txBytes : 0;
    167                     });
    168         } catch (RemoteException e) {
    169             record(logmsg, e);
    170             return stats;
    171         }
    172 
    173         mLog.log(logmsg + YIELDS + stats);
    174         return stats;
    175     }
    176 
    177     public boolean setLocalPrefixes(ArrayList<String> localPrefixes) {
    178         final String logmsg = String.format("setLocalPrefixes([%s])",
    179                 String.join(",", localPrefixes));
    180 
    181         final CbResults results = new CbResults();
    182         try {
    183             mOffloadControl.setLocalPrefixes(localPrefixes,
    184                     (boolean success, String errMsg) -> {
    185                         results.success = success;
    186                         results.errMsg = errMsg;
    187                     });
    188         } catch (RemoteException e) {
    189             record(logmsg, e);
    190             return false;
    191         }
    192 
    193         record(logmsg, results);
    194         return results.success;
    195     }
    196 
    197     public boolean setDataLimit(String iface, long limit) {
    198 
    199         final String logmsg = String.format("setDataLimit(%s, %d)", iface, limit);
    200 
    201         final CbResults results = new CbResults();
    202         try {
    203             mOffloadControl.setDataLimit(
    204                     iface, limit,
    205                     (boolean success, String errMsg) -> {
    206                         results.success = success;
    207                         results.errMsg = errMsg;
    208                     });
    209         } catch (RemoteException e) {
    210             record(logmsg, e);
    211             return false;
    212         }
    213 
    214         record(logmsg, results);
    215         return results.success;
    216     }
    217 
    218     public boolean setUpstreamParameters(
    219             String iface, String v4addr, String v4gateway, ArrayList<String> v6gws) {
    220         iface = (iface != null) ? iface : NO_INTERFACE_NAME;
    221         v4addr = (v4addr != null) ? v4addr : NO_IPV4_ADDRESS;
    222         v4gateway = (v4gateway != null) ? v4gateway : NO_IPV4_GATEWAY;
    223         v6gws = (v6gws != null) ? v6gws : new ArrayList<>();
    224 
    225         final String logmsg = String.format("setUpstreamParameters(%s, %s, %s, [%s])",
    226                 iface, v4addr, v4gateway, String.join(",", v6gws));
    227 
    228         final CbResults results = new CbResults();
    229         try {
    230             mOffloadControl.setUpstreamParameters(
    231                     iface, v4addr, v4gateway, v6gws,
    232                     (boolean success, String errMsg) -> {
    233                         results.success = success;
    234                         results.errMsg = errMsg;
    235                     });
    236         } catch (RemoteException e) {
    237             record(logmsg, e);
    238             return false;
    239         }
    240 
    241         record(logmsg, results);
    242         return results.success;
    243     }
    244 
    245     public boolean addDownstreamPrefix(String ifname, String prefix) {
    246         final String logmsg = String.format("addDownstreamPrefix(%s, %s)", ifname, prefix);
    247 
    248         final CbResults results = new CbResults();
    249         try {
    250             mOffloadControl.addDownstream(ifname, prefix,
    251                     (boolean success, String errMsg) -> {
    252                         results.success = success;
    253                         results.errMsg = errMsg;
    254                     });
    255         } catch (RemoteException e) {
    256             record(logmsg, e);
    257             return false;
    258         }
    259 
    260         record(logmsg, results);
    261         return results.success;
    262     }
    263 
    264     public boolean removeDownstreamPrefix(String ifname, String prefix) {
    265         final String logmsg = String.format("removeDownstreamPrefix(%s, %s)", ifname, prefix);
    266 
    267         final CbResults results = new CbResults();
    268         try {
    269             mOffloadControl.removeDownstream(ifname, prefix,
    270                     (boolean success, String errMsg) -> {
    271                         results.success = success;
    272                         results.errMsg = errMsg;
    273                     });
    274         } catch (RemoteException e) {
    275             record(logmsg, e);
    276             return false;
    277         }
    278 
    279         record(logmsg, results);
    280         return results.success;
    281     }
    282 
    283     private void record(String msg, Throwable t) {
    284         mLog.e(msg + YIELDS + "exception: " + t);
    285     }
    286 
    287     private void record(String msg, CbResults results) {
    288         final String logmsg = msg + YIELDS + results;
    289         if (!results.success) {
    290             mLog.e(logmsg);
    291         } else {
    292             mLog.log(logmsg);
    293         }
    294     }
    295 
    296     private static class TetheringOffloadCallback extends ITetheringOffloadCallback.Stub {
    297         public final Handler handler;
    298         public final ControlCallback controlCb;
    299         public final SharedLog log;
    300 
    301         public TetheringOffloadCallback(Handler h, ControlCallback cb, SharedLog sharedLog) {
    302             handler = h;
    303             controlCb = cb;
    304             log = sharedLog;
    305         }
    306 
    307         @Override
    308         public void onEvent(int event) {
    309             handler.post(() -> {
    310                 switch (event) {
    311                     case OffloadCallbackEvent.OFFLOAD_STARTED:
    312                         controlCb.onStarted();
    313                         break;
    314                     case OffloadCallbackEvent.OFFLOAD_STOPPED_ERROR:
    315                         controlCb.onStoppedError();
    316                         break;
    317                     case OffloadCallbackEvent.OFFLOAD_STOPPED_UNSUPPORTED:
    318                         controlCb.onStoppedUnsupported();
    319                         break;
    320                     case OffloadCallbackEvent.OFFLOAD_SUPPORT_AVAILABLE:
    321                         controlCb.onSupportAvailable();
    322                         break;
    323                     case OffloadCallbackEvent.OFFLOAD_STOPPED_LIMIT_REACHED:
    324                         controlCb.onStoppedLimitReached();
    325                         break;
    326                     default:
    327                         log.e("Unsupported OffloadCallbackEvent: " + event);
    328                 }
    329             });
    330         }
    331 
    332         @Override
    333         public void updateTimeout(NatTimeoutUpdate params) {
    334             handler.post(() -> {
    335                     controlCb.onNatTimeoutUpdate(
    336                         networkProtocolToOsConstant(params.proto),
    337                         params.src.addr, uint16(params.src.port),
    338                         params.dst.addr, uint16(params.dst.port));
    339             });
    340         }
    341     }
    342 
    343     private static int networkProtocolToOsConstant(int proto) {
    344         switch (proto) {
    345             case NetworkProtocol.TCP: return OsConstants.IPPROTO_TCP;
    346             case NetworkProtocol.UDP: return OsConstants.IPPROTO_UDP;
    347             default:
    348                 // The caller checks this value and will log an error. Just make
    349                 // sure it won't collide with valid OsContants.IPPROTO_* values.
    350                 return -Math.abs(proto);
    351         }
    352     }
    353 
    354     private static class CbResults {
    355         boolean success;
    356         String errMsg;
    357 
    358         @Override
    359         public String toString() {
    360             if (success) {
    361                 return "ok";
    362             } else {
    363                 return "fail: " + errMsg;
    364             }
    365         }
    366     }
    367 }
    368