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