• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/**
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import vpn from '@ohos.net.vpn';
17import { BusinessError } from '@kit.BasicServicesKit';
18import type common from '@ohos.app.ability.common';
19import VpnConfig from './VpnConfig';
20import VpnConstant from './VpnConstant';
21import { VpnConfigModel } from '../../model/vpnImpl/VpnConfigModel';
22import LogUtil from '../../../../../../../common/utils/src/main/ets/default/baseUtil/LogUtil';
23
24const MODULE_TAG: string = 'setting_vpn:VpnConnectModel:';
25AppStorage.setOrCreate(VpnConstant.STORAGE_KEY_CONNECT_STATE, VpnConstant.VPN_STATE_NONE);
26
27/**
28 * app management service class
29 */
30export class VpnConnectModel {
31  private static instance: VpnConnectModel;
32
33  private connection: vpn.VpnConnection | undefined = undefined;
34  private timeoutId: number = undefined;
35  private connectedVpnId: string = undefined;
36  private connectState: number = VpnConstant.VPN_STATE_NONE;
37  private replaceConnectVpnConfig: VpnConfig = undefined;
38
39  public static getInstance(): VpnConnectModel {
40    if (!this.instance) {
41      this.instance = new VpnConnectModel();
42    }
43    return this.instance;
44  }
45
46  setReplaceConnectVpn(vpnConfig: VpnConfig): void {
47    this.replaceConnectVpnConfig = vpnConfig;
48  }
49
50  setConnectState(state: number, id?: string): void {
51    if (id) {
52      this.connectedVpnId = id;
53    }
54    LogUtil.info(MODULE_TAG + `setConnectState: ${this.connectState} -> ${state} id:${this.connectedVpnId}`);
55    this.connectState = state;
56    AppStorage.setOrCreate(VpnConstant.STORAGE_KEY_CONNECT_STATE, state);
57  }
58
59  getConnectedVpnId(): string {
60    return this.connectedVpnId;
61  }
62
63  isConnecting(vpnId: string): boolean {
64    return (vpnId === this.connectedVpnId) &&
65      (this.connectState === VpnConstant.VPN_STATE_CONNECTING);
66  }
67
68  isConnectedOrConnecting(vpnId: string): boolean {
69    let connectState = this.connectState;
70    return (vpnId === this.connectedVpnId) &&
71      ((connectState === VpnConstant.VPN_STATE_CONNECTING) ||
72        (connectState === VpnConstant.VPN_STATE_CONNECTED));
73  }
74
75  onConnectStateChange(isConnected: boolean): void {
76    LogUtil.info(MODULE_TAG + `onConnectStateChange isConnected=` + isConnected);
77    this.removeTimeout();
78    if (isConnected) {
79      this.setConnectState(VpnConstant.VPN_STATE_CONNECTED);
80      return;
81    }
82    if (this.replaceConnectVpnConfig) {
83      let config = this.replaceConnectVpnConfig;
84      this.replaceConnectVpnConfig = undefined;
85      this.setUp(config);
86      return;
87    }
88    if (this.connectState === VpnConstant.VPN_STATE_CONNECTING) {
89      this.setConnectState(VpnConstant.VPN_STATE_CONNECT_FAILED);
90      return;
91    }
92    if (this.connectState === VpnConstant.VPN_STATE_DISCONNECTING) {
93      this.setConnectState(VpnConstant.VPN_STATE_DISCONNECTED);
94    }
95  }
96
97  init(context: common.UIAbilityContext): void {
98    this.connection = vpn.createVpnConnection(context);
99    try {
100      LogUtil.info(MODULE_TAG + `vpn on start`);
101      vpn.on('connect', (data) => {
102        this.onConnectStateChange(data?.isConnected);
103      })
104    } catch (error) {
105      LogUtil.error(MODULE_TAG + `vpn on error = ${JSON.stringify(error)}`);
106    }
107    this.getConnectedVpn();
108  }
109
110  release(): void {
111    try {
112      LogUtil.info(MODULE_TAG + `vpnConnection on subscribe off start`);
113      vpn.off('connect', (data) => {
114        LogUtil.info(MODULE_TAG + `vpnConnection off data = ${data}`);
115      })
116    } catch (error) {
117      LogUtil.error(MODULE_TAG + `vpnConnection off error = ${JSON.stringify(error)}`);
118    }
119  }
120
121  removeTimeout(): void {
122    if (this.timeoutId !== undefined) {
123      LogUtil.info(MODULE_TAG + `removeTimeout timeoutId = ${this.timeoutId}`);
124      clearTimeout(this.timeoutId);
125    }
126  }
127
128  async setUp(vpnConfig: VpnConfig): Promise<void> {
129    if (vpnConfig === undefined || vpnConfig === null) {
130      LogUtil.info(MODULE_TAG + `setUp failed, invalid param.`);
131      return;
132    }
133    LogUtil.info(MODULE_TAG + `setUp start`);
134    this.setConnectState(VpnConstant.VPN_STATE_CONNECTING, vpnConfig.vpnId);
135    this.removeTimeout();
136    this.timeoutId = setTimeout(() => {
137      LogUtil.info(MODULE_TAG + `setUp timeout vpnId=` + vpnConfig.vpnId);
138      this.setConnectState(VpnConstant.VPN_STATE_DISCONNECTING, vpnConfig.vpnId);
139      this.destroy((error: string) => {
140        if (error) {
141          LogUtil.error(MODULE_TAG + `vpn destroy failed, error:` + error);
142        }
143        this.setConnectState(VpnConstant.VPN_STATE_CONNECT_FAILED, vpnConfig.vpnId);
144      });
145    }, VpnConstant.VPN_CONNECT_TIME_OUT_DURATION);
146    try {
147      await this.connection.setUp(vpnConfig);
148    } catch (err) {
149      LogUtil.error(MODULE_TAG + `setUp error = ${JSON.stringify(err)}`);
150      this.removeTimeout();
151      VpnConfigModel.getInstance().showToast($r('app.string.vpn_error_operation_failed') + ' error:' + err);
152      this.setConnectState(VpnConstant.VPN_STATE_CONNECT_FAILED, vpnConfig.vpnId);
153      // destroy connection
154      this.destroy((error: string) => {
155        if (error) {
156          LogUtil.info(MODULE_TAG + `vpn destroy failed, error:` + error);
157        }
158      });
159    }
160  }
161
162  getConnectedVpn(): void {
163    try {
164      LogUtil.info(MODULE_TAG + `getConnectedVpn start`);
165      vpn.getConnectedSysVpnConfig().then((data) => {
166        if (data && data.addresses && data.vpnId) {
167          this.setConnectState(VpnConstant.VPN_STATE_CONNECTED, data.vpnId);
168        } else {
169          this.setConnectState(VpnConstant.VPN_STATE_NONE);
170        }
171      });
172    } catch (error) {
173      LogUtil.error(MODULE_TAG + `getConnectedVpn error:  ${JSON.stringify(error)}`);
174      this.setConnectState(VpnConstant.VPN_STATE_NONE);
175    }
176  }
177
178  isHapAvailable(): boolean {
179    return this.timeoutId !== undefined;
180  }
181
182  destroy(callback): void {
183    this.removeTimeout();
184    this.connection?.destroy((error: BusinessError) => {
185      if (error) {
186        LogUtil.info(MODULE_TAG + `destroy error = ${JSON.stringify(error)}`);
187      }
188      callback(error?.message);
189    });
190  }
191
192  uint8ArrayToString(u8a: Uint8Array): string {
193    let dataStr = "";
194    for (let i = 0; i < u8a.length; i++) {
195      dataStr += String.fromCharCode(u8a[i])
196    }
197    return dataStr;
198  }
199}