1 /* 2 * Copyright (C) 2019 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 package com.android.networkstack.tethering.util; 17 18 import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH; 19 import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET; 20 import static android.net.NetworkCapabilities.TRANSPORT_WIFI; 21 import static android.net.NetworkCapabilities.TRANSPORT_USB; 22 import static android.net.TetheringManager.CONNECTIVITY_SCOPE_GLOBAL; 23 import static android.net.TetheringManager.CONNECTIVITY_SCOPE_LOCAL; 24 import static android.net.TetheringManager.TETHERING_BLUETOOTH; 25 import static android.net.TetheringManager.TETHERING_ETHERNET; 26 import static android.net.TetheringManager.TETHERING_NCM; 27 import static android.net.TetheringManager.TETHERING_USB; 28 import static android.net.TetheringManager.TETHERING_VIRTUAL; 29 import static android.net.TetheringManager.TETHERING_WIFI; 30 import static android.net.TetheringManager.TETHERING_WIFI_P2P; 31 import static android.net.TetheringManager.TETHERING_WIGIG; 32 33 import android.net.TetherStatsParcel; 34 import android.net.TetheringManager.TetheringRequest; 35 import android.util.Log; 36 37 import androidx.annotation.NonNull; 38 39 import com.android.net.module.util.JniUtil; 40 import com.android.net.module.util.bpf.TetherStatsValue; 41 42 import java.io.FileDescriptor; 43 import java.net.Inet6Address; 44 import java.net.SocketException; 45 import java.net.UnknownHostException; 46 import java.util.Arrays; 47 48 /** 49 * The classes and the methods for tethering utilization. 50 * 51 * {@hide} 52 */ 53 public class TetheringUtils { 54 static { getTetheringJniLibraryName()55 System.loadLibrary(getTetheringJniLibraryName()); 56 } 57 58 public static final byte[] ALL_NODES = new byte[] { 59 (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 60 }; 61 62 /** The name should be com_android_networkstack_tethering_util_jni. */ getTetheringJniLibraryName()63 public static String getTetheringJniLibraryName() { 64 return JniUtil.getJniLibraryName(TetheringUtils.class.getPackage()); 65 } 66 67 /** 68 * Configures a socket for receiving and sending ICMPv6 neighbor advertisments. 69 * @param fd the socket's {@link FileDescriptor}. 70 */ setupNaSocket(FileDescriptor fd)71 public static native void setupNaSocket(FileDescriptor fd) 72 throws SocketException; 73 74 /** 75 * Configures a socket for receiving and sending ICMPv6 neighbor solicitations. 76 * @param fd the socket's {@link FileDescriptor}. 77 */ setupNsSocket(FileDescriptor fd)78 public static native void setupNsSocket(FileDescriptor fd) 79 throws SocketException; 80 81 /** 82 * The object which records offload Tx/Rx forwarded bytes/packets. 83 * TODO: Replace the inner class ForwardedStats of class OffloadHardwareInterface with 84 * this class as well. 85 */ 86 public static class ForwardedStats { 87 public final long rxBytes; 88 public final long rxPackets; 89 public final long txBytes; 90 public final long txPackets; 91 ForwardedStats()92 public ForwardedStats() { 93 rxBytes = 0; 94 rxPackets = 0; 95 txBytes = 0; 96 txPackets = 0; 97 } 98 ForwardedStats(long rxBytes, long txBytes)99 public ForwardedStats(long rxBytes, long txBytes) { 100 this.rxBytes = rxBytes; 101 this.rxPackets = 0; 102 this.txBytes = txBytes; 103 this.txPackets = 0; 104 } 105 ForwardedStats(long rxBytes, long rxPackets, long txBytes, long txPackets)106 public ForwardedStats(long rxBytes, long rxPackets, long txBytes, long txPackets) { 107 this.rxBytes = rxBytes; 108 this.rxPackets = rxPackets; 109 this.txBytes = txBytes; 110 this.txPackets = txPackets; 111 } 112 ForwardedStats(@onNull TetherStatsParcel tetherStats)113 public ForwardedStats(@NonNull TetherStatsParcel tetherStats) { 114 rxBytes = tetherStats.rxBytes; 115 rxPackets = tetherStats.rxPackets; 116 txBytes = tetherStats.txBytes; 117 txPackets = tetherStats.txPackets; 118 } 119 ForwardedStats(@onNull TetherStatsValue tetherStats)120 public ForwardedStats(@NonNull TetherStatsValue tetherStats) { 121 rxBytes = tetherStats.rxBytes; 122 rxPackets = tetherStats.rxPackets; 123 txBytes = tetherStats.txBytes; 124 txPackets = tetherStats.txPackets; 125 } 126 ForwardedStats(@onNull ForwardedStats other)127 public ForwardedStats(@NonNull ForwardedStats other) { 128 rxBytes = other.rxBytes; 129 rxPackets = other.rxPackets; 130 txBytes = other.txBytes; 131 txPackets = other.txPackets; 132 } 133 134 /** Add Tx/Rx bytes/packets and return the result as a new object. */ 135 @NonNull add(@onNull ForwardedStats other)136 public ForwardedStats add(@NonNull ForwardedStats other) { 137 return new ForwardedStats(rxBytes + other.rxBytes, rxPackets + other.rxPackets, 138 txBytes + other.txBytes, txPackets + other.txPackets); 139 } 140 141 /** Subtract Tx/Rx bytes/packets and return the result as a new object. */ 142 @NonNull subtract(@onNull ForwardedStats other)143 public ForwardedStats subtract(@NonNull ForwardedStats other) { 144 // TODO: Perhaps throw an exception if any negative difference value just in case. 145 final long rxBytesDiff = Math.max(rxBytes - other.rxBytes, 0); 146 final long rxPacketsDiff = Math.max(rxPackets - other.rxPackets, 0); 147 final long txBytesDiff = Math.max(txBytes - other.txBytes, 0); 148 final long txPacketsDiff = Math.max(txPackets - other.txPackets, 0); 149 return new ForwardedStats(rxBytesDiff, rxPacketsDiff, txBytesDiff, txPacketsDiff); 150 } 151 152 /** Returns the string representation of this object. */ 153 @NonNull toString()154 public String toString() { 155 return String.format("ForwardedStats(rxb: %d, rxp: %d, txb: %d, txp: %d)", rxBytes, 156 rxPackets, txBytes, txPackets); 157 } 158 } 159 160 /** 161 * Configures a socket for receiving ICMPv6 router solicitations and sending advertisements. 162 * @param fd the socket's {@link FileDescriptor}. 163 * @param ifIndex the interface index. 164 */ setupRaSocket(FileDescriptor fd, int ifIndex)165 public static native void setupRaSocket(FileDescriptor fd, int ifIndex) 166 throws SocketException; 167 168 /** 169 * Read s as an unsigned 16-bit integer. 170 */ uint16(short s)171 public static int uint16(short s) { 172 return s & 0xffff; 173 } 174 175 /** Get inet6 address for all nodes given scope ID. */ getAllNodesForScopeId(int scopeId)176 public static Inet6Address getAllNodesForScopeId(int scopeId) { 177 try { 178 return Inet6Address.getByAddress("ff02::1", ALL_NODES, scopeId); 179 } catch (UnknownHostException uhe) { 180 Log.wtf("TetheringUtils", "Failed to construct Inet6Address from " 181 + Arrays.toString(ALL_NODES) + " and scopedId " + scopeId); 182 return null; 183 } 184 } 185 186 /** 187 * Create a legacy tethering request for calls to the legacy tether() API, which doesn't take an 188 * explicit request. These are always CONNECTIVITY_SCOPE_GLOBAL, per historical behavior. 189 */ 190 @NonNull createLegacyGlobalScopeTetheringRequest(int type)191 public static TetheringRequest createLegacyGlobalScopeTetheringRequest(int type) { 192 final TetheringRequest request = new TetheringRequest.Builder(type).build(); 193 request.getParcel().requestType = TetheringRequest.REQUEST_TYPE_LEGACY; 194 request.getParcel().connectivityScope = CONNECTIVITY_SCOPE_GLOBAL; 195 return request; 196 } 197 198 /** 199 * Create a local-only implicit tethering request. This is used for Wifi local-only hotspot and 200 * Wifi P2P, which start tethering based on the WIFI_(AP/P2P)_STATE_CHANGED broadcasts. 201 */ 202 @NonNull createImplicitLocalOnlyTetheringRequest(int type)203 public static TetheringRequest createImplicitLocalOnlyTetheringRequest(int type) { 204 final TetheringRequest request = new TetheringRequest.Builder(type).build(); 205 request.getParcel().requestType = TetheringRequest.REQUEST_TYPE_IMPLICIT; 206 request.getParcel().connectivityScope = CONNECTIVITY_SCOPE_LOCAL; 207 return request; 208 } 209 210 /** 211 * Create a placeholder request. This is used in case we try to find a pending request but there 212 * is none (e.g. stopTethering removed a pending request), or for cases where we only have the 213 * tethering type (e.g. stopTethering(int)). 214 */ 215 @NonNull createPlaceholderRequest(int type)216 public static TetheringRequest createPlaceholderRequest(int type) { 217 final TetheringRequest request = new TetheringRequest.Builder(type).build(); 218 request.getParcel().requestType = TetheringRequest.REQUEST_TYPE_PLACEHOLDER; 219 request.getParcel().connectivityScope = CONNECTIVITY_SCOPE_GLOBAL; 220 return request; 221 } 222 223 /** 224 * Returns the transport type for the given interface type. 225 * 226 * @param interfaceType The interface type. 227 * @return The transport type. 228 * @throws IllegalArgumentException if the interface type is invalid. 229 */ getTransportTypeForTetherableType(int interfaceType)230 public static int getTransportTypeForTetherableType(int interfaceType) { 231 switch (interfaceType) { 232 case TETHERING_WIFI: 233 case TETHERING_WIGIG: 234 case TETHERING_WIFI_P2P: 235 return TRANSPORT_WIFI; 236 case TETHERING_USB: 237 case TETHERING_NCM: 238 return TRANSPORT_USB; 239 case TETHERING_BLUETOOTH: 240 return TRANSPORT_BLUETOOTH; 241 case TETHERING_ETHERNET: 242 case TETHERING_VIRTUAL: // For virtual machines. 243 return TRANSPORT_ETHERNET; 244 default: 245 throw new IllegalArgumentException("Invalid interface type: " + interfaceType); 246 } 247 } 248 } 249