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 android.net.TetherStatsParcel; 19 import android.net.TetheringRequestParcel; 20 import android.util.Log; 21 22 import androidx.annotation.NonNull; 23 24 import com.android.net.module.util.JniUtil; 25 import com.android.net.module.util.bpf.TetherStatsValue; 26 27 import java.io.FileDescriptor; 28 import java.net.Inet6Address; 29 import java.net.SocketException; 30 import java.net.UnknownHostException; 31 import java.util.Arrays; 32 import java.util.Objects; 33 34 /** 35 * The classes and the methods for tethering utilization. 36 * 37 * {@hide} 38 */ 39 public class TetheringUtils { 40 static { getTetheringJniLibraryName()41 System.loadLibrary(getTetheringJniLibraryName()); 42 } 43 44 public static final byte[] ALL_NODES = new byte[] { 45 (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 46 }; 47 48 /** The name should be com_android_networkstack_tethering_util_jni. */ getTetheringJniLibraryName()49 public static String getTetheringJniLibraryName() { 50 return JniUtil.getJniLibraryName(TetheringUtils.class.getPackage()); 51 } 52 53 /** 54 * Configures a socket for receiving and sending ICMPv6 neighbor advertisments. 55 * @param fd the socket's {@link FileDescriptor}. 56 */ setupNaSocket(FileDescriptor fd)57 public static native void setupNaSocket(FileDescriptor fd) 58 throws SocketException; 59 60 /** 61 * Configures a socket for receiving and sending ICMPv6 neighbor solicitations. 62 * @param fd the socket's {@link FileDescriptor}. 63 */ setupNsSocket(FileDescriptor fd)64 public static native void setupNsSocket(FileDescriptor fd) 65 throws SocketException; 66 67 /** 68 * The object which records offload Tx/Rx forwarded bytes/packets. 69 * TODO: Replace the inner class ForwardedStats of class OffloadHardwareInterface with 70 * this class as well. 71 */ 72 public static class ForwardedStats { 73 public final long rxBytes; 74 public final long rxPackets; 75 public final long txBytes; 76 public final long txPackets; 77 ForwardedStats()78 public ForwardedStats() { 79 rxBytes = 0; 80 rxPackets = 0; 81 txBytes = 0; 82 txPackets = 0; 83 } 84 ForwardedStats(long rxBytes, long txBytes)85 public ForwardedStats(long rxBytes, long txBytes) { 86 this.rxBytes = rxBytes; 87 this.rxPackets = 0; 88 this.txBytes = txBytes; 89 this.txPackets = 0; 90 } 91 ForwardedStats(long rxBytes, long rxPackets, long txBytes, long txPackets)92 public ForwardedStats(long rxBytes, long rxPackets, long txBytes, long txPackets) { 93 this.rxBytes = rxBytes; 94 this.rxPackets = rxPackets; 95 this.txBytes = txBytes; 96 this.txPackets = txPackets; 97 } 98 ForwardedStats(@onNull TetherStatsParcel tetherStats)99 public ForwardedStats(@NonNull TetherStatsParcel tetherStats) { 100 rxBytes = tetherStats.rxBytes; 101 rxPackets = tetherStats.rxPackets; 102 txBytes = tetherStats.txBytes; 103 txPackets = tetherStats.txPackets; 104 } 105 ForwardedStats(@onNull TetherStatsValue tetherStats)106 public ForwardedStats(@NonNull TetherStatsValue tetherStats) { 107 rxBytes = tetherStats.rxBytes; 108 rxPackets = tetherStats.rxPackets; 109 txBytes = tetherStats.txBytes; 110 txPackets = tetherStats.txPackets; 111 } 112 ForwardedStats(@onNull ForwardedStats other)113 public ForwardedStats(@NonNull ForwardedStats other) { 114 rxBytes = other.rxBytes; 115 rxPackets = other.rxPackets; 116 txBytes = other.txBytes; 117 txPackets = other.txPackets; 118 } 119 120 /** Add Tx/Rx bytes/packets and return the result as a new object. */ 121 @NonNull add(@onNull ForwardedStats other)122 public ForwardedStats add(@NonNull ForwardedStats other) { 123 return new ForwardedStats(rxBytes + other.rxBytes, rxPackets + other.rxPackets, 124 txBytes + other.txBytes, txPackets + other.txPackets); 125 } 126 127 /** Subtract Tx/Rx bytes/packets and return the result as a new object. */ 128 @NonNull subtract(@onNull ForwardedStats other)129 public ForwardedStats subtract(@NonNull ForwardedStats other) { 130 // TODO: Perhaps throw an exception if any negative difference value just in case. 131 final long rxBytesDiff = Math.max(rxBytes - other.rxBytes, 0); 132 final long rxPacketsDiff = Math.max(rxPackets - other.rxPackets, 0); 133 final long txBytesDiff = Math.max(txBytes - other.txBytes, 0); 134 final long txPacketsDiff = Math.max(txPackets - other.txPackets, 0); 135 return new ForwardedStats(rxBytesDiff, rxPacketsDiff, txBytesDiff, txPacketsDiff); 136 } 137 138 /** Returns the string representation of this object. */ 139 @NonNull toString()140 public String toString() { 141 return String.format("ForwardedStats(rxb: %d, rxp: %d, txb: %d, txp: %d)", rxBytes, 142 rxPackets, txBytes, txPackets); 143 } 144 } 145 146 /** 147 * Configures a socket for receiving ICMPv6 router solicitations and sending advertisements. 148 * @param fd the socket's {@link FileDescriptor}. 149 * @param ifIndex the interface index. 150 */ setupRaSocket(FileDescriptor fd, int ifIndex)151 public static native void setupRaSocket(FileDescriptor fd, int ifIndex) 152 throws SocketException; 153 154 /** 155 * Read s as an unsigned 16-bit integer. 156 */ uint16(short s)157 public static int uint16(short s) { 158 return s & 0xffff; 159 } 160 161 /** Check whether two TetheringRequestParcels are the same. */ isTetheringRequestEquals(final TetheringRequestParcel request, final TetheringRequestParcel otherRequest)162 public static boolean isTetheringRequestEquals(final TetheringRequestParcel request, 163 final TetheringRequestParcel otherRequest) { 164 if (request == otherRequest) return true; 165 166 return request != null && otherRequest != null 167 && request.tetheringType == otherRequest.tetheringType 168 && Objects.equals(request.localIPv4Address, otherRequest.localIPv4Address) 169 && Objects.equals(request.staticClientAddress, otherRequest.staticClientAddress) 170 && request.exemptFromEntitlementCheck == otherRequest.exemptFromEntitlementCheck 171 && request.showProvisioningUi == otherRequest.showProvisioningUi 172 && request.connectivityScope == otherRequest.connectivityScope; 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