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 的发现:
如果您在 Android Manafest 中明确向 <service>
定义提供 SERVICE_META_DATA_SUPPORTS_ALWAYS_ON 标志,其值为 false
,它将告诉系统您的应用程序不支持 Always On 功能。但是,如果您将其省略,则默认为 true
。所以应该不需要提供这个。
根据 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 被认为不支持始终开启满足以下条件之一:
- 传递给函数的包名是空的。
- 系统无法使用
PackageManager. getApplicationInfoAsUser()
定位包。
- 申请目标小于
VERSION_CODES.N
- 该软件包在其
AndroidManifest.xml
中不包含任何 Vpn 服务。
- 该包定义了
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。
这个错字占用了我三天的时间,我再也回不来了。
我正在尝试为 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 的发现:
如果您在 Android Manafest 中明确向
<service>
定义提供 SERVICE_META_DATA_SUPPORTS_ALWAYS_ON 标志,其值为false
,它将告诉系统您的应用程序不支持 Always On 功能。但是,如果您将其省略,则默认为true
。所以应该不需要提供这个。根据 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 被认为不支持始终开启满足以下条件之一:- 传递给函数的包名是空的。
- 系统无法使用
PackageManager. getApplicationInfoAsUser()
定位包。 - 申请目标小于
VERSION_CODES.N
- 该软件包在其
AndroidManifest.xml
中不包含任何 Vpn 服务。 - 该包定义了
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。
这个错字占用了我三天的时间,我再也回不来了。