Skip to content

Redroid相关调整

adbd绑定eth0

在使用Android VPN时,系统会启动TUN网卡将所有连接进行转发,此时会导致adb连接断开。
这里通过修改adbd监听端口函数,添加SO_BINDTODEVICE参数强制绑定监听tcp:5555的socket到eth0上. packages/modules/adb/socket_spec.cpp:

c++
#include <android-base/properties.h>
int socket_spec_listen(std::string_view spec, std::string* error, int* resolved_port) {
    if (spec.starts_with("tcp:")) {
        ...

    // hack: bind eth0 when listening on tcp:5555
    if (spec == "tcp:5555" && android::base::GetBoolProperty("ro.boot.redroid_adbd_bind_eth0", true))
        adb_setsockopt(borrowed_fd{result}, SOL_SOCKET, SO_BINDTODEVICE, "eth0", 4);

    if (result >= 0 && resolved_port) {
            *resolved_port = adb_socket_get_local_port(result);
        }
    return result;

    } else if (spec.starts_with("vsock:")) {
        ...
    }
}

默认创建安全显示器

Android 12+有个secure flag机制,有些app在输入用户名密码界面会用到这个flag,导致scrcpy串流的时候会黑屏。这里通过修改surfaceflinger的方式来去掉这个限制: frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp:

cpp
sp<IBinder> SurfaceFlinger::createDisplay(const String8& displayName, bool secure,
                                          float requestedRefreshRate) {
    // onTransact already checks for some permissions, but adding an additional check here.
    // This is to ensure that only system and graphics can request to create a secure
    // display. Secure displays can show secure content so we add an additional restriction on it.
    const int uid = IPCThreadState::self()->getCallingUid();
    if (secure && uid != AID_GRAPHICS && uid != AID_SYSTEM) {
        ALOGE("Only privileged processes can create a secure display");
        return nullptr;
    }

    class DisplayToken : public BBinder {
        sp<SurfaceFlinger> flinger;
        virtual ~DisplayToken() {
             // no more references, this display must be terminated
             Mutex::Autolock _l(flinger->mStateLock);
             flinger->mCurrentState.displays.removeItem(wp<IBinder>::fromExisting(this));
             flinger->setTransactionFlags(eDisplayTransactionNeeded);
         }
     public:
        explicit DisplayToken(const sp<SurfaceFlinger>& flinger)
            : flinger(flinger) {
        }
    };

    sp<BBinder> token = sp<DisplayToken>::make(sp<SurfaceFlinger>::fromExisting(this));

    Mutex::Autolock _l(mStateLock);
    // Display ID is assigned when virtual display is allocated by HWC.
    DisplayDeviceState state;
    secure = base::GetBoolProperty("androidboot.redroid_create_secure_display", true);
    state.isSecure = secure;
    state.displayName = displayName;
    state.requestedRefreshRate = Fps::fromValue(requestedRefreshRate);
    mCurrentState.displays.add(token, state);
    return token;
}

虚假WiFi

有些App会检测网络连接类型,如果既不是流量也不是WiFi就会提示无网络。
这里从framework层的connectivitymanager类入手,对app返回一个虚假的WiFi连接。
大量参考了Waydroid的patch链接,在此对Waydroid开发组的工作表示感谢。

packages/modules/Connectivity/framework/src/android/net/ConnectivityManager.java:

java
import android.net.wifi.FakeWiFi;
...
public NetworkInfo getActiveNetworkInfo() {
        try {
           NetworkInfo network = mService.getActiveNetworkInfo();
           if (FakeWiFi.isHackEnabled())
               return FakeWiFi.maybeOverwrite(network);
           return network;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
}

public NetworkInfo getNetworkInfo(int networkType) {
        try {
            NetworkInfo network = mService.getNetworkInfo(networkType);
            if (networkType == ConnectivityManager.TYPE_WIFI && FakeWiFi.isHackEnabled())
                return FakeWiFi.maybeOverwrite(network);
            return network;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
}

public NetworkInfo[] getAllNetworkInfo() {
         try {
             NetworkInfo[] networks = mService.getAllNetworkInfo();
             if (!FakeWiFi.isHackEnabled())
                 return networks;
 
             int i;
             boolean wifi_found = false;
             for (i = 0; i < networks.length; i++) {
                 if (networks[i].getType() == ConnectivityManager.TYPE_WIFI) {
                     wifi_found = true;
                     break;
                 }
             }
 
             if (wifi_found && networks[i].isConnected())
                 return networks;
 
             if (wifi_found) {
                 networks[i] = FakeWiFi.getFakeNetworkInfo();
             } else {
                 NetworkInfo[] extended = new NetworkInfo[networks.length + 1];
                 for (i = 0; i < networks.length; i++)
                     extended[i] = networks[i];
                 extended[networks.length] = FakeWiFi.getFakeNetworkInfo();
                 networks = extended;
             }
             return networks;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
}

public boolean isActiveNetworkMetered() {
        if (FakeWiFi.isHackEnabled())
            return false;

        try {
            return mService.isActiveNetworkMetered();
        } catch (RemoteException e) {
            ...
        }
}
...

packages/modules/Wifi/framework/java/android/net/wifi/FakeWiFi.java:

java
package android.net.wifi;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiSsid;
import android.net.wifi.SupplicantState;
import android.net.DhcpInfo;
import android.os.PatternMatcher;
import android.os.SystemProperties;
import android.util.Log;

import java.lang.Exception;
import java.lang.reflect.Field;
import java.net.NetworkInterface;
import java.net.InetAddress;
import java.util.*;
import java.util.regex.Pattern;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;

public final class FakeWiFi {
    private static final String LOG_TAG = "FakeWiFi";

    private FakeWiFi() {}

    public static boolean isHackEnabled() {
       return SystemProperties.getBoolean("ro.boot.redroid_fake_wifi", false);
    }

    @NonNull
    public static NetworkInfo getFakeNetworkInfo()
    {
        NetworkInfo info = createNetworkInfo(ConnectivityManager.TYPE_WIFI, true);
        return info;
    }

    @NonNull
    public static NetworkInfo createNetworkInfo(final int type, final boolean connected)
    {
        NetworkInfo networkInfo = new NetworkInfo(type, 0, "WIFI", null);
        networkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);

        // networkInfo.setIsAvailable(true);
        try {
            Field isAvailable = networkInfo.getClass().getDeclaredField("mIsAvailable");
            isAvailable.setAccessible(true);
            isAvailable.setBoolean(networkInfo, true);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return networkInfo;
    }

    @NonNull
    private static WifiSsid createWifiSsid()
    {
        return WifiSsid.createFromAsciiEncoded(SystemProperties.get("ro.boot.redroid_fake_wifi_ssid", "FakeWiFi"));
    }

    @NonNull
    public static WifiInfo createWifiInfo()
    {
        IpInfo ip = getIpInfo();
        InetAddress addr = (ip != null ? ip.addr : null);

        WifiInfo info = new WifiInfo.Builder()
                .setNetworkId(1)
                .setBssid(SystemProperties.get("ro.boot.redroid_fake_wifi_bssid", "66:55:44:33:22:11"))
                .setRssi(200) // MAX_RSSI
                .build();

        info.setSupplicantState(SupplicantState.COMPLETED);
        info.setMacAddress(SystemProperties.get("ro.boot.redroid_fake_wifi_mac", "11:22:33:44:55:66"));
        info.setInetAddress(addr);
        info.setLinkSpeed(SystemProperties.getInt("ro.boot.redroid_fake_wifi_speed", 866));  // Mbps
        info.setFrequency(5000); // MHz
        info.setSSID(createWifiSsid());
        return info;
    }

    public static class IpInfo
    {
        NetworkInterface intf;
        InetAddress addr;
        String ip;
        int ip_hex;
        int netmask_hex;
    }

    // get current ip and netmask
    @Nullable
    public static IpInfo getIpInfo()
    {
        try
        {
            List<NetworkInterface> interfaces = Collections.list(NetworkInterface.getNetworkInterfaces());
            for (NetworkInterface intf : interfaces)
            {
                List<InetAddress> addrs = Collections.list(intf.getInetAddresses());
                for (InetAddress addr : addrs)
                {
                    if (!addr.isLoopbackAddress())
                    {
                        String sAddr = addr.getHostAddress().toUpperCase();
                        boolean isIPv4 = isIPv4Address(sAddr);
                        if (isIPv4)
                        {
                            IpInfo info = new IpInfo();
                            info.addr = addr;
                            info.intf = intf;
                            info.ip = sAddr;
                            info.ip_hex = InetAddress_to_hex(addr);
                            info.netmask_hex = netmask_to_hex(intf.getInterfaceAddresses().get(0).getNetworkPrefixLength());
                            return info;
                        }
                    }
                }
            }
        } catch (Exception ex) { } // for now eat exceptions
        return null;
    }

    public static boolean isIPv4Address(@NonNull String input) {
        Pattern IPV4_PATTERN = Pattern.compile("^(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}$");
        return IPV4_PATTERN.matcher(input).matches();
    }

    public static int netmask_to_hex(int netmask_slash)
    {
        int r = 0;
        int b = 1;
        for (int i = 0; i < netmask_slash;  i++, b = b << 1)
            r |= b;
        return r;
    }

    // for DhcpInfo
    private static int InetAddress_to_hex(InetAddress a)
    {
        int result = 0;
        byte b[] = a.getAddress();
        for (int i = 0; i < 4; i++)
            result |= (b[i] & 0xff) << (8 * i);
        return result;
    }

    @NonNull
    public static DhcpInfo createDhcpInfo()
    {
        DhcpInfo i = new DhcpInfo();
        IpInfo ip = getIpInfo();
        i.ipAddress = ip.ip_hex;
        i.netmask = ip.netmask_hex;
        i.dns1 = 0x04040404;
        i.dns2 = 0x08080808;
        // gateway, leaseDuration, serverAddress
        return i;
    }

    @Nullable
    public static NetworkInfo maybeOverwrite(@Nullable NetworkInfo network) {
        if (network == null || network.getType() != ConnectivityManager.TYPE_WIFI || !network.isConnected())
            network = getFakeNetworkInfo();
        return network;
    }
}

packages/modules/Wifi/framework/java/android/net/wifi/WifiManager.java:

java
import android.net.wifi.FakeWiFi;
...
public WifiInfo getConnectionInfo() {
    if (FakeWiFi.isHackEnabled())
        return FakeWiFi.createWifiInfo();
    ...
}
public DhcpInfo getDhcpInfo() {
         if (FakeWiFi.isHackEnabled() && FakeWiFi.getIpInfo() != null)
            return FakeWiFi.createDhcpInfo();
            ...
}
 

public int getWifiState() {
         if (FakeWiFi.isHackEnabled())
            return WifiManager.WIFI_STATE_ENABLED;
            ...
}
 

public boolean isWifiEnabled() {
        if (FakeWiFi.isHackEnabled())
            return true;
            ...
}

导入自定义App

vendor/redroid_external_apps/Android.bp:

android_app_import {
    name: "Via",
    presigned: true,
    apk: "Via.apk",
    dex_preopt: {
        enabled: false,
    },
    product_specific: true,
}

将Via.apk放置于同一个目录下,随后编辑你的设备的device.mk:

makefile
PRODUCT_PACKAGES += Via