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 configOffload()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 { onStarted()59 public void onStarted() {} onStoppedError()60 public void onStoppedError() {} onStoppedUnsupported()61 public void onStoppedUnsupported() {} onSupportAvailable()62 public void onSupportAvailable() {} onStoppedLimitReached()63 public void onStoppedLimitReached() {} 64 onNatTimeoutUpdate(int proto, String srcAddr, int srcPort, String dstAddr, int dstPort)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 ForwardedStats()74 public ForwardedStats() { 75 rxBytes = 0; 76 txBytes = 0; 77 } 78 add(ForwardedStats other)79 public void add(ForwardedStats other) { 80 rxBytes += other.rxBytes; 81 txBytes += other.txBytes; 82 } 83 toString()84 public String toString() { 85 return String.format("rx:%s tx:%s", rxBytes, txBytes); 86 } 87 } 88 OffloadHardwareInterface(Handler h, SharedLog log)89 public OffloadHardwareInterface(Handler h, SharedLog log) { 90 mHandler = h; 91 mLog = log.forSubComponent(TAG); 92 } 93 getDefaultTetherOffloadDisabled()94 public int getDefaultTetherOffloadDisabled() { 95 return DEFAULT_TETHER_OFFLOAD_DISABLED; 96 } 97 initOffloadConfig()98 public boolean initOffloadConfig() { 99 return configOffload(); 100 } 101 initOffloadControl(ControlCallback controlCb)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 stopOffloadControl()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 getForwardedStats(String upstream)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 setLocalPrefixes(ArrayList<String> localPrefixes)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 setDataLimit(String iface, long limit)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 setUpstreamParameters( String iface, String v4addr, String v4gateway, ArrayList<String> v6gws)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 addDownstreamPrefix(String ifname, String prefix)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 removeDownstreamPrefix(String ifname, String prefix)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 record(String msg, Throwable t)283 private void record(String msg, Throwable t) { 284 mLog.e(msg + YIELDS + "exception: " + t); 285 } 286 record(String msg, CbResults results)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 TetheringOffloadCallback(Handler h, ControlCallback cb, SharedLog sharedLog)301 public TetheringOffloadCallback(Handler h, ControlCallback cb, SharedLog sharedLog) { 302 handler = h; 303 controlCb = cb; 304 log = sharedLog; 305 } 306 307 @Override onEvent(int event)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 updateTimeout(NatTimeoutUpdate params)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 networkProtocolToOsConstant(int proto)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 toString()359 public String toString() { 360 if (success) { 361 return "ok"; 362 } else { 363 return "fail: " + errMsg; 364 } 365 } 366 } 367 } 368