我正在尝试为android编写一个VPN应用程序,无论我做什么,我的应用程序的“始终开启”设置似乎都是灰色的。
我在 android 文档的任何地方都找不到任何文档说明为什么可能不允许 VpnService 使用“始终开启”功能。
只是为了确保我清楚,如果我手动启用它,我的 VpnService 工作得很好。我可以毫无问题地连接、使用它等。我只是对为什么在 Android 设置中它在我的 Vpn 服务的“始终开启”切换下显示为“不支持此设备”感到困惑。
注意:我使用的是自定义协议,在某个论坛上,我确实看到过“谷歌不允许不使用谷歌白名单协议的 VPN 的 Always-On”,但是我在哪里找不到支持这一说法的文件,所以我怀疑这是原因。
我从阅读Android 文档中发现:
如果您在 Android Manafest 中的定义中显式提供SERVICE_META_DATA_SUPPORTS_ALWAYS_ON标志,其值将告诉系统您的应用程序不支持 Always On 功能。但是,如果您将其省略,则默认为. 所以应该没有必要提供这个。
<service>
false
true
根据AppManagementFragment.java#updateRestrictedViews中的 Android 源,它使用了一个函数
mConnectivityManager.isAlwaysOnVpnPackageSupportedForUser(mUserId, mPackageName)
,但是我很难遵循这个函数的逻辑。查看ConnectivityManager.java#isAlwaysOnVpnPackageSupportedForUser时,它似乎最终出现在ConnectivityService.java#isAlwaysOnVpnPackageSupported并最终指向Vpn.java#isAlwaysOnPackageSupported如果满足以下条件之一,这可能导致您的 VPN 被认为不支持始终开启:- 传递给函数的包名称为空。
- 系统无法使用
PackageManager. getApplicationInfoAsUser()
. - 申请目标小于
VERSION_CODES.N
- 该软件包中不包含任何 Vpn 服务
AndroidManifest.xml
。 - 该包定义了任何 VpnServices,其
SERVICE_META_DATA_SUPPORTS_ALWAYS_ON
元数据设置为false
.
注意:我的应用程序针对 API 29。
就是这样。
由于我的应用程序满足 中所述的所有要求Vpn.java#isAlwaysOnPackageSupported
,我对它为什么不起作用感到非常困惑。
我创建了一个 android studio 项目来演示我的三星 Galaxy S10 上发生的这种情况。你可以在 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>
在我的仪表板活动中,我使用它来处理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
类扩展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();