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_AIDL; 20 21 import android.annotation.NonNull; 22 import android.annotation.TargetApi; 23 import android.hardware.tetheroffload.ForwardedStats; 24 import android.hardware.tetheroffload.IOffload; 25 import android.hardware.tetheroffload.ITetheringOffloadCallback; 26 import android.hardware.tetheroffload.NatTimeoutUpdate; 27 import android.hardware.tetheroffload.NetworkProtocol; 28 import android.hardware.tetheroffload.OffloadCallbackEvent; 29 import android.os.Build; 30 import android.os.Handler; 31 import android.os.NativeHandle; 32 import android.os.ParcelFileDescriptor; 33 import android.os.ServiceManager; 34 import android.system.OsConstants; 35 36 import com.android.internal.annotations.VisibleForTesting; 37 import com.android.modules.utils.build.SdkLevel; 38 import com.android.net.module.util.SharedLog; 39 import com.android.networkstack.tethering.OffloadHardwareInterface.OffloadHalCallback; 40 41 import java.util.ArrayList; 42 43 /** 44 * The implementation of IOffloadHal which based on Stable AIDL interface 45 */ 46 @TargetApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 47 public class OffloadHalAidlImpl implements IOffloadHal { 48 private static final String TAG = OffloadHalAidlImpl.class.getSimpleName(); 49 private static final String HAL_INSTANCE_NAME = IOffload.DESCRIPTOR + "/default"; 50 51 private final Handler mHandler; 52 private final SharedLog mLog; 53 private final IOffload mIOffload; 54 @OffloadHardwareInterface.OffloadHalVersion 55 private final int mOffloadVersion; 56 57 private TetheringOffloadCallback mTetheringOffloadCallback; 58 59 @VisibleForTesting OffloadHalAidlImpl(int version, @NonNull IOffload offload, @NonNull Handler handler, @NonNull SharedLog log)60 public OffloadHalAidlImpl(int version, @NonNull IOffload offload, @NonNull Handler handler, 61 @NonNull SharedLog log) { 62 mOffloadVersion = version; 63 mIOffload = offload; 64 mHandler = handler; 65 mLog = log.forSubComponent(TAG); 66 } 67 68 /** 69 * Initialize the Tetheroffload HAL. Provides bound netlink file descriptors for use in the 70 * management process. 71 */ initOffload(@onNull NativeHandle handle1, @NonNull NativeHandle handle2, @NonNull OffloadHalCallback callback)72 public boolean initOffload(@NonNull NativeHandle handle1, @NonNull NativeHandle handle2, 73 @NonNull OffloadHalCallback callback) { 74 final String methodStr = String.format("initOffload(%d, %d, %s)", 75 handle1.getFileDescriptor().getInt$(), handle2.getFileDescriptor().getInt$(), 76 (callback == null) ? "null" 77 : "0x" + Integer.toHexString(System.identityHashCode(callback))); 78 mTetheringOffloadCallback = new TetheringOffloadCallback(mHandler, callback, mLog); 79 try { 80 mIOffload.initOffload( 81 ParcelFileDescriptor.adoptFd(handle1.getFileDescriptor().getInt$()), 82 ParcelFileDescriptor.adoptFd(handle2.getFileDescriptor().getInt$()), 83 mTetheringOffloadCallback); 84 } catch (Exception e) { 85 logAndIgnoreException(e, methodStr); 86 return false; 87 } 88 mLog.i(methodStr); 89 return true; 90 } 91 92 /** Stop the Tetheroffload HAL. */ stopOffload()93 public boolean stopOffload() { 94 final String methodStr = "stopOffload()"; 95 try { 96 mIOffload.stopOffload(); 97 } catch (Exception e) { 98 logAndIgnoreException(e, methodStr); 99 return false; 100 } 101 102 mTetheringOffloadCallback = null; 103 mLog.i(methodStr); 104 return true; 105 } 106 107 /** Get HAL interface version number. */ getVersion()108 public int getVersion() { 109 return mOffloadVersion; 110 } 111 112 /** Get Tx/Rx usage from last query. */ getForwardedStats(@onNull String upstream)113 public OffloadHardwareInterface.ForwardedStats getForwardedStats(@NonNull String upstream) { 114 ForwardedStats stats = new ForwardedStats(); 115 final String methodStr = String.format("getForwardedStats(%s)", upstream); 116 try { 117 stats = mIOffload.getForwardedStats(upstream); 118 } catch (Exception e) { 119 logAndIgnoreException(e, methodStr); 120 } 121 mLog.i(methodStr); 122 return new OffloadHardwareInterface.ForwardedStats(stats.rxBytes, stats.txBytes); 123 } 124 125 /** Set local prefixes to offload management process. */ setLocalPrefixes(@onNull ArrayList<String> localPrefixes)126 public boolean setLocalPrefixes(@NonNull ArrayList<String> localPrefixes) { 127 final String methodStr = String.format("setLocalPrefixes([%s])", 128 String.join(",", localPrefixes)); 129 try { 130 mIOffload.setLocalPrefixes(localPrefixes.toArray(new String[localPrefixes.size()])); 131 } catch (Exception e) { 132 logAndIgnoreException(e, methodStr); 133 return false; 134 } 135 mLog.i(methodStr); 136 return true; 137 } 138 139 /** 140 * Set data limit value to offload management process. 141 * Method setDataLimit is deprecated in AIDL, so call setDataWarningAndLimit instead, 142 * with warningBytes set to its MAX_VALUE. 143 */ setDataLimit(@onNull String iface, long limit)144 public boolean setDataLimit(@NonNull String iface, long limit) { 145 final long warning = Long.MAX_VALUE; 146 final String methodStr = String.format("setDataLimit(%s, %d)", iface, limit); 147 try { 148 mIOffload.setDataWarningAndLimit(iface, warning, limit); 149 } catch (Exception e) { 150 logAndIgnoreException(e, methodStr); 151 return false; 152 } 153 mLog.i(methodStr); 154 return true; 155 } 156 157 /** Set data warning and limit value to offload management process. */ setDataWarningAndLimit(@onNull String iface, long warning, long limit)158 public boolean setDataWarningAndLimit(@NonNull String iface, long warning, long limit) { 159 final String methodStr = 160 String.format("setDataWarningAndLimit(%s, %d, %d)", iface, warning, limit); 161 try { 162 mIOffload.setDataWarningAndLimit(iface, warning, limit); 163 } catch (Exception e) { 164 logAndIgnoreException(e, methodStr); 165 return false; 166 } 167 mLog.i(methodStr); 168 return true; 169 } 170 171 /** Set upstream parameters to offload management process. */ setUpstreamParameters(@onNull String iface, @NonNull String v4addr, @NonNull String v4gateway, @NonNull ArrayList<String> v6gws)172 public boolean setUpstreamParameters(@NonNull String iface, @NonNull String v4addr, 173 @NonNull String v4gateway, @NonNull ArrayList<String> v6gws) { 174 final String methodStr = String.format("setUpstreamParameters(%s, %s, %s, [%s])", 175 iface, v4addr, v4gateway, String.join(",", v6gws)); 176 try { 177 mIOffload.setUpstreamParameters(iface, v4addr, v4gateway, 178 v6gws.toArray(new String[v6gws.size()])); 179 } catch (Exception e) { 180 logAndIgnoreException(e, methodStr); 181 return false; 182 } 183 mLog.i(methodStr); 184 return true; 185 } 186 187 /** Add downstream prefix to offload management process. */ addDownstream(@onNull String ifname, @NonNull String prefix)188 public boolean addDownstream(@NonNull String ifname, @NonNull String prefix) { 189 final String methodStr = String.format("addDownstream(%s, %s)", ifname, prefix); 190 try { 191 mIOffload.addDownstream(ifname, prefix); 192 } catch (Exception e) { 193 logAndIgnoreException(e, methodStr); 194 return false; 195 } 196 mLog.i(methodStr); 197 return true; 198 } 199 200 /** Remove downstream prefix from offload management process. */ removeDownstream(@onNull String ifname, @NonNull String prefix)201 public boolean removeDownstream(@NonNull String ifname, @NonNull String prefix) { 202 final String methodStr = String.format("removeDownstream(%s, %s)", ifname, prefix); 203 try { 204 mIOffload.removeDownstream(ifname, prefix); 205 } catch (Exception e) { 206 logAndIgnoreException(e, methodStr); 207 return false; 208 } 209 mLog.i(methodStr); 210 return true; 211 } 212 213 /** 214 * Get {@link IOffloadHal} object from the AIDL service. 215 * 216 * @param handler {@link Handler} to specify the thread upon which the callback will be invoked. 217 * @param log Log to be used by the repository. 218 */ getIOffloadHal(Handler handler, SharedLog log)219 public static IOffloadHal getIOffloadHal(Handler handler, SharedLog log) { 220 // Tetheroffload AIDL interface is only supported after U. 221 if (!SdkLevel.isAtLeastU() || !ServiceManager.isDeclared(HAL_INSTANCE_NAME)) return null; 222 223 IOffload offload = IOffload.Stub.asInterface( 224 ServiceManager.waitForDeclaredService(HAL_INSTANCE_NAME)); 225 if (offload == null) return null; 226 227 return new OffloadHalAidlImpl(OFFLOAD_HAL_VERSION_AIDL, offload, handler, log); 228 } 229 logAndIgnoreException(Exception e, final String methodStr)230 private void logAndIgnoreException(Exception e, final String methodStr) { 231 mLog.e(methodStr + " failed with " + e.getClass().getSimpleName() + ": ", e); 232 } 233 234 private static class TetheringOffloadCallback extends ITetheringOffloadCallback.Stub { 235 public final Handler handler; 236 public final OffloadHalCallback callback; 237 public final SharedLog log; 238 TetheringOffloadCallback( Handler h, OffloadHalCallback cb, SharedLog sharedLog)239 TetheringOffloadCallback( 240 Handler h, OffloadHalCallback cb, SharedLog sharedLog) { 241 handler = h; 242 callback = cb; 243 log = sharedLog; 244 } 245 handleOnEvent(int event)246 private void handleOnEvent(int event) { 247 switch (event) { 248 case OffloadCallbackEvent.OFFLOAD_STARTED: 249 callback.onStarted(); 250 break; 251 case OffloadCallbackEvent.OFFLOAD_STOPPED_ERROR: 252 callback.onStoppedError(); 253 break; 254 case OffloadCallbackEvent.OFFLOAD_STOPPED_UNSUPPORTED: 255 callback.onStoppedUnsupported(); 256 break; 257 case OffloadCallbackEvent.OFFLOAD_SUPPORT_AVAILABLE: 258 callback.onSupportAvailable(); 259 break; 260 case OffloadCallbackEvent.OFFLOAD_STOPPED_LIMIT_REACHED: 261 callback.onStoppedLimitReached(); 262 break; 263 case OffloadCallbackEvent.OFFLOAD_WARNING_REACHED: 264 callback.onWarningReached(); 265 break; 266 default: 267 log.e("Unsupported OffloadCallbackEvent: " + event); 268 } 269 } 270 271 @Override onEvent(int event)272 public void onEvent(int event) { 273 handler.post(() -> { 274 handleOnEvent(event); 275 }); 276 } 277 278 @Override updateTimeout(NatTimeoutUpdate params)279 public void updateTimeout(NatTimeoutUpdate params) { 280 handler.post(() -> { 281 callback.onNatTimeoutUpdate( 282 networkProtocolToOsConstant(params.proto), 283 params.src.addr, params.src.port, 284 params.dst.addr, params.dst.port); 285 }); 286 } 287 288 @Override getInterfaceHash()289 public String getInterfaceHash() { 290 return ITetheringOffloadCallback.HASH; 291 } 292 293 @Override getInterfaceVersion()294 public int getInterfaceVersion() { 295 return ITetheringOffloadCallback.VERSION; 296 } 297 } 298 networkProtocolToOsConstant(int proto)299 private static int networkProtocolToOsConstant(int proto) { 300 switch (proto) { 301 case NetworkProtocol.TCP: return OsConstants.IPPROTO_TCP; 302 case NetworkProtocol.UDP: return OsConstants.IPPROTO_UDP; 303 default: 304 // The caller checks this value and will log an error. Just make 305 // sure it won't collide with valid OsConstants.IPPROTO_* values. 306 return -Math.abs(proto); 307 } 308 } 309 } 310