1/* 2 * Copyright (c) 2023-2023 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 { Log } from '@ohos/common'; 17import CheckEmptyUtils from '@ohos/common'; 18import CommonUtils from '../utils/CommonUtils'; 19import type { WifiListener } from '../model/WifiModel'; 20import ArrayList from '@ohos.util.ArrayList'; 21import HashSet from '@ohos.util.HashSet'; 22import wifi from '@ohos.wifi'; 23import CommonEventManager from '@ohos.commonEventManager'; 24 25const TAG = 'P2pDiscoveryChannel'; 26const DISCOVERY_SLEEP: number = 60000; 27 28export class P2PDiscoveryChannel implements WifiListener { 29 private static instance: P2PDiscoveryChannel; 30 private static readonly printerPattern: RegExp = /^(^3-.+-[145])|(0003.+000[145])$/; 31 private readonly p2pDevices: ArrayList<wifi.WifiP2pDevice> = new ArrayList<wifi.WifiP2pDevice>(); 32 private discoveryCallback: (found: boolean, peer: wifi.WifiP2pDevice) => void; 33 private discoverySleepTimer: number; 34 private wifiSubscriber; 35 36 public static getInstance(): P2PDiscoveryChannel { 37 Log.info(TAG, 'getInstance enter'); 38 if (this.instance === undefined) { 39 this.instance = new P2PDiscoveryChannel(); 40 } 41 return this.instance; 42 } 43 44 startDiscovery(callback: (found: boolean, peer: wifi.WifiP2pDevice) => void): void { 45 this.discoveryCallback = callback; 46 wifi.on('p2pPeerDeviceChange', this.updatePeerDevices); 47 this.startP2pDiscovery(); 48 this.registerWifiCommonEvent(); 49 50 // 3分钟后,native p2pService进入休眠状态,清除标记 51 this.discoverySleepTimer = setInterval(()=> { 52 Log.debug(TAG, 'native p2p service is sleep, start discovery'); 53 this.startP2pDiscovery(); 54 }, DISCOVERY_SLEEP); 55 } 56 57 private startP2pDiscovery(): void { 58 wifi.startDiscoverDevices(); 59 wifi.getP2pPeerDevices().then((peers: wifi.WifiP2pDevice[]) => { 60 this.updatePeerDevices(peers); 61 }); 62 } 63 64 /** 65 * register wifi common event; 66 */ 67 private async registerWifiCommonEvent(): Promise<void> { 68 try { 69 this.wifiSubscriber = await CommonEventManager.createSubscriber({events: 70 [CommonEventManager.Support.COMMON_EVENT_WIFI_P2P_CONN_STATE]}); 71 CommonEventManager.subscribe(this.wifiSubscriber, (err, data) => { 72 if (err) { 73 Log.error(TAG, `subscribe failed, code is ${err.code}, message is ${err.message}`); 74 } else { 75 this.onConnectionStateChanged(data); 76 } 77 }); 78 } catch (error) { 79 Log.error(TAG, 'create subscriber fail: ' + JSON.stringify(error)); 80 } 81 } 82 83 private updatePeerDevices = (newDevices: wifi.WifiP2pDevice[]): void => { 84 if (CheckEmptyUtils.isEmptyArr<wifi.WifiP2pDevice>(newDevices)) { 85 Log.error(TAG, 'newDevices is empty'); 86 return; 87 } 88 Log.debug(TAG, `found length: ${newDevices.length}`); 89 let oldDevices = new ArrayList<wifi.WifiP2pDevice>(); 90 for (let p2pDevice of this.p2pDevices) { 91 oldDevices.add(p2pDevice); 92 } 93 this.p2pDevices.clear(); 94 for (let newDevice of newDevices) { 95 if (newDevice !== undefined && newDevice.primaryDeviceType !== undefined && 96 P2PDiscoveryChannel.printerPattern.test(<string>newDevice.primaryDeviceType)) { 97 let index = this.p2pDevices.convertToArray().findIndex((device) => { 98 return device.deviceAddress === newDevice.deviceAddress; 99 }); 100 if (index < 0) { 101 this.p2pDevices.add(newDevice); 102 } 103 } 104 } 105 106 // notify new found devices 107 let foundAddress: HashSet<string> = new HashSet<string>(); 108 let foundPrinters = this.p2pDevices.convertToArray().filter((newDevice) => { 109 foundAddress.add(newDevice.deviceAddress); 110 let index = oldDevices.convertToArray().findIndex((device) => { 111 return device.deviceAddress === newDevice.deviceAddress; 112 }); 113 if (index < 0) { 114 return true; 115 } 116 return false; 117 }); 118 if (!CheckEmptyUtils.isEmptyArr<wifi.WifiP2pDevice>(foundPrinters)) { 119 for (let foundPrinter of foundPrinters) { 120 this.discoveryCallback(true, foundPrinter); 121 } 122 } 123 124 // notify lost devices 125 for (let oldDevice of oldDevices) { 126 if (!foundAddress.has(oldDevice.deviceAddress)) { 127 this.discoveryCallback(false, oldDevice); 128 } 129 } 130 }; 131 132 onConnectionStateChanged(data): void { 133 if (data.event === CommonEventManager.Support.COMMON_EVENT_WIFI_P2P_CONN_STATE && data.code === wifi.P2pConnectState.DISCONNECTED) { 134 Log.debug(TAG, 'p2p connect state change event: ' + JSON.stringify(data)); 135 if (this.discoverySleepTimer !== undefined) { 136 this.startP2pDiscovery(); 137 } 138 } 139 } 140 141 /** 142 * stop discovery 143 */ 144 public cancel(): void { 145 Log.info('stop p2p discovery'); 146 this.p2pDevices.clear(); 147 wifi.off('p2pPeerDeviceChange', this.updatePeerDevices); 148 wifi.stopDiscoverDevices(); 149 if (this.wifiSubscriber !== undefined) { 150 CommonEventManager.unsubscribe(this.wifiSubscriber); 151 } 152 clearTimeout(this.discoverySleepTimer); 153 } 154}