VpnService 永远在线 "not supported by this app"

VpnService always-on "not supported by this app"

我正在尝试为 android 编写 VPN 应用程序,但无论我做什么,我的应用程序的 "Always On" 设置似乎都是灰色的。

我在 android 文档中找不到任何说明为什么 VpnService 可能不允许使用 "Always On" 功能的文档。

为了确保我清楚,如果我手动启用它,我的 VpnService 工作正常。我能够毫无问题地连接、使用它等。我只是对为什么在 Android 设置中它在我的 Vpn 服务的 "Always-On" 开关下显示为 "not supported for this device" 感到困惑。

Note: I am using a custom protocol, and on some forum somewhere I did see once that "Google doesn't allow Always-On for VPNs that don't use protocols that have been whitelisted by google", however no where have I found documentation to support this claim, so I'm doubtful that it is the cause.

阅读 Android Documentation 的发现:

  1. 如果您在 Android Manafest 中明确向 <service> 定义提供 SERVICE_META_DATA_SUPPORTS_ALWAYS_ON 标志,其值为 false,它将告诉系统您的应用程序不支持 Always On 功能。但是,如果您将其省略,则默认为 true。所以应该不需要提供这个。

  2. 根据 AppManagementFragment.java#updateRestrictedViews, it uses a function mConnectivityManager.isAlwaysOnVpnPackageSupportedForUser(mUserId, mPackageName), however I'm having difficulty following the logic of this function. When looking at ConnectivityManager.java#isAlwaysOnVpnPackageSupportedForUser, it seems to end up in ConnectivityService.java#isAlwaysOnVpnPackageSupported and ultimately points to Vpn.java#isAlwaysOnPackageSupported 中的 Android Souce,如果 one,这可能会导致您的 VPN 被认为不支持始终开启满足以下条件之一:

    1. 传递给函数的包名是空的。
    2. 系统无法使用 PackageManager. getApplicationInfoAsUser() 定位包。
    3. 申请目标小于VERSION_CODES.N
    4. 该软件包在其 AndroidManifest.xml 中不包含任何 Vpn 服务。
    5. 该包定义了 SERVICE_META_DATA_SUPPORTS_ALWAYS_ON 元数据设置为 false 的任何 VpnServices。

Note: My application targets API 29.

仅此而已。

由于我的应用程序满足 Vpn.java#isAlwaysOnPackageSupported 中规定的所有要求,我很困惑为什么它不能工作。

I've created an android studio project that demonstrates this happening on my Samsung Galaxy S10. You can find it here on github.

我的代码供参考:

我的服务配置如 Android 文档中所述。

<service
    android:name=".provider.VpnProvider"
    android:enabled="true"
    android:permission="android.permission.BIND_VPN_SERVICE"
    android:description="@string/service_description"
    android:exported="false">
    <intent-filter>
        <action android:name="android.net.VpnSerivce" />
        <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
    </intent-filter>
</service>

在我的仪表板 activity 中,我使用它来处理 Connect 按钮被点击。

public static final int REQ_START_VPN = 40;

public boolean connect() {
    Intent intent;

    try {
        intent = VpnProvider.prepare(this.getApplicationContext());
    } catch (IllegalStateException e) {
        return false;
    }

    if (intent != null) {
        try {
            this.startActivityForResult(intent, REQ_START_VPN);
            return true;
        } catch (ActivityNotFoundException e) {
            new BasicDialog(
                this, R.string.not_supported,
                R.string.not_supported_message
            ).show();
        }
    } else {
        this.onActivityResult(REQ_START_VPN, AppCompatActivity.RESULT_OK, null);
        return true;
    }
}

然后我使用以下方法处理结果:

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (resultCode == AppCompatActivity.RESULT_OK) {
        if (requestCode == REQ_START_VPN) {
            try {
                this.startService(new Intent(this, VpnProvider.class));
            } catch (IllegalStateException ignored) {}
        }
    }
}

我的 VpnProvider class 扩展 VpnService 并具有以下用于构建界面的内容:

VpnService.Builder builder = this.new Builder();
builder.setMtu(mtu);
builder.addAddress("1.2.3.4", 32);
builder.addDnsServer("8.8.8.8");
builder.addDnsServer("8.8.4.4");
builder.addRoute("0.0.0.0", 0);
vpnInterface = builder.establish();

我找到问题了。

在我的 AndroidManifest.xml 中,当我为我的服务声明 intent-filters 时,我打错了字。

<action android:name="android.net.VpnSerivce" />

而不是

<action android:name="android.net.VpnService" />

这使得我的应用程序不会通知操作系统我的应用程序具有 android 文档 here 中记录的 VpnService。

这个错字占用了我三天的时间,我再也回不来了。