Reputation: 70
How to Limit the Internet Speed of a VPN Connection on Android programmatically.
I have managed to control the upload speed using the OpenVPN "shaper" option, but I want to control both upload and download speeds.
Thank you.
Upvotes: -1
Views: 525
Reputation: 2858
Android does not provide direct APIs for throttling network speed. You may consider using ThrottlingInterceptor
and use it like :
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new ThrottlingInterceptor(1024 * 100)) // Limit to 100 KBps
.build();
TLDR;
. .. ...
import android.app.PendingIntent;
import android.content.Intent;
import android.net.VpnService;
import android.os.ParcelFileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.TimeUnit;
public class MyVpnService extends VpnService implements Runnable {
private Thread mThread;
private ParcelFileDescriptor mInterface;
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (mThread != null) {
mThread.interrupt();
}
mThread = new Thread(this, "MyVpnThread");
mThread.start();
return START_STICKY;
}
@Override
public void onDestroy() {
if (mThread != null) {
mThread.interrupt();
}
try {
if (mInterface != null) {
mInterface.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
try {
// Configure the TUN interface
Builder builder = new Builder();
builder.addAddress("10.0.0.2", 24);
builder.addRoute("0.0.0.0", 0);
mInterface = builder.setSession("MyVPNService")
.setConfigureIntent(PendingIntent.getActivity(this, 0, new Intent(this, MainActivity.class), 0))
.establish();
// Packets to be sent are queued in this input stream.
FileInputStream in = new FileInputStream(mInterface.getFileDescriptor());
// Packets received need to be written to this output stream.
FileOutputStream out = new FileOutputStream(mInterface.getFileDescriptor());
// Allocate the buffer for a single packet.
ByteBuffer packet = ByteBuffer.allocate(32767);
while (true) {
// Read the outgoing packet from the input stream.
int length = in.read(packet.array());
if (length > 0) {
// Process the packet
packet.limit(length);
processPacket(packet);
// Send the packet to the output stream.
out.write(packet.array(), 0, length);
packet.clear();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void processPacket(ByteBuffer packet) {
// Desired bandwidth limit in bytes per second (100 KB/s)
long bytesPerSecond = 102400; // 100 KBps
// Calculate the time in nanoseconds that should elapse per byte
long nanosPerByte = TimeUnit.SECONDS.toNanos(1) / bytesPerSecond;
// The number of bytes in the packet
int packetSize = packet.limit();
// The expected time to send this packet
long expectedTime = packetSize * nanosPerByte;
// Introduce delay based on the expected time to throttle the speed
try {
Thread.sleep(TimeUnit.NANOSECONDS.toMillis(expectedTime));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("Throttling interrupted", e);
}
}
}
<service
android:name=".MyVpnService"
android:permission="android.permission.BIND_VPN_SERVICE">
<intent-filter>
<action android:name="android.net.VpnService" />
</intent-filter>
</service>
import android.content.Intent;
import android.net.VpnService;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
private static final int VPN_REQUEST_CODE = 0x0F;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = VpnService.prepare(this);
if (intent != null) {
startActivityForResult(intent, VPN_REQUEST_CODE);
} else {
onActivityResult(VPN_REQUEST_CODE, RESULT_OK, null);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == VPN_REQUEST_CODE && resultCode == RESULT_OK) {
Intent intent = new Intent(this, MyVpnService.class);
startService(intent);
}
}
}
ThrottlingInterceptor
as a Serviceclass ThrottlingInterceptor(private val maxBytesPerSecond: Long) {
private var bytesTransferred: Long = 0
private var lastTime: Long = System.currentTimeMillis()
@Synchronized
fun shouldThrottle(packetSize: Int): Boolean {
val currentTime = System.currentTimeMillis()
if (currentTime - lastTime > 1000) {
lastTime = currentTime
bytesTransferred = 0
}
if (bytesTransferred + packetSize > maxBytesPerSecond) {
return true
}
bytesTransferred += packetSize
return false
}
@Synchronized
fun reset() {
lastTime = System.currentTimeMillis()
bytesTransferred = 0
}
}
class MyVpnService : VpnService() {
private val interceptor = ThrottlingInterceptor(100 * 1024 / 8) // 100 kbps
override fun onCreate() {
super.onCreate()
// Initialize the VPN service here
}
private fun processPacket(packet: ByteBuffer) {
val packetSize = packet.remaining()
if (interceptor.shouldThrottle(packetSize)) {
try {
Thread.sleep(1000) // Sleep for 1 second to reset the limit
} catch (e: InterruptedException) {
e.printStackTrace()
}
interceptor.reset()
}
// Forward the packet
forwardPacket(packet)
}
private fun forwardPacket(packet: ByteBuffer) {
// Logic to forward packet to destination
}
}
import java.io.IOException;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okio.Buffer;
import okio.ForwardingSource;
import okio.Okio;
import okio.Source;
public class ThrottlingInterceptor implements Interceptor {
private final long bytesPerSecond;
public ThrottlingInterceptor(long bytesPerSecond) {
this.bytesPerSecond = bytesPerSecond;
}
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response originalResponse = chain.proceed(request);
return originalResponse.newBuilder()
.body(new ThrottlingResponseBody(originalResponse.body(), bytesPerSecond))
.build();
}
private static class ThrottlingResponseBody extends okhttp3.ResponseBody {
private final okhttp3.ResponseBody responseBody;
private final long bytesPerSecond;
private BufferedSource bufferedSource;
public ThrottlingResponseBody(okhttp3.ResponseBody responseBody, long bytesPerSecond) {
this.responseBody = responseBody;
this.bytesPerSecond = bytesPerSecond;
}
@Override
public okhttp3.MediaType contentType() {
return responseBody.contentType();
}
@Override
public long contentLength() {
return responseBody.contentLength();
}
@Override
public BufferedSource source() {
if (bufferedSource == null) {
bufferedSource = Okio.buffer(new ThrottlingSource(responseBody.source(), bytesPerSecond));
}
return bufferedSource;
}
private static class ThrottlingSource extends ForwardingSource {
private final long bytesPerSecond;
private long totalBytesRead = 0L;
private long startTime = System.currentTimeMillis();
ThrottlingSource(Source source, long bytesPerSecond) {
super(source);
this.bytesPerSecond = bytesPerSecond;
}
@Override
public long read(Buffer sink, long byteCount) throws IOException {
long bytesRead = super.read(sink, byteCount);
if (bytesRead == -1) return -1;
totalBytesRead += bytesRead;
long elapsedTime = System.currentTimeMillis() - startTime;
long expectedTime = (totalBytesRead * 1000) / bytesPerSecond;
if (expectedTime > elapsedTime) {
try {
Thread.sleep(expectedTime - elapsedTime);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new IOException("Throttling interrupted", e);
}
}
return bytesRead;
}
}
}
}
Use OkHttpClient with ThrottlingInterceptor while requesting network request
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new ThrottlingInterceptor(1024 * 100)) // Limit to 100 KBps
.build();
Request request = new Request.Builder()
.url("https://your.api.endpoint")
.build();
try (Response response = client.newCall(request).execute()) {
// Handle the response
} catch (IOException e) {
e.printStackTrace();
}
References
https://developer.android.com/reference/android/net/VpnService
https://privatevpn.com/blog/guides/449/how-to-remove-internet-throttling
Android: How to determine Network speed in android programmatically
How to limit speed of internet connection on Android emulator?
https://www.quora.com/Is-there-any-option-to-restrict-data-speed-in-Android-mobile
Upvotes: 2