1 /* 2 * Copyright (C) 2022 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.networkstack.tethering; 18 19 import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_HIDL_1_0; 20 import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_HIDL_1_1; 21 import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_NONE; 22 import static com.android.networkstack.tethering.OffloadHardwareInterface.halVerToString; 23 import static com.android.networkstack.tethering.util.TetheringUtils.uint16; 24 25 import android.annotation.NonNull; 26 import android.hardware.tetheroffload.config.V1_0.IOffloadConfig; 27 import android.hardware.tetheroffload.control.V1_0.IOffloadControl; 28 import android.hardware.tetheroffload.control.V1_0.NatTimeoutUpdate; 29 import android.hardware.tetheroffload.control.V1_0.NetworkProtocol; 30 import android.hardware.tetheroffload.control.V1_0.OffloadCallbackEvent; 31 import android.hardware.tetheroffload.control.V1_1.ITetheringOffloadCallback; 32 import android.os.Handler; 33 import android.os.NativeHandle; 34 import android.os.RemoteException; 35 import android.system.OsConstants; 36 import android.util.Log; 37 38 import com.android.net.module.util.SharedLog; 39 import com.android.networkstack.tethering.OffloadHardwareInterface.ForwardedStats; 40 import com.android.networkstack.tethering.OffloadHardwareInterface.OffloadHalCallback; 41 42 import java.util.ArrayList; 43 import java.util.NoSuchElementException; 44 45 /** 46 * The implementation of IOffloadHal which based on HIDL interfaces 47 */ 48 public class OffloadHalHidlImpl implements IOffloadHal { 49 private static final String TAG = OffloadHalHidlImpl.class.getSimpleName(); 50 private static final String YIELDS = " -> "; 51 52 private final Handler mHandler; 53 private final SharedLog mLog; 54 private final IOffloadConfig mIOffloadConfig; 55 private final IOffloadControl mIOffloadControl; 56 @OffloadHardwareInterface.OffloadHalVersion 57 private final int mOffloadControlVersion; 58 59 private OffloadHalCallback mOffloadHalCallback; 60 private TetheringOffloadCallback mTetheringOffloadCallback; 61 OffloadHalHidlImpl(int version, @NonNull IOffloadConfig config, @NonNull IOffloadControl control, @NonNull Handler handler, @NonNull SharedLog log)62 public OffloadHalHidlImpl(int version, @NonNull IOffloadConfig config, 63 @NonNull IOffloadControl control, @NonNull Handler handler, @NonNull SharedLog log) { 64 mOffloadControlVersion = version; 65 mIOffloadConfig = config; 66 mIOffloadControl = control; 67 mHandler = handler; 68 mLog = log.forSubComponent(TAG); 69 } 70 71 /** 72 * Initialize the Tetheroffload HAL. Provides bound netlink file descriptors for use in the 73 * management process. 74 */ initOffload(@onNull NativeHandle handle1, @NonNull NativeHandle handle2, @NonNull OffloadHalCallback callback)75 public boolean initOffload(@NonNull NativeHandle handle1, @NonNull NativeHandle handle2, 76 @NonNull OffloadHalCallback callback) { 77 final String logmsg = "initOffload()"; 78 79 mOffloadHalCallback = callback; 80 mTetheringOffloadCallback = new TetheringOffloadCallback( 81 mHandler, mOffloadHalCallback, mLog, mOffloadControlVersion); 82 final CbResults results = new CbResults(); 83 try { 84 mIOffloadConfig.setHandles(handle1, handle2, 85 (boolean success, String errMsg) -> { 86 results.mSuccess = success; 87 results.mErrMsg = errMsg; 88 }); 89 mIOffloadControl.initOffload( 90 mTetheringOffloadCallback, 91 (boolean success, String errMsg) -> { 92 results.mSuccess = success; 93 results.mErrMsg = errMsg; 94 }); 95 } catch (RemoteException e) { 96 record(logmsg, e); 97 return false; 98 } 99 100 record(logmsg, results); 101 return results.mSuccess; 102 } 103 104 /** Stop the Tetheroffload HAL. */ stopOffload()105 public boolean stopOffload() { 106 try { 107 mIOffloadControl.stopOffload( 108 (boolean success, String errMsg) -> { 109 if (!success) mLog.e("stopOffload failed: " + errMsg); 110 }); 111 } catch (RemoteException e) { 112 mLog.e("failed to stopOffload: " + e); 113 } 114 mOffloadHalCallback = null; 115 mTetheringOffloadCallback = null; 116 mLog.log("stopOffload()"); 117 return true; 118 } 119 120 /** Get HAL interface version number. */ getVersion()121 public int getVersion() { 122 return mOffloadControlVersion; 123 } 124 125 /** Get Tx/Rx usage from last query. */ getForwardedStats(@onNull String upstream)126 public ForwardedStats getForwardedStats(@NonNull String upstream) { 127 final String logmsg = String.format("getForwardedStats(%s)", upstream); 128 129 final ForwardedStats stats = new ForwardedStats(); 130 try { 131 mIOffloadControl.getForwardedStats( 132 upstream, 133 (long rxBytes, long txBytes) -> { 134 stats.rxBytes = (rxBytes > 0) ? rxBytes : 0; 135 stats.txBytes = (txBytes > 0) ? txBytes : 0; 136 }); 137 } catch (RemoteException e) { 138 record(logmsg, e); 139 return stats; 140 } 141 142 return stats; 143 } 144 145 /** Set local prefixes to offload management process. */ setLocalPrefixes(@onNull ArrayList<String> localPrefixes)146 public boolean setLocalPrefixes(@NonNull ArrayList<String> localPrefixes) { 147 final String logmsg = String.format("setLocalPrefixes([%s])", 148 String.join(",", localPrefixes)); 149 150 final CbResults results = new CbResults(); 151 try { 152 mIOffloadControl.setLocalPrefixes(localPrefixes, 153 (boolean success, String errMsg) -> { 154 results.mSuccess = success; 155 results.mErrMsg = errMsg; 156 }); 157 } catch (RemoteException e) { 158 record(logmsg, e); 159 return false; 160 } 161 162 record(logmsg, results); 163 return results.mSuccess; 164 } 165 166 /** Set data limit value to offload management process. */ setDataLimit(@onNull String iface, long limit)167 public boolean setDataLimit(@NonNull String iface, long limit) { 168 169 final String logmsg = String.format("setDataLimit(%s, %d)", iface, limit); 170 171 final CbResults results = new CbResults(); 172 try { 173 mIOffloadControl.setDataLimit( 174 iface, limit, 175 (boolean success, String errMsg) -> { 176 results.mSuccess = success; 177 results.mErrMsg = errMsg; 178 }); 179 } catch (RemoteException e) { 180 record(logmsg, e); 181 return false; 182 } 183 184 record(logmsg, results); 185 return results.mSuccess; 186 } 187 188 /** Set data warning and limit value to offload management process. */ setDataWarningAndLimit(@onNull String iface, long warning, long limit)189 public boolean setDataWarningAndLimit(@NonNull String iface, long warning, long limit) { 190 if (mOffloadControlVersion < OFFLOAD_HAL_VERSION_HIDL_1_1) { 191 throw new UnsupportedOperationException( 192 "setDataWarningAndLimit is not supported below HAL V1.1"); 193 } 194 final String logmsg = 195 String.format("setDataWarningAndLimit(%s, %d, %d)", iface, warning, limit); 196 197 final CbResults results = new CbResults(); 198 try { 199 ((android.hardware.tetheroffload.control.V1_1.IOffloadControl) mIOffloadControl) 200 .setDataWarningAndLimit( 201 iface, warning, limit, 202 (boolean success, String errMsg) -> { 203 results.mSuccess = success; 204 results.mErrMsg = errMsg; 205 }); 206 } catch (RemoteException e) { 207 record(logmsg, e); 208 return false; 209 } 210 211 record(logmsg, results); 212 return results.mSuccess; 213 } 214 215 /** Set upstream parameters to offload management process. */ setUpstreamParameters(@onNull String iface, @NonNull String v4addr, @NonNull String v4gateway, @NonNull ArrayList<String> v6gws)216 public boolean setUpstreamParameters(@NonNull String iface, @NonNull String v4addr, 217 @NonNull String v4gateway, @NonNull ArrayList<String> v6gws) { 218 final String logmsg = String.format("setUpstreamParameters(%s, %s, %s, [%s])", 219 iface, v4addr, v4gateway, String.join(",", v6gws)); 220 221 final CbResults results = new CbResults(); 222 try { 223 mIOffloadControl.setUpstreamParameters( 224 iface, v4addr, v4gateway, v6gws, 225 (boolean success, String errMsg) -> { 226 results.mSuccess = success; 227 results.mErrMsg = errMsg; 228 }); 229 } catch (RemoteException e) { 230 record(logmsg, e); 231 return false; 232 } 233 234 record(logmsg, results); 235 return results.mSuccess; 236 } 237 238 /** Add downstream prefix to offload management process. */ addDownstream(@onNull String ifname, @NonNull String prefix)239 public boolean addDownstream(@NonNull String ifname, @NonNull String prefix) { 240 final String logmsg = String.format("addDownstream(%s, %s)", ifname, prefix); 241 242 final CbResults results = new CbResults(); 243 try { 244 mIOffloadControl.addDownstream(ifname, prefix, 245 (boolean success, String errMsg) -> { 246 results.mSuccess = success; 247 results.mErrMsg = errMsg; 248 }); 249 } catch (RemoteException e) { 250 record(logmsg, e); 251 return false; 252 } 253 254 record(logmsg, results); 255 return results.mSuccess; 256 } 257 258 /** Remove downstream prefix from offload management process. */ removeDownstream(@onNull String ifname, @NonNull String prefix)259 public boolean removeDownstream(@NonNull String ifname, @NonNull String prefix) { 260 final String logmsg = String.format("removeDownstream(%s, %s)", ifname, prefix); 261 262 final CbResults results = new CbResults(); 263 try { 264 mIOffloadControl.removeDownstream(ifname, prefix, 265 (boolean success, String errMsg) -> { 266 results.mSuccess = success; 267 results.mErrMsg = errMsg; 268 }); 269 } catch (RemoteException e) { 270 record(logmsg, e); 271 return false; 272 } 273 274 record(logmsg, results); 275 return results.mSuccess; 276 } 277 278 /** 279 * Get {@link IOffloadHal} object from the HIDL service. 280 * 281 * @param handler {@link Handler} to specify the thread upon which the callback will be invoked. 282 * @param log Log to be used by the repository. 283 */ getIOffloadHal(Handler handler, SharedLog log)284 public static IOffloadHal getIOffloadHal(Handler handler, SharedLog log) { 285 IOffloadConfig config = null; 286 try { 287 config = IOffloadConfig.getService(true /*retry*/); 288 } catch (RemoteException e) { 289 log.e("getIOffloadConfig error " + e); 290 return null; 291 } catch (NoSuchElementException e) { 292 log.i("getIOffloadConfig Tether Offload HAL not present/implemented"); 293 return null; 294 } 295 296 IOffloadControl control = null; 297 int version = OFFLOAD_HAL_VERSION_NONE; 298 try { 299 control = android.hardware.tetheroffload.control 300 .V1_1.IOffloadControl.getService(true /*retry*/); 301 version = OFFLOAD_HAL_VERSION_HIDL_1_1; 302 } catch (NoSuchElementException e) { 303 // Unsupported by device. 304 } catch (RemoteException e) { 305 log.e("Unable to get offload control " + OFFLOAD_HAL_VERSION_HIDL_1_1); 306 } 307 if (control == null) { 308 try { 309 control = IOffloadControl.getService(true /*retry*/); 310 version = OFFLOAD_HAL_VERSION_HIDL_1_0; 311 } catch (NoSuchElementException e) { 312 // Unsupported by device. 313 } catch (RemoteException e) { 314 log.e("Unable to get offload control " + OFFLOAD_HAL_VERSION_HIDL_1_0); 315 } 316 } 317 318 if (config == null || control == null) return null; 319 320 return new OffloadHalHidlImpl(version, config, control, handler, log); 321 } 322 record(String msg, Throwable t)323 private void record(String msg, Throwable t) { 324 mLog.e(msg + YIELDS + "exception: " + t); 325 } 326 record(String msg, CbResults results)327 private void record(String msg, CbResults results) { 328 final String logmsg = msg + YIELDS + results; 329 if (!results.mSuccess) { 330 mLog.e(logmsg); 331 } else { 332 mLog.log(logmsg); 333 } 334 } 335 336 private static class TetheringOffloadCallback extends ITetheringOffloadCallback.Stub { 337 public final Handler handler; 338 public final OffloadHalCallback callback; 339 public final SharedLog log; 340 private final int mOffloadControlVersion; 341 TetheringOffloadCallback( Handler h, OffloadHalCallback cb, SharedLog sharedLog, int offloadControlVersion)342 TetheringOffloadCallback( 343 Handler h, OffloadHalCallback cb, SharedLog sharedLog, int offloadControlVersion) { 344 handler = h; 345 callback = cb; 346 log = sharedLog; 347 this.mOffloadControlVersion = offloadControlVersion; 348 } 349 handleOnEvent(int event)350 private void handleOnEvent(int event) { 351 switch (event) { 352 case OffloadCallbackEvent.OFFLOAD_STARTED: 353 callback.onStarted(); 354 break; 355 case OffloadCallbackEvent.OFFLOAD_STOPPED_ERROR: 356 callback.onStoppedError(); 357 break; 358 case OffloadCallbackEvent.OFFLOAD_STOPPED_UNSUPPORTED: 359 callback.onStoppedUnsupported(); 360 break; 361 case OffloadCallbackEvent.OFFLOAD_SUPPORT_AVAILABLE: 362 callback.onSupportAvailable(); 363 break; 364 case OffloadCallbackEvent.OFFLOAD_STOPPED_LIMIT_REACHED: 365 callback.onStoppedLimitReached(); 366 break; 367 case android.hardware.tetheroffload.control 368 .V1_1.OffloadCallbackEvent.OFFLOAD_WARNING_REACHED: 369 callback.onWarningReached(); 370 break; 371 default: 372 log.e("Unsupported OffloadCallbackEvent: " + event); 373 } 374 } 375 376 @Override onEvent(int event)377 public void onEvent(int event) { 378 // The implementation should never call onEvent()) if the event is already reported 379 // through newer callback. 380 if (mOffloadControlVersion > OFFLOAD_HAL_VERSION_HIDL_1_0) { 381 Log.wtf(TAG, "onEvent(" + event + ") fired on HAL " 382 + halVerToString(mOffloadControlVersion)); 383 } 384 handler.post(() -> { 385 handleOnEvent(event); 386 }); 387 } 388 389 @Override onEvent_1_1(int event)390 public void onEvent_1_1(int event) { 391 if (mOffloadControlVersion < OFFLOAD_HAL_VERSION_HIDL_1_1) { 392 Log.wtf(TAG, "onEvent_1_1(" + event + ") fired on HAL " 393 + halVerToString(mOffloadControlVersion)); 394 return; 395 } 396 handler.post(() -> { 397 handleOnEvent(event); 398 }); 399 } 400 401 @Override updateTimeout(NatTimeoutUpdate params)402 public void updateTimeout(NatTimeoutUpdate params) { 403 handler.post(() -> { 404 callback.onNatTimeoutUpdate( 405 networkProtocolToOsConstant(params.proto), 406 params.src.addr, uint16(params.src.port), 407 params.dst.addr, uint16(params.dst.port)); 408 }); 409 } 410 } 411 networkProtocolToOsConstant(int proto)412 private static int networkProtocolToOsConstant(int proto) { 413 switch (proto) { 414 case NetworkProtocol.TCP: return OsConstants.IPPROTO_TCP; 415 case NetworkProtocol.UDP: return OsConstants.IPPROTO_UDP; 416 default: 417 // The caller checks this value and will log an error. Just make 418 // sure it won't collide with valid OsConstants.IPPROTO_* values. 419 return -Math.abs(proto); 420 } 421 } 422 423 private static class CbResults { 424 boolean mSuccess; 425 String mErrMsg; 426 427 @Override toString()428 public String toString() { 429 if (mSuccess) { 430 return "ok"; 431 } else { 432 return "fail: " + mErrMsg; 433 } 434 } 435 } 436 } 437