1/** 2 * Copyright (c) 2021 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 */ 15import LogUtil from '../../../../../../../common/utils/src/main/ets/default/baseUtil/LogUtil'; 16import ConfigData from '../../../../../../../common/utils/src/main/ets/default/baseUtil/ConfigData'; 17import wifi from '@ohos.wifiManager'; 18import BaseModel from '../../../../../../../common/utils/src/main/ets/default/model/BaseModel'; 19import { BusinessError } from '@ohos.base'; 20 21const MODULE_TAG = ConfigData.TAG + 'WifiModel -> '; 22const Undefined_TaskId = -1; 23export interface WifiScanInfo { 24 ssid: string, 25 bssid: string, 26 rssi: number, 27 band: number, 28 frequency: number, 29 timestamp: number, 30 securityType: number, 31}; 32 33export enum ConnState { 34 /** The device is searching for an available AP. */ 35 SCANNING, 36 37 /** The Wi-Fi connection is being set up. */ 38 CONNECTING, 39 40 /** The Wi-Fi connection is being authenticated. */ 41 AUTHENTICATING, 42 43 /** The IP address of the Wi-Fi connection is being obtained. */ 44 OBTAINING_IPADDR, 45 46 /** The Wi-Fi connection has been set up. */ 47 CONNECTED, 48 49 /** The Wi-Fi connection is being torn down. */ 50 DISCONNECTING, 51 52 /** The Wi-Fi connection has been torn down. */ 53 DISCONNECTED, 54 55 /** Failed to set up the Wi-Fi connection. */ 56 UNKNOWN 57} 58 59export enum WiFiSummaryMap { 60 CONNECTED, 61 CONNECTING, 62 SAVE_ENCRYPTED, 63 SAVE_OPEN, 64 ENCRYPTED, 65 OPEN, 66 OBTAINING_IP, 67} 68 69export enum WiFiIntensityMap { 70 GOOD, 71 WELL, 72 NORMAL, 73 BAD, 74} 75 76export enum WiFiEncryptMethodMap { 77 OPEN, 78 WEP, 79 WPA, 80 WPA2, 81} 82 83export class ApScanResult { 84 // interface WifiScanInfo 85 private apInfo = { 86 ssid:'', 87 bssid: '', 88 rssi: -100, 89 band: 0, 90 frequency: 0, 91 timestamp: 0, 92 securityType: 1, 93 }; 94 private connectStatus: number = ConnState.UNKNOWN; 95 private isSaved: boolean = false; 96 97 constructor(apInfo?: any); 98 99 constructor(apInfo: any) { 100 if (apInfo === null || apInfo === undefined) { 101 return; 102 } 103 this.apInfo = apInfo; 104 }; 105 106 getApInfo() { 107 return this.apInfo; 108 } 109 110 getSignalLevel(): number { 111 return wifi.getSignalLevel(this.apInfo.rssi, this.apInfo.band); 112 } 113 114 isConnected(): boolean { 115 return (this.connectStatus === ConnState.CONNECTED); 116 } 117 118 isSavedConfig(): boolean { 119 return (this.isSaved === true); 120 } 121 122 isSecurityAp(): boolean { 123 // WiFiSecurityType is enum from 0 to 4, 0 is `Invalid`, 1 is `Open`, 2 to 4 is `Encrypted` 124 return (this.apInfo.securityType !== 1); 125 } 126 127 isValidAp(): boolean { 128 // no ssid or signal level 0 is invalid 129 return (this.apInfo.ssid !== '' && this.getSignalLevel() != 0); 130 } 131 132 updateConnectStatus(status: number) { 133 this.connectStatus = status; 134 } 135 136 updateSavedStatus(status: boolean) { 137 this.isSaved = status; 138 } 139 140 renderToListModel(): { settingIcon: string, settingSummary: number, settingTitle: string, settingValue: string, 141 settingArrow: string, settingArrowStyle: string, settingUri: string, apInfo: WifiScanInfo } { 142 function generateArrow(that: ApScanResult): string { 143 let signalLevel: string = that.getSignalLevel().toString(); 144 let lockPrefix: string = 'lock_'; 145 if (that.isSecurityAp() !== true) { 146 lockPrefix = ''; 147 } 148 let result: string = `/res/image/ic_wifi_${lockPrefix}signal_${signalLevel}_dark.svg`; 149 return result; 150 } 151 152 function generateSummary(that: ApScanResult): number { 153 if (that.isConnected()) { 154 return WiFiSummaryMap.CONNECTED; 155 } 156 if (that.connectStatus === ConnState.CONNECTING) { 157 return WiFiSummaryMap.CONNECTING; 158 } 159 if (that.connectStatus === ConnState.OBTAINING_IPADDR) { 160 return WiFiSummaryMap.OBTAINING_IP; 161 } 162 if (that.isSavedConfig()) { 163 if (that.isSecurityAp()) { 164 return WiFiSummaryMap.SAVE_ENCRYPTED; 165 } else { 166 return WiFiSummaryMap.SAVE_OPEN; 167 } 168 } else { 169 if (that.isSecurityAp()) { 170 return WiFiSummaryMap.ENCRYPTED; 171 } else { 172 return WiFiSummaryMap.OPEN; 173 } 174 } 175 } 176 177 let ret = { 178 settingIcon: '', 179 settingSummary: generateSummary(this), 180 settingTitle: this.apInfo.ssid, 181 settingValue: '', 182 settingArrow: generateArrow(this), 183 settingArrowStyle: 'wifi', 184 settingUri: '', 185 apInfo: this.apInfo, 186 }; 187 return ret; 188 } 189 190 toString(): string { 191 return `apInfo is: ssid=${this.getApInfo().ssid} signal=${this.getSignalLevel()} isConnected=${this.isConnected()}`; 192 } 193 194 static compare(x: ApScanResult, y: ApScanResult): number { 195 let xApInfo = x.getApInfo(); 196 let yApInfo = y.getApInfo(); 197 // rssi value is negative number 198 return ((-xApInfo.rssi) - (-yApInfo.rssi)); 199 } 200 201 static filter(arr: ApScanResult[]): ApScanResult[] { 202 let hash = {}; 203 return arr.reduce((total, currItem) => { 204 if (!hash[currItem.getApInfo().ssid]) { 205 hash[currItem.getApInfo().ssid] = true; 206 total.push(currItem); 207 } 208 return total; 209 }, []); 210 } 211 212 static index(arr: ApScanResult[], target: ApScanResult): number { 213 return arr.map((item) => { 214 return item.getApInfo().ssid; 215 }).indexOf(target.getApInfo().ssid); 216 } 217} 218 219export class WifiModel extends BaseModel { 220 private userSelectedAp: ApScanResult = new ApScanResult(); 221 private linkedApInfo: wifi.WifiLinkedInfo = null 222 private scanTaskId: number = Undefined_TaskId; 223 private isScanning: boolean = false; 224 225 destroyWiFiModelData() { 226 this.linkedApInfo = undefined; 227 this.setUserSelectedAp(null); 228 AppStorage.SetOrCreate('slConnectedWifi', (new ApScanResult()).renderToListModel()); 229 } 230 231 registerWiFiStatusObserver(callback) { 232 LogUtil.info(MODULE_TAG + 'start register wifi status observer'); 233 wifi.on('wifiStateChange', callback); 234 } 235 236 unregisterWiFiStatusObserver() { 237 LogUtil.info(MODULE_TAG + 'start unregister wifi status observer'); 238 wifi.off('wifiStateChange'); 239 } 240 241 registerWiFiConnectionObserver(callback) { 242 LogUtil.info(MODULE_TAG + 'start register wifi connection observer'); 243 wifi.on('wifiConnectionChange', callback); 244 } 245 246 unregisterWiFiConnectionObserver() { 247 LogUtil.info(MODULE_TAG + 'start unregister wifi connection observer'); 248 try { 249 if (wifi.isWifiActive()) { 250 wifi.off('wifiConnectionChange'); 251 } 252 } catch (error) { 253 let e: BusinessError = error as BusinessError; 254 LogUtil.error(MODULE_TAG + `off failed errorCode: ${e.code}, Message: ${e.message}`); 255 } 256 } 257 258 setUserSelectedAp(apInfo?: any) { 259 if (apInfo === null || typeof apInfo === 'undefined') { 260 this.userSelectedAp = new ApScanResult(); 261 } 262 this.userSelectedAp = new ApScanResult(apInfo); 263 } 264 265 isSavedAp(ssid: string): boolean { 266 let deviceConfigs: any[] = wifi.getDeviceConfigs(); 267 for (let i = 0; i < deviceConfigs.length; i++) { 268 if (ssid === deviceConfigs[i].ssid) { 269 return true; 270 } 271 } 272 return false; 273 } 274 275 isWiFiActive(): boolean { 276 const isActive: boolean = wifi.isWifiActive(); 277 LogUtil.info(MODULE_TAG + 'check WiFi active status is : ' + isActive); 278 return isActive; 279 } 280 281 isWiFiConnected(): boolean { 282 let ret = wifi.isConnected(); 283 LogUtil.info(MODULE_TAG + 'check WiFi connected status is : ' + ret); 284 return ret; 285 } 286 287 enableWiFi() { 288 if (wifi.isWifiActive() === true) { 289 LogUtil.info(MODULE_TAG + 'wifi is already active'); 290 return; 291 } 292 try { 293 wifi.enableWifi(); 294 }catch(error){ 295 LogUtil.info(MODULE_TAG + "enable failed:" + JSON.stringify(error)); 296 } 297 } 298 299 disableWifi() { 300 this.setUserSelectedAp(null); 301 302 if (wifi.isWifiActive() !== true) { 303 LogUtil.info(MODULE_TAG + 'wifi is already inactive'); 304 return; 305 } 306 try { 307 wifi.disableWifi(); 308 }catch(error){ 309 LogUtil.info(MODULE_TAG + "disAble failed:" + JSON.stringify(error)); 310 } 311 } 312 313 scanWiFi() { 314 try { 315 wifi.startScan() 316 } catch (error) { 317 let e: BusinessError = error as BusinessError; 318 LogUtil.error(MODULE_TAG + `startScan failed errorCode: ${e.code}, Message: ${e.message}`); 319 } 320 } 321 322 connectWiFi(password: string) { 323 let apInfo = this.userSelectedAp.getApInfo(); 324 let ret = false; 325 let connectParam: wifi.WifiDeviceConfig = { 326 "ssid": apInfo.ssid, 327 "bssid": apInfo.bssid, 328 "preSharedKey": password, 329 "isHiddenSsid": false, // we don't support connect to hidden ap yet 330 "securityType": apInfo.securityType 331 }; 332 LogUtil.info(MODULE_TAG + 'disconnect WiFi isConnected is ' + wifi.isConnected()); 333 if (wifi.isConnected() === true) { 334 wifi.disconnect(); 335 LogUtil.info(MODULE_TAG + 'disconnect WiFi ret is ' + ret); 336 this.registerWiFiConnectionObserver((code: Number) => { 337 if (code === 0) { 338 try { 339 wifi.connectToDevice(connectParam); 340 this.unregisterWiFiConnectionObserver(); 341 } catch (error) { 342 let e: BusinessError = error as BusinessError; 343 LogUtil.error(MODULE_TAG + `connectToDevice failed errorCode: ${e.code}, Message: ${e.message}`); 344 } 345 } 346 }) 347 } else { 348 try { 349 wifi.connectToDevice(connectParam); 350 LogUtil.info(MODULE_TAG + 'connect WiFi ret is ' + ret); 351 } catch (error) { 352 let e: BusinessError = error as BusinessError; 353 LogUtil.error(MODULE_TAG + `connectToDevice failed errorCode: ${e.code}, Message: ${e.message}`); 354 } 355 } 356 return ret; 357 } 358 359 /** 360 * Disconnect wifi 361 */ 362 disconnectWiFi() { 363 this.setUserSelectedAp(null); 364 365 let ret = wifi.disconnect(); 366 LogUtil.info(MODULE_TAG + 'disconnect WiFi result is : ' + ret); 367 return ret; 368 } 369 370 getSignalIntensity(apInfo: WifiScanInfo) { 371 let result = wifi.getSignalLevel(apInfo.rssi, apInfo.band); 372 if (result <= 1) { 373 return WiFiIntensityMap.BAD; 374 } 375 if (result <= 2) { 376 return WiFiIntensityMap.NORMAL; 377 } 378 if (result <= 3) { 379 return WiFiIntensityMap.WELL; 380 } 381 return WiFiIntensityMap.GOOD; 382 } 383 384 getEncryptMethod(apInfo: WifiScanInfo) { 385 if (apInfo.securityType === 1) { 386 return WiFiEncryptMethodMap.OPEN; 387 } 388 if (apInfo.securityType === 2) { 389 return WiFiEncryptMethodMap.WEP; 390 } 391 return WiFiEncryptMethodMap.WPA2; 392 } 393 394 getLinkInfo() { 395 return this.linkedApInfo; 396 } 397 398 removeDeviceConfig(apInfo: WifiScanInfo) { 399 LogUtil.info(MODULE_TAG + 'start to removeDeviceConfig'); 400 let deviceConfigs: any[] = wifi.getDeviceConfigs(); 401 let networkId: number = -1; 402 for (let i = 0; i < deviceConfigs.length; i++) { 403 if (deviceConfigs[i].ssid === apInfo.ssid) { 404 networkId = deviceConfigs[i].netId; 405 break; 406 } 407 } 408 if (networkId === -1) { 409 return; 410 } 411 LogUtil.info(MODULE_TAG + 'start to removeDevice'); 412 wifi.removeDevice(networkId); 413 } 414 415 connectByDeviceConfig(apInfo: WifiScanInfo) { 416 let deviceConfigs: any[] = wifi.getDeviceConfigs(); 417 // find the wifi device config 418 for (let i = 0; i < deviceConfigs.length; i++) { 419 if (deviceConfigs[i].ssid === apInfo.ssid) { 420 let ret = wifi.connectToDevice(deviceConfigs[i]); 421 LogUtil.info(MODULE_TAG + 'connect ret for : ' + i + ' = ' + ret); 422 } 423 } 424 LogUtil.info(MODULE_TAG + 'end connect by device config'); 425 } 426 427 refreshApScanResults() { 428 wifi.getLinkedInfo((err: BusinessError, result: wifi.WifiLinkedInfo) => { 429 if (err) { 430 LogUtil.info(MODULE_TAG + 'get linked info failed'); 431 return; 432 } 433 LogUtil.info(MODULE_TAG + 'scan get linked info succeed'); 434 this.linkedApInfo = result; 435 }); 436 437 let results: wifi.WifiScanInfo[] = wifi.getScanInfoList() 438 LogUtil.info(MODULE_TAG + 'get scan info succeed'); 439 440 function removeDuplicateResults(arr: any[]): ApScanResult[] { 441 let results: ApScanResult[] = []; 442 for (let i = 0; i < arr.length; i++) { 443 let apResult = new ApScanResult(arr[i]); 444 if (apResult.isValidAp()) { 445 results.push(apResult); 446 } 447 } 448 return ApScanResult.filter(results); 449 }; 450 451 function removeConnectedAp(arr: ApScanResult[], needRemove: ApScanResult) { 452 let index = ApScanResult.index(arr, needRemove); 453 if (index !== -1) { 454 arr.splice(index, 1); 455 } 456 return arr; 457 } 458 459 function addSavedConfigFlag(aps: ApScanResult[]): ApScanResult[] { 460 let configs: ApScanResult[] = []; 461 let deviceConfigs: any[] = wifi.getDeviceConfigs(); 462 for (let i = 0; i < deviceConfigs.length; i++) { 463 let temp = new ApScanResult(deviceConfigs[i]); 464 configs.push(temp); 465 } 466 for (let i = 0; i < configs.length; i++) { 467 let index = ApScanResult.index(aps, configs[i]); 468 if (index !== -1) { 469 let item = aps[index]; 470 item.updateSavedStatus(true); 471 aps.splice(index, 1); 472 aps.unshift(item); 473 } 474 } 475 return aps; 476 } 477 478 function addConnectStatusFlag(arr: ApScanResult[], linked: any): ApScanResult { 479 let ap: ApScanResult = new ApScanResult(); 480 for (let i = 0; i < arr.length; i++) { 481 if (arr[i].getApInfo().ssid === linked.ssid) { 482 ap = arr[i]; 483 break; 484 } 485 } 486 ap.updateConnectStatus(linked.connState); 487 return ap; 488 } 489 490 function unshiftConnectingAp(arr: ApScanResult[], ap: ApScanResult): ApScanResult[] { 491 let index = ApScanResult.index(arr, ap); 492 if (index !== -1) { 493 arr.splice(index, 1); 494 arr.unshift(ap); 495 } 496 return arr; 497 } 498 499 // step 1 : remove duplicate ap info 500 let scanResults: ApScanResult[] = removeDuplicateResults(results); 501 LogUtil.info(MODULE_TAG + 'scan results items length is : ' + scanResults.length); 502 503 // step 2 : add saved config flags 504 scanResults = addSavedConfigFlag(scanResults); 505 scanResults.sort(ApScanResult.compare); 506 507 // step 3 : add wifi summary 508 if (this.linkedApInfo) { 509 let linkInfoResult: ApScanResult = addConnectStatusFlag(scanResults, this.linkedApInfo); 510 if (linkInfoResult.isConnected()) { 511 AppStorage.SetOrCreate('slnetId', this.linkedApInfo.networkId + ''); 512 LogUtil.info(MODULE_TAG + 'scan connected'); 513 scanResults = removeConnectedAp(scanResults, linkInfoResult); 514 AppStorage.SetOrCreate('slConnectedWifi', linkInfoResult.renderToListModel()); 515 } else { 516 LogUtil.info(MODULE_TAG + 'scan not connected'); 517 scanResults = unshiftConnectingAp(scanResults, linkInfoResult); 518 AppStorage.SetOrCreate('slConnectedWifi', (new ApScanResult()).renderToListModel()); 519 } 520 } 521 LogUtil.info(MODULE_TAG + 'scan list results'); 522 AppStorage.SetOrCreate('slWiFiLists', scanResults.map((item) => { 523 return item.renderToListModel(); 524 })); 525 } 526 527 startScanTask() { 528 LogUtil.info(MODULE_TAG + 'start the wifi scan task'); 529 530 if (this.scanTaskId !== Undefined_TaskId) { 531 clearInterval(this.scanTaskId); 532 this.scanTaskId = Undefined_TaskId; 533 } 534 535 this.scanTaskId = setInterval(() => { 536 if (this.isWiFiActive() && this.isScanning) { 537 this.refreshApScanResults(); 538 return; 539 } 540 if (this.isWiFiActive() === true) { 541 LogUtil.info(MODULE_TAG + 'scan wifi started'); 542 this.scanWiFi(); 543 this.isScanning = true; 544 this.refreshApScanResults(); 545 } 546 }, 5000); 547 } 548 549 stopScanTask() { 550 LogUtil.info(MODULE_TAG + 'stop the wifi scan task'); 551 if (this.scanTaskId !== Undefined_TaskId) { 552 clearInterval(this.scanTaskId); 553 this.scanTaskId = Undefined_TaskId; 554 } 555 this.isScanning = false; 556 } 557} 558 559let wifiModel = new WifiModel(); 560export default wifiModel as WifiModel; 561