Skip to content

Instantly share code, notes, and snippets.

@Kr328
Created January 14, 2026 19:54
Show Gist options
  • Select an option

  • Save Kr328/914397abee1b811cb699e25e88216c84 to your computer and use it in GitHub Desktop.

Select an option

Save Kr328/914397abee1b811cb699e25e88216c84 to your computer and use it in GitHub Desktop.
A simple helper to get global proxy in Android. (Deprecated: use Settings.Global instead to get better performance and compatibility)
package org.kisara.kisara;
import android.annotation.SuppressLint;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.ProxyInfo;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.stream.Collectors;
import dalvik.system.PathClassLoader;
final class ProxyHelper {
private static final String TAG = "ProxyHelper";
private static boolean getGlobalProxyAvailable = true;
private static ReflectFunction<ConnectivityManager, ProxyInfo> getGlobalProxyFunc = null;
@SuppressLint("PrivateApi")
private static ReflectFunction<ConnectivityManager, ProxyInfo> findGetGlobalProxyFunction() throws ReflectiveOperationException {
if (Build.VERSION.SDK_INT >= 28) {
long beginAt = System.nanoTime();
final IBinder proxy = (IBinder) HiddenApiClassLoader
.load("android.os.ServiceManager")
.getMethod("getService", String.class)
.invoke(null, Context.CONNECTIVITY_SERVICE);
final Object stub = HiddenApiClassLoader
.load("android.net.IConnectivityManager$Stub")
.getMethod("asInterface", android.os.IBinder.class)
.invoke(null, proxy);
if (stub == null) {
throw new IllegalStateException("failed to create connectivity stub");
}
final Method method = stub.getClass().getMethod("getGlobalProxy");
final long elapsed = System.nanoTime() - beginAt;
Log.d(TAG, "getGlobalProxy: " + method + " in " + elapsed + " ns");
return (c) -> (ProxyInfo) method.invoke(stub);
} else {
@SuppressWarnings("JavaReflectionMemberAccess") final Method method = ConnectivityManager.class
.getDeclaredMethod("getGlobalProxy");
method.setAccessible(true);
Log.d(TAG, "getGlobalProxy: " + method);
return (c) -> (ProxyInfo) method.invoke(c);
}
}
@SuppressLint("PrivateApi")
public static @Nullable ProxyInfo getGlobalProxy(@NonNull ConnectivityManager connectivity) {
if (!getGlobalProxyAvailable) {
return null;
}
ReflectFunction<ConnectivityManager, ProxyInfo> get = getGlobalProxyFunc;
if (get == null) {
try {
get = getGlobalProxyFunc = findGetGlobalProxyFunction();
} catch (Throwable e) {
Log.w(TAG, "no available method to get global proxy", e);
getGlobalProxyAvailable = false;
return null;
}
}
try {
return get.apply(connectivity);
} catch (InvocationTargetException e) {
final Throwable cause = e.getTargetException();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
} else {
throw new RuntimeException(cause);
}
} catch (ReflectiveOperationException e) {
Log.w(TAG, "failed to invoke get global proxy", e);
getGlobalProxyAvailable = false;
return null;
} catch (Throwable e) {
Log.w(TAG, "unexpected error", e);
getGlobalProxyAvailable = false;
return null;
}
}
private interface ReflectFunction<T, R> {
R apply(T t) throws ReflectiveOperationException;
}
private static class HiddenApiClassLoader extends PathClassLoader {
public HiddenApiClassLoader(boolean full) {
super(findConnectivityClassPath(full), ClassLoader.getSystemClassLoader());
}
private static String findConnectivityClassPath(final boolean framework) {
final String bootClassPath = System.getProperty("java.boot.class.path", "");
assert bootClassPath != null;
if (framework) {
String frameworkClassPath = Arrays.stream(bootClassPath.split(":"))
.filter(s -> s.contains("framework"))
.collect(Collectors.joining(":"));
Log.v(TAG, "framework class path: " + frameworkClassPath);
return frameworkClassPath;
} else {
String filteredClassPath = Arrays.stream(bootClassPath.split(":"))
.filter(s -> s.contains("connectivity"))
.collect(Collectors.joining(":"));
Log.v(TAG, "filtered class path: " + filteredClassPath);
return filteredClassPath;
}
}
public static Class<?> load(final String name) throws ClassNotFoundException {
try {
return Filtered.loader.loadClass(name);
} catch (ClassNotFoundException e) {
return Full.loader.loadClass(name);
}
}
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
if (name.startsWith("android.net.IConnectivityManager")) {
return findClass(name);
}
return super.loadClass(name, resolve);
}
private static class Full {
private static final ClassLoader loader = new HiddenApiClassLoader(true);
}
private static class Filtered {
private static final ClassLoader loader = new HiddenApiClassLoader(false);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment