1/* 2 * Copyright (c) 2025 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 { connection } from '@kit.NetworkKit'; 17import { BusinessError, emitter } from '@kit.BasicServicesKit'; 18import { radio } from '@kit.TelephonyKit'; 19import { wifiManager } from '@kit.ConnectivityKit'; 20import { logger } from './Logger'; 21import { NetworkEventData } from './EmitterData'; 22import { HashMap, JSON } from '@kit.ArkTS'; 23 24type NetworkData = boolean | connection.NetBlockStatusInfo | connection.NetBearType 25 | connection.NetConnectionPropertyInfo | connection.NetCapabilityInfo; 26 27// 网络监听emitter事件 28export enum NetworkEventName { 29 // 注册网络监听订阅事件 30 NetObserverRegister, 31 // 网络可用 32 NetAvailable, 33 // 网络阻塞 34 NetBlock, 35 // 网络丢失/断开 36 NetLost, 37 // 当网络能力变化时,如网络从无网络到有网络、从4G切换到5G 38 NetCapabilitiesChange, 39 // 网络不可用 40 NetUnavailable, 41 // WIFI状态改变 42 WifiStateChange, 43 // WIFI连接状态改变 44 WifiConnectionChange, 45 // 弱网 46 WeakNet, 47 // 订阅网络连接信息变化事件,当网络连接信息变化时,如从无网络到有网络、从Wi-Fi切换到蜂窝 48 NetConnectionPropertiesChange 49} 50 51export class NetUtils { 52 public static instance: NetUtils; 53 private connectionMap: HashMap<connection.NetBearType, connection.NetConnection> = new HashMap(); 54 // 网络状态监听eventId 55 private networkEventId: number = 10001; 56 // 网络监听相关结果数据 57 private emitterEvent: NetworkEventData; 58 59 constructor() { 60 this.emitterEvent = new NetworkEventData(this.networkEventId); 61 } 62 63 static getInstance(): NetUtils { 64 if (!NetUtils.instance) { 65 NetUtils.instance = new NetUtils(); 66 } 67 return NetUtils.instance; 68 } 69 70 public getEmitterEvent(): NetworkEventData { 71 return this.emitterEvent; 72 } 73 74 private setEventPriority(priority: emitter.EventPriority): void { 75 this.emitterEvent.priority = priority; 76 } 77 78 /** 79 * 通过emitter将结果传递给页面 80 * @param eventName 事件名称 81 * @param status 网络状态(可传递不同的监听结果数据类型) 82 * @param netType 网络类型(WI-FI、蜂窝数据等) 83 * @param priority emitter事件权重 84 */ 85 private postEvent(eventName: NetworkEventName, status: NetworkData, netType?: connection.NetBearType, 86 priority?: emitter.EventPriority) { 87 this.emitterEvent.priority = priority; 88 emitter.emit(this.emitterEvent, { 89 data: new NetEventData(eventName, status, netType) 90 }) 91 } 92 93 /** 94 * 开启网络监听 95 * @param netType 需要监听的网络类型 96 */ 97 public startNetObserve(...netType: connection.NetBearType[]) { 98 netType.forEach((type: connection.NetBearType) => { 99 this.networkObserve(type); 100 if (type === connection.NetBearType.BEARER_WIFI) { 101 this.wifiStateObserve(); 102 } 103 }) 104 } 105 106 /** 107 * 停止网络监听 108 */ 109 public stopNetObserve(netType: connection.NetBearType) { 110 this.connectionMap.get(netType).unregister(() => { 111 logger.info('Success unregister:' + netType.toString()); 112 }) 113 } 114 115 /** 116 * 停止所有网络监听 117 */ 118 public stopAllNetObserve() { 119 emitter.off(this.getEmitterEvent().eventId); 120 this.connectionMap.forEach((netConnection: connection.NetConnection, netType: connection.NetBearType) => { 121 netConnection.unregister(() => { 122 logger.info('Success unregister:' + netType.toString()); 123 }); 124 }) 125 } 126 127 /** 128 * 获取当前网络连接类型 129 * 判断当前使用的网络是蜂窝或者Wi-Fi 130 * BEARER_CELLULAR时(值为0),表示蜂窝网络 131 * BEARER_WIFI时(值为1),表示Wi-Fi网络 132 * BEARER_BLUETOOTH时(值为2),表示蓝牙网络 133 * BEARER_ETHERNET时(值为3),表示以太网网络 134 * BEARER_VPN时(值为4),表示VPN网络 135 * @returns 当前可用网络类型的列表 136 */ 137 getNetworkConnectionType(): Array<connection.NetBearType> { 138 try { 139 // 获取默认激活的数据网络 140 let netHandle = connection.getDefaultNetSync(); 141 if (!netHandle || netHandle.netId === 0) { 142 return []; 143 } 144 // 获取网络的类型、拥有的能力等信息 145 let netCapability = connection.getNetCapabilitiesSync(netHandle); 146 return netCapability.bearerTypes; 147 } catch (e) { 148 let err = e as BusinessError; 149 logger.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message); 150 return []; 151 } 152 } 153 154 /** 155 * 判断当前网络是否可用 156 * 使用网络前,例如打开一个应用时,需要检查当前连接的网络是否可用。 157 * 如果可用,则正常进行网络请求;如果不可用,则需要提示用户网络不可用。 158 * @returns 当前网络是否可用 159 */ 160 judgeHasNet(): boolean { 161 try { 162 let netHandle = connection.getDefaultNetSync(); 163 if (!netHandle || netHandle.netId === 0) { 164 return false; 165 } 166 let netCapability = connection.getNetCapabilitiesSync(netHandle); 167 let cap = netCapability.networkCap || []; 168 if (cap.includes(connection.NetCap.NET_CAPABILITY_VALIDATED)) { 169 //connection.NetCap.NET_CAPABILITY_VALIDATED,该值代表网络是通的,能够发起HTTP和HTTPS的请求。 170 // 网络信息变化,网络可用 171 return true; 172 } else { 173 // 网络信息变化,网络不可用 174 return false; 175 } 176 } catch (e) { 177 let err = e as BusinessError; 178 logger.error('JudgeHasNet' + JSON.stringify(err)); 179 } 180 return false; 181 } 182 183 /** 184 * 获取网络状态,查询手机卡注册网络的运营商名称、是否处于漫游状态、设备的网络注册状态等信息 185 */ 186 getNetworkStatus() { 187 radio.getNetworkState((err: BusinessError, data: radio.NetworkState) => { 188 if (err) { 189 logger.error(`getNetworkState failed, callback: err->${JSON.stringify(err)}`); 190 } 191 // regState字段表示设备的网络注册状态 192 // (REG_STATE_POWER_OFF,值为3)蜂窝无线电已关闭,modem下电,无法和网侧进行通信 193 logger.info('Success getNetworkStatus:' + JSON.stringify(data)); 194 }); 195 } 196 197 /** 198 * 查询当前网络是3G/4G/5G 199 * SignalInformation.signalType表示蜂窝网络的类型 200 * 未知(值为0) 201 * 2G:GSM(值为1)、CDMA(值为2) 202 * 3G:WCDMA(值为3)、TDSCDMA(值为4) 203 * 4G:LTE(值为5) 204 * 5G:NR(值为6) 205 * @returns 指定SIM卡槽对应的注册网络信号强度信息列表 206 */ 207 async getSignalType(): Promise<radio.SignalInformation[]> { 208 let slotId: number = await radio.getPrimarySlotId(); 209 let data: radio.SignalInformation[] = radio.getSignalInformationSync(slotId); 210 // signalType代表网络类型NetworkType 211 let signalType = data[0].signalType; 212 logger.info('getSignalType:' + JSON.stringify(data)); 213 return data; 214 } 215 216 /** 217 * 查询WLAN是否已使能,可以帮助用户快速了解自己是否可以使用Wi-Fi网络进行连接。 218 * 当Wi-Fi跟蜂窝同时存在时,有助于实现网络连接的无缝切换。 219 * @returns true:已使能, false:未使能 220 */ 221 getWifiStatus(): boolean { 222 try { 223 let isWifiActive: boolean = wifiManager.isWifiActive(); 224 return isWifiActive; 225 } catch (error) { 226 logger.error('failed:' + JSON.stringify(error)); 227 } 228 return false; 229 } 230 231 /** 232 * 查询WLAN是否已连接是判断能否通过Wi-Fi 进行各种网络活动的直接方式。 233 * @returns true,已连接。false,未连接 234 */ 235 getWifiIsConnected(): boolean { 236 try { 237 let ret = wifiManager.isConnected(); 238 logger.info('isConnected:' + ret); 239 return ret; 240 } catch (error) { 241 logger.error('failed:' + JSON.stringify(error)); 242 } 243 return false; 244 } 245 246 /** 247 * 获取当前连接Wi-Fi的信号强度 248 * 通过获取信号强度,用户可以初步判断当前网络是否能够满足其对速度的需求。 249 * 取值范围为[0, 4],取值越大表示信号越强 250 * @returns 信号强度,取值范围[0, 4],-1表示报错 251 */ 252 async getSignalLevel(): Promise<number> { 253 try { 254 let wifiLinkedInfo: wifiManager.WifiLinkedInfo = await wifiManager.getLinkedInfo(); 255 let rssi = wifiLinkedInfo.rssi; 256 let band = wifiLinkedInfo.band; 257 let level = wifiManager.getSignalLevel(rssi, band); 258 logger.info('level:' + JSON.stringify(level)); 259 return level; 260 } catch (error) { 261 logger.error('failed:' + JSON.stringify(error)); 262 } 263 return -1; 264 } 265 266 /** 267 * 监听网络状态 268 * @param netType 监听的网络类型(WI-FI、蜂窝数据等) 269 */ 270 networkObserve(netType: connection.NetBearType) { 271 // TODO:根据网络类型,设置不同的网络监听,用于WI-FI和蜂窝网络切换时判断各自网络状态的变化。 272 let netConnection: connection.NetConnection = connection.createNetConnection({ 273 netCapabilities: { 274 bearerTypes: [netType] 275 } 276 }) 277 // 注册网络监听,注册成功后才能监听到对应类型的网络状态变化 278 netConnection.register((error: BusinessError) => { 279 let result = true; 280 if (error) { 281 logger.info('NetUtils', 'NetType :' + netType + ', network register failed: ' + JSON.stringify(error)); 282 result = false; 283 } 284 logger.info('NetUtils', 'NetType :' + netType + ', network register succeed'); 285 this.postEvent(NetworkEventName.NetObserverRegister, result, netType); 286 }); 287 // 网络能力改变监听,当网络能力变化时,如网络从无网络到有网络、从4G切换到5G时,会触发该事件。 288 netConnection.on('netCapabilitiesChange', (data: connection.NetCapabilityInfo) => { 289 logger.info('NetUtils', 'NetType :' + netType + ', network netCapabilitiesChange: ' + JSON.stringify(data)); 290 this.postEvent(NetworkEventName.NetCapabilitiesChange, data, netType); 291 }) 292 // 网络可用监听,当网络可用时触发该事件。 293 netConnection.on('netAvailable', (data: connection.NetHandle) => { 294 logger.info('NetUtils', 295 'NetType :' + netType + ', network succeeded to get netAvailable: ' + JSON.stringify(data)); 296 // 检查默认数据网络是否被激活,使用同步方式返回接口,如果被激活则返回true,否则返回false。 297 this.postEvent(NetworkEventName.NetAvailable, connection.hasDefaultNetSync(), netType); 298 }); 299 300 // 订阅网络阻塞状态事件,当网络阻塞时,如网络性能下降、数据传输出现延迟等情况时,会触发该事件 301 netConnection.on('netBlockStatusChange', (data: connection.NetBlockStatusInfo) => { 302 logger.info('NetUtils', 'NetType :' + netType + ', network netBlockStatusChange ' + JSON.stringify(data)); 303 this.postEvent(NetworkEventName.NetBlock, data, netType); 304 }); 305 // 网络连接信息变化监听,当网络连接信息变化时,如从无网络到有网络、从Wi-Fi切换到蜂窝时,会触发该事件。 306 netConnection.on('netConnectionPropertiesChange', (data: connection.NetConnectionPropertyInfo) => { 307 logger.info('NetUtils', 308 'NetType :' + netType + ', network netConnectionPropertiesChange ' + JSON.stringify(data)); 309 this.postEvent(NetworkEventName.NetConnectionPropertiesChange, data, netType); 310 }); 311 312 // 订阅网络丢失事件,当网络严重中断或正常断开时触发该事件 313 // 网络丢失是指网络严重中断或正常断开事件,当断开Wi-Fi时,是属于正常断开网络连接,会触发netLost事件 314 netConnection.on('netLost', (data: connection.NetHandle) => { 315 this.postEvent(NetworkEventName.NetLost, true, netType); 316 logger.info('NetUtils', 'NetType :' + netType + ', Succeeded to get netLost: ' + JSON.stringify(data)); 317 }); 318 319 // 订阅网络不可用事件,当网络不可用时触发该事件 320 // 网络不可用是指网络不可用事件,当连接的网络不能使用时,会触发netUnavailable事件。 321 netConnection.on('netUnavailable', () => { 322 logger.info('NetUtils', 'NetType :' + netType + ', Succeeded to get unavailable net event'); 323 this.postEvent(NetworkEventName.NetUnavailable, true, netType); 324 }); 325 326 this.connectionMap.set(netType, netConnection); 327 } 328 329 /** 330 * WI-FI状态监听 331 */ 332 wifiStateObserve() { 333 // 注册WLAN状态改变事件 334 // 0,未激活;1,已激活;2,激活中;3:去激活中 335 wifiManager.on('wifiStateChange', (result: number) => { 336 logger.info('NetUtils', 'wifiStateChange: ' + result); 337 this.postEvent(NetworkEventName.WifiStateChange, result); 338 }); 339 // 注册WLAN连接状态改变事件 340 // 0,已断开;1,已连接 341 wifiManager.on('wifiConnectionChange', (result: number) => { 342 logger.info('NetUtils', 'wifiConnectionChange: ' + result); 343 this.postEvent(NetworkEventName.WifiConnectionChange, result); 344 }); 345 } 346 347 /** 348 * 解析网络监听结果,用于打印日志 349 * @param data 网络监听结果 350 * @returns 解析后的结果数据 351 */ 352 parseResult(data: emitter.EventData): string { 353 if (data.data) { 354 if (!data.data.eventName) { 355 logger.info('parseResult data.data.eventName is undefined.'); 356 return ''; 357 } 358 } else { 359 logger.info('parseResult data.data is undefined.'); 360 return ''; 361 } 362 let result = ''; 363 let name: number = (data.data)!.eventName ?? -1; 364 switch (name) { 365 case NetworkEventName.NetObserverRegister.valueOf(): 366 result = 'NetObserverRegister'; 367 break; 368 case NetworkEventName.NetAvailable.valueOf(): 369 result = 'NetAvailable'; 370 break; 371 case NetworkEventName.NetBlock.valueOf(): 372 result = 'NetBlock'; 373 break; 374 case NetworkEventName.NetLost.valueOf(): 375 result = 'NetLost'; 376 break; 377 case NetworkEventName.NetCapabilitiesChange.valueOf(): 378 result = 'NetCapabilitiesChange'; 379 break; 380 case NetworkEventName.NetUnavailable.valueOf(): 381 result = 'NetUnavailable'; 382 break; 383 case NetworkEventName.NetConnectionPropertiesChange.valueOf(): 384 result = 'NetConnectionPropertiesChange'; 385 break; 386 case NetworkEventName.WifiStateChange.valueOf(): 387 result = 'WifiStateChange'; 388 break; 389 case NetworkEventName.WifiConnectionChange.valueOf(): 390 result = 'WifiConnectionChange'; 391 break; 392 case NetworkEventName.WeakNet.valueOf(): 393 result = 'WeakNet'; 394 break; 395 default: 396 result = name.toString(); 397 break; 398 } 399 400 let netTemp: string = ''; 401 let temp: number = data.data!.netType ?? -1; 402 if (temp === 1) { 403 netTemp = 'WIFI'; 404 } 405 if (temp === 0) { 406 netTemp = 'CELLULAR'; 407 } 408 if (temp === -1) { 409 netTemp = temp.toString(); 410 } 411 412 result = result + '------' + (data.data!.status ?? -1) + '------' + netTemp; 413 414 return result; 415 } 416} 417 418export class NetEventData { 419 public eventName: NetworkEventName; 420 public status: NetworkData; 421 public netType: connection.NetBearType; 422 423 constructor(eventName: NetworkEventName, status: NetworkData, netType: connection.NetBearType) { 424 this.eventName = eventName; 425 this.status = status; 426 this.netType = netType; 427 } 428}