From e7c7a0a957a6371e4e5968c8169223b667a3a3f6 Mon Sep 17 00:00:00 2001 From: rong_zhichao Date: Wed, 7 May 2025 16:55:42 +0800 Subject: [PATCH 1/2] add sysvpn Signed-off-by: rong_zhichao --- .../entry/src/main/ets/common/VpnConfig.ts | 96 +++++++++ .../entry/src/main/ets/common/VpnTypeModel.ts | 55 +++++ .../entry/src/main/ets/pages/Index.ets | 19 ++ .../entry/src/main/ets/pages/SysVpn.ets | 194 ++++++++++++++++++ .../main/ets/vpnability/SysVpnExtAbility.ets | 159 ++++++++++++++ .../entry/src/main/module.json5 | 11 +- .../resources/base/profile/main_pages.json | 3 +- 7 files changed, 530 insertions(+), 7 deletions(-) create mode 100644 code/DocsSample/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/common/VpnConfig.ts create mode 100644 code/DocsSample/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/common/VpnTypeModel.ts create mode 100644 code/DocsSample/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/pages/SysVpn.ets create mode 100644 code/DocsSample/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/vpnability/SysVpnExtAbility.ets diff --git a/code/DocsSample/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/common/VpnConfig.ts b/code/DocsSample/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/common/VpnConfig.ts new file mode 100644 index 0000000000..b73ee337cf --- /dev/null +++ b/code/DocsSample/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/common/VpnConfig.ts @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import vpn from '@ohos.net.vpn'; +import { VpnTypeModel } from './VpnTypeModel'; + +/** + * extend system VpnConig + */ +export default class VpnConfig implements vpn.SysVpnConfig { + // vpnConfig + addresses: Array = []; + routes?: Array; + dnsAddresses?: Array; + searchDomains?: Array; + isLegacy: boolean = true; + + // sysVpnConfig + vpnId: string = ''; + vpnName: string = ''; + vpnType: vpn.SysVpnType = VpnTypeModel.TYPE_IKEV2_IPSEC_MSCHAPv2; + userName?: string; + password?: string; + saveLogin: boolean = false; + userId?: number; + forwardingRoutes?: string; + trustedApplications: Array = []; + blockedApplications: Array = []; +} + +export class OpenVpnConfig extends VpnConfig implements vpn.OpenVpnConfig { + ovpnConfigFilePath?: string // openVpn file name + ovpnConfigContent?: string // openVpn config + ovpnConfig?: string // openVpn config base64 + ovpnAuthType: number = 0 // openvpn auth type + ovpnProtocolFileRaw?: string // Protocol + ovpnProtocol: number = 0 // 0:tcp 1:udp + ovpnAddressPortFileRaw?: string // port + ovpnPort?: string // openvpn port + askpass: string // private key password + ovpnCaCertFilePath?: string // openVpn CA FilePath + ovpnUserCertFilePath?: string // openVpn USER FilePath + ovpnPrivateKeyFilePath?: string // openVpn private key FilePath + ovpnCaCertFileRaw?: string // ca raw data + ovpnCaCert?: string // CA data + ovpnUserCertFileRaw?: string // + ovpnUserCert?: string // + ovpnPrivateKeyFileRaw?: string // private key raw data + ovpnPrivateKey?: string // private key data + ovpnUserPassFileRaw?: string // userpass raw d + ovpnProxyHostFileRaw?: string // host raw data + ovpnProxyHost?: string //ovpn host + ovpnProxyPort?: string //ovpn port + ovpnProxyUserPassFileRaw?: string // userpass data + ovpnProxyUser?: string //ovpn user + ovpnProxyPass?: string //ovpn pass +} + +export class IpsecVpnConfig extends VpnConfig implements vpn.IpsecVpnConfig { + ipsecIdentifier?: string // ipsec identifier + ipsecPreSharedKey?: string // ipsec pre sharedKey + l2tpSharedKey?: string // L2TP secret + ipsecPublicUserCertConfig?: string // public userCert config + ipsecPublicUserCertFilePath?: string // public userCert FilePath + ipsecPrivateUserCertConfig?: string // private userCert config + ipsecPrivateUserCertFilePath?: string // private userCert FilePath + ipsecCaCertConfig?: string // ca config + ipsecCaCertFilePath?: string // ca FilePath + ipsecPublicServerCertConfig?: string // public serverCert config + ipsecPublicServerCertFilePath?: string // public serverCert FilePath + ipsecPrivateServerCertConfig?: string // private serverCert config + ipsecPrivateServerCertFilePath?: string // private serverCert FilePath + swanctlConfig?: string // swanctl config base64 + strongSwanConfig?: string // strongswan config base64 + optionsL2tpdClient?: string // optionsL2tpd Client base64 + xl2tpdConfig?: string // xl2tpd config base64 + ipsecConfig?: string // swanctl config base64 + ipsecSecrets?: string // swanctl config base64 +} + +export class VpnListItem { + vpnName: string //vpnName + vpnId: string //UUID +} \ No newline at end of file diff --git a/code/DocsSample/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/common/VpnTypeModel.ts b/code/DocsSample/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/common/VpnTypeModel.ts new file mode 100644 index 0000000000..4f340e2340 --- /dev/null +++ b/code/DocsSample/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/common/VpnTypeModel.ts @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * support vpn type & displayname + */ +export class VpnTypeModel { + static readonly TYPE_IKEV2_IPSEC_MSCHAPv2: number = 1; // vpn.SysVpnType.IKEV2_IPSEC_MSCHAPv2; + static readonly TYPE_IKEV2_IPSEC_PSK: number = 2; // vpn.SysVpnType.IKEV2_IPSEC_PSK; + static readonly TYPE_IKEV2_IPSEC_RSA: number = 3; // vpn.SysVpnType.IKEV2_IPSEC_RSA; + static readonly TYPE_L2TP_IPSEC_PSK: number = 4; // vpn.SysVpnType.L2TP_IPSEC_PSK; + static readonly TYPE_L2TP_IPSEC_RSA: number = 5; // vpn.SysVpnType.L2TP_IPSEC_RSA; + static readonly TYPE_IPSEC_XAUTH_PSK: number = 6; // vpn.SysVpnType.IPSEC_XAUTH_PSK; + static readonly TYPE_IPSEC_XAUTH_RSA: number = 7; // vpn.SysVpnType.IPSEC_XAUTH_RSA; + static readonly TYPE_IPSEC_HYBRID_RSA: number = 8; // vpn.SysVpnType.IPSEC_HYBRID_RSA; + static readonly TYPE_OPENVPN: number = 9; // vpn.SysVpnType.OPENVPN; + static readonly TYPE_L2TP: number = 10; + + private static instance: VpnTypeModel; + + public static getInstance(): VpnTypeModel { + if (!this.instance) { + this.instance = new VpnTypeModel(); + } + return this.instance; + } + + public uint8ArrayToString(fileData): string { + let dataString = ''; + for (let i = 0; i < fileData.length; i++) { + dataString += String.fromCharCode(fileData[i]); + } + return dataString; + } + + public stringToUint8Array(str: string): Uint8Array { + let arr: number[] = []; + for (let i = 0, j = str.length; i < j; ++i) { + arr.push(str.charCodeAt(i)); + } + return new Uint8Array(arr); + } +} \ No newline at end of file diff --git a/code/DocsSample/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/pages/Index.ets b/code/DocsSample/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/pages/Index.ets index 391fc3f88e..bc32795dc0 100644 --- a/code/DocsSample/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/pages/Index.ets +++ b/code/DocsSample/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/pages/Index.ets @@ -80,6 +80,25 @@ struct Index { .id(ComponentId.STOP_VPN_BUTTON) .width('70%') .fontSize(BUTTON_FONT_SIZE) + .margin(BUTTON_MARGIN) + + // Sys VPN 按钮 + Button('start sysvpn') + .onClick(() => { + Logger.info('developTag', '%{public}s', 'Succeeded in clicking the button.'); + router.pushUrl({ url: 'pages/SysVpn' }).then(() => { + Logger.info('developTag', '%{public}s', 'Succeeded in jumping to the second page.'); + this.flag = 'Pass'; + }).catch((err: BusinessError) => { + Logger.error('developTag', 'Failed to jump to the second page: %{public}s', + JSON.stringify(err) ?? ''); + this.flag = 'Error'; + }); + }) + .id(ComponentId.STOP_VPN_BUTTON) + .width('70%') + .fontSize(BUTTON_FONT_SIZE) + .margin(BUTTON_MARGIN) }.width('100%') }.height('100%') } diff --git a/code/DocsSample/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/pages/SysVpn.ets b/code/DocsSample/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/pages/SysVpn.ets new file mode 100644 index 0000000000..a8ee630a25 --- /dev/null +++ b/code/DocsSample/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/pages/SysVpn.ets @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { vpnExtension } from "@kit.NetworkKit"; +import { Want } from "@kit.AbilityKit"; +import Logger from "../common/Logger"; + +const TAG: string = 'SysVpn:'; + +@Entry +@Component +export struct SysVpn { + @State l2tpVpnState: string = ''; + @State ikev2PskState1: string = ''; + @State ikev2PskState2: string = ''; + @State l2tpPskState: string = ''; + + aboutToAppear(): void { + } + + onConnectClick(value: string) { + Logger.info(`${TAG} , start connect vpn :: ${value}`); + let want: Want = { + bundleName: "com.samples.vpncontrol_case", + abilityName: "SysVpnExtAbility", + parameters: { + 'connect': value + } + }; + vpnExtension.startVpnExtensionAbility(want); + } + + onDisconnectClick(value: string) { + Logger.info(`${TAG} , start disconnect vpn :: ${value}`); + let want: Want = { + bundleName: "com.samples.vpncontrol_case", + abilityName: "SysVpnExtAbility", + parameters: { + 'disconnect': value + } + }; + vpnExtension.startVpnExtensionAbility(want); + } + + build() { + Column() { + Button('授权') + .onClick(() => { + let want: Want = { + bundleName: "com.samples.vpncontrol_case", + abilityName: "SysVpnExtAbility", + }; + vpnExtension.startVpnExtensionAbility(want); + }) + .width('80%') + .fontSize(20) + .margin(16) + .stateEffect(true) + .alignSelf(ItemAlign.Center) + + + // IKEV2_IPSEC_PSK 1 + Column() { + Row() { + Text('IKEV2_IPSEC_PSK') + .fontSize(20) + Text(this.ikev2PskState1) + .fontSize(20).fontColor(Color.Blue).margin({ right: 36 }) + }.margin({ + bottom: 10 + }).width('100%').justifyContent(FlexAlign.SpaceBetween) + + Row() { + Button('连接') + .onClick(() => { + this.onConnectClick('ikev2_ipsec_psk_1'); + this.ikev2PskState1 = '已连接' + }) + .fontSize(20) + .margin(16) + .stateEffect(true) + .width(100) + + Button('断开') + .onClick(() => { + this.onDisconnectClick('ikev2_ipsec_psk_1'); + this.ikev2PskState1 = '已断开' + }) + .fontSize(20) + .margin(16) + .stateEffect(true) + .width(100) + }.width('100%').justifyContent(FlexAlign.SpaceBetween) + }.margin({ + top: 24, + left: 48, + right: 48 + }) + + // l2tp_psk + Column() { + Row() { + Text('L2TP_IPSEC_PSK') + .fontSize(20) + Text(this.l2tpPskState) + .fontSize(20).fontColor(Color.Blue).margin({ right: 36 }) + }.margin({ + bottom: 10 + }).width('100%').justifyContent(FlexAlign.SpaceBetween) + + Row() { + Button('连接') + .onClick(() => { + this.onConnectClick('l2tp_ipsec_psk_1'); + this.l2tpPskState = '已连接' + }) + .fontSize(20) + .margin(16) + .stateEffect(true) + .width(100) + + Button('断开') + .onClick(() => { + this.onDisconnectClick('l2tp_ipsec_psk_1'); + this.l2tpPskState = '已断开' + }) + .width(100) + .fontSize(20) + .margin(16) + .stateEffect(true) + }.width('100%').justifyContent(FlexAlign.SpaceBetween) + }.margin({ + top: 24, + left: 48, + right: 48 + }) + + // l2tp + Column() { + Row() { + Text('L2TP') + .fontSize(20) + Text(this.l2tpVpnState) + .fontSize(20).fontColor(Color.Blue).margin({ right: 36 }) + }.margin({ + bottom: 10 + }).width('100%').justifyContent(FlexAlign.SpaceBetween) + + Row() { + Button('连接') + .onClick(() => { + this.onConnectClick('l2tp_1'); + this.l2tpVpnState = '已连接' + }) + .fontSize(20) + .margin(16) + .stateEffect(true) + .width(100) + + Button('断开') + .onClick(() => { + this.onDisconnectClick('l2tp_1'); + this.l2tpVpnState = '已断开' + }) + .fontSize(20) + .margin(16) + .stateEffect(true) + .width(100) + }.width('100%').justifyContent(FlexAlign.SpaceBetween) + }.margin({ + top: 24, + left: 48, + right: 48 + }) + } + .alignItems(HorizontalAlign.Start) + .backgroundColor($r('sys.color.ohos_id_color_sub_background')) + .width('100%') + .height('100%') + .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM]) + } +} \ No newline at end of file diff --git a/code/DocsSample/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/vpnability/SysVpnExtAbility.ets b/code/DocsSample/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/vpnability/SysVpnExtAbility.ets new file mode 100644 index 0000000000..be29d059b0 --- /dev/null +++ b/code/DocsSample/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/vpnability/SysVpnExtAbility.ets @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Want } from '@kit.AbilityKit'; +import VpnExtensionAbility from '@ohos.app.ability.VpnExtensionAbility'; +import { vpnExtension } from '@kit.NetworkKit'; +import { util } from '@kit.ArkTS'; +import Logger from '../common/Logger'; +import VpnConfig, { IpsecVpnConfig } from '../common/VpnConfig'; +import { VpnTypeModel } from '../common/VpnTypeModel'; + + +const TAG: string = "SysVpnExtAbility:: "; + +export default class SysVpnExtAbility extends VpnExtensionAbility { + private vpnConnection: vpnExtension.VpnConnection | undefined = undefined; + ikev2IpsecPskConfig_1: IpsecVpnConfig = { + addresses: [{ + address: { + address: '192.168.1.21', + }, + prefixLength: 1 + }], + isLegacy: true, + vpnName: 'ikev2_ipsec_psk_1', + vpnId: util.generateRandomUUID(), + vpnType: VpnTypeModel.TYPE_IKEV2_IPSEC_PSK, + saveLogin: true, + trustedApplications: [], + blockedApplications: [], + ipsecIdentifier: '192.168.1.200', + ipsecPreSharedKey: '123456' + }; + ikev2IpsecPskConfig_2: IpsecVpnConfig = { + addresses: [{ + address: { + address: '192.168.1.21', + }, + prefixLength: 1 + }], + isLegacy: true, + vpnName: 'ikev2_ipsec_psk_2', + vpnId: util.generateRandomUUID(), + vpnType: VpnTypeModel.TYPE_IKEV2_IPSEC_PSK, + saveLogin: true, + trustedApplications: [], + blockedApplications: [], + ipsecIdentifier: '192.168.1.200', + ipsecPreSharedKey: '123456' + }; + l2tpPskConfig: IpsecVpnConfig = { + addresses: [{ + address: { + address: '113.140.94.102', + }, + prefixLength: 1 + }], + isLegacy: true, + vpnName: 'vpn_l2tp_ipsec_psk', + vpnId: util.generateRandomUUID(), + vpnType: VpnTypeModel.TYPE_L2TP_IPSEC_PSK, + saveLogin: true, + trustedApplications: [], + blockedApplications: [], + l2tpSharedKey: '300339', + ipsecPreSharedKey: '300339', + ipsecIdentifier: '300339', + } + + l2tpConfig: IpsecVpnConfig = { + addresses: [{ + address: { + address: '192.168.1.11', + }, + prefixLength: 24 + }], + isLegacy: true, + vpnName: 'vpn_l2tp_1', + vpnId: util.generateRandomUUID(), + vpnType: VpnTypeModel.TYPE_L2TP, + saveLogin: true, + trustedApplications: [], + blockedApplications: [], + userName: 'use', + password:'123456', + } + + onCreate(want: Want) { + } + + connectVpn(type: string) { + let config = this.getVpnConfigByType(type); + this.vpnConnection = vpnExtension.createVpnConnection(this.context); + try { + Logger.info(TAG + `start setup`); + this.vpnConnection.create(config); + } catch (err) { + Logger.error(TAG + `setUp error = ${JSON.stringify(err)}`); + } + } + + disConnectVpn(type: string) { + Logger.info(TAG + `start destroy`); + this.vpnConnection?.destroy(); + } + + getVpnConfigByType(vpnType: string): VpnConfig { + switch (vpnType) { + case 'ikev2_ipsec_psk_1': + return this.ikev2IpsecPskConfig_1; + case 'ikev2_ipsec_psk_2': + return this.ikev2IpsecPskConfig_2; + case 'l2tp_ipsec_psk_1': + return this.l2tpPskConfig; + case 'l2tp_1': + return this.l2tpConfig; + default: + return this.ikev2IpsecPskConfig_1; + } + } + + onRequest(want: Want, startId: number) { + Logger.info(TAG + `onRequest, want: ${JSON.stringify(want)}`); + if (want.parameters) { + if (want.parameters['connect']) { + let type = want.parameters['connect'] as string; + this.connectVpn(type); + } + if (want.parameters['disconnect']) { + let type = want.parameters['disconnect'] as string; + this.disConnectVpn(type); + } + } + } + + onConnect(want: Want) { + Logger.info(TAG + `onConnect, want: ${want.abilityName}`); + } + + onDisconnect(want: Want) { + Logger.info(TAG + `onDisconnect, want: ${want.abilityName}`); + } + + onDestroy() { + Logger.info(TAG + `onDestroy`); + } +} \ No newline at end of file diff --git a/code/DocsSample/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/module.json5 b/code/DocsSample/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/module.json5 index f9eda2b5e2..f9514d49bc 100644 --- a/code/DocsSample/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/module.json5 +++ b/code/DocsSample/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/module.json5 @@ -65,17 +65,16 @@ "name": "MyVpnExtAbility", "srcEntry": "./ets/vpnability/VPNExtentionAbility.ets", "type": "vpn" + }, + { + "name": "SysVpnExtAbility", + "srcEntry": "./ets/vpnability/SysVpnExtAbility.ets", + "type": "vpn" } ], "requestPermissions": [ { "name": "ohos.permission.INTERNET" - }, - { - "name": "ohos.permission.MANAGE_VPN" - }, - { - "name": "ohos.permission.NOTIFICATION_CONTROLLER" } ], } diff --git a/code/DocsSample/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/resources/base/profile/main_pages.json b/code/DocsSample/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/resources/base/profile/main_pages.json index 65b5903f30..6f0764dd10 100644 --- a/code/DocsSample/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/resources/base/profile/main_pages.json +++ b/code/DocsSample/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/resources/base/profile/main_pages.json @@ -2,6 +2,7 @@ "src": [ "pages/Index", "pages/StartVpn", - "pages/StopVpn" + "pages/StopVpn", + "pages/SysVpn" ] } -- Gitee From ce7bf00eec4cee989cb790329839019e2688703d Mon Sep 17 00:00:00 2001 From: rong_zhichao Date: Wed, 7 May 2025 16:58:55 +0800 Subject: [PATCH 2/2] add sysvpn 2 Signed-off-by: rong_zhichao --- .../VPNControl_Case/entry/src/main/module.json5 | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/code/DocsSample/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/module.json5 b/code/DocsSample/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/module.json5 index f9514d49bc..c602c814f5 100644 --- a/code/DocsSample/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/module.json5 +++ b/code/DocsSample/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/module.json5 @@ -75,6 +75,12 @@ "requestPermissions": [ { "name": "ohos.permission.INTERNET" + }, + { + "name": "ohos.permission.MANAGE_VPN" + }, + { + "name": "ohos.permission.NOTIFICATION_CONTROLLER" } ], } -- Gitee