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 ArrayList from '@ohos.util.ArrayList';
17import type { MessageEvent } from '@ohos/common';
18import { Log } from '@ohos/common';
19import CheckEmptyUtils from '@ohos/common';
20import type { P2PDiscoveryListener } from './Discovery';
21import worker from '@ohos.worker';
22import { WorkerUtil } from '../utils/WorkerUtil';
23import { WorkerRequest, RequestCode, ResponseCode } from '../model/WorkerData';
24import type { WorkerResponse } from '../model/WorkerData';
25
26const TAG = 'P2pMonitor';
27
28export class P2PMonitor {
29  private readonly _listeners: ArrayList<P2PDiscoveryListener> = new ArrayList<P2PDiscoveryListener>();
30  private _worker: worker.ThreadWorker;
31
32  constructor() {
33    this.initDiscoveryWorker();
34  }
35
36  public discovery(listener: P2PDiscoveryListener): void {
37    Log.debug(TAG, 'start discovery enter');
38    if (this._listeners.isEmpty()) {
39      Log.info(TAG, '_listeners is empty, start discover');
40      this._listeners.add(listener);
41      let discoverRequest: WorkerRequest = new WorkerRequest(RequestCode.P2P_START_DISCOVERY);
42      WorkerUtil.postMessageToWorkerThread(this._worker, discoverRequest);
43    } else {
44      Log.info(TAG, '_listeners is not empty ignore');
45    }
46  }
47
48  public stopDiscover(listener: P2PDiscoveryListener): void {
49    Log.debug(TAG, 'remove discovery listener');
50    this._listeners.remove(listener);
51    if (this._listeners.isEmpty()) {
52      Log.debug(TAG, 'stop discovery');
53      let initRequest: WorkerRequest = new WorkerRequest(RequestCode.P2P_CANCEL_DISCOVERY);
54      WorkerUtil.postMessageToWorkerThread(this._worker, initRequest);
55    }
56  }
57
58  handleP2pDiscoveryResult(responseCode: ResponseCode, data): void {
59    if (ResponseCode.ERROR === responseCode) {
60      Log.error(TAG, 'handleP2pDiscoveryResult ResponseCode is error');
61      return;
62    }
63    if (data.found) {
64      for (let listener of this._listeners) {
65        listener.onPeerFound(data.p2pDevice);
66      }
67    } else {
68      for (let listener of this._listeners) {
69        listener.onPeerLost(data.p2pDevice);
70      }
71    }
72  }
73
74  private initDiscoveryWorker(): boolean {
75    if (this._worker === undefined) {
76      this._worker = new worker.ThreadWorker('entry/ets/workers/DiscoveryWorker.ts', { type: 'classic', name: 'DiscoveryWorkerOfExtension' });
77      if (this._worker === undefined) {
78        Log.error(TAG, 'initWorker failed');
79        return false;
80      }
81      this._worker.onmessage = this.onMessage;
82      this._worker.onmessageerror = (messageEvent: MessageEvent<object>): void => {
83        Log.error(TAG, 'onMessageError : ' + JSON.stringify(messageEvent));
84      };
85      this._worker.onerror = (errorEvent: object): void => {
86        Log.error(TAG, 'onError : ' + JSON.stringify(errorEvent));
87      };
88      this._worker.onexit = (code: number): void => {
89        Log.info(TAG, 'onExit : ' + code);
90        this._worker = undefined;
91      };
92      Log.info(TAG, 'initDiscoveryWorker success');
93      return true;
94    }
95    return false;
96  }
97
98  private onMessage = (messageEvent: MessageEvent<WorkerResponse>): void => {
99    if (CheckEmptyUtils.isEmpty(messageEvent)) {
100      Log.error(TAG, 'discovery worker response is empty');
101      return;
102    }
103    if (!messageEvent.hasOwnProperty('data')) {
104      Log.error(TAG, 'messageEvent has not data');
105      return;
106    }
107    let workerResponse: WorkerResponse = messageEvent.data;
108    if (workerResponse === undefined || workerResponse === null) {
109      Log.error(TAG, 'workerResponse is empty');
110      return;
111    }
112    if (!workerResponse.hasOwnProperty('requestCode') || !workerResponse.hasOwnProperty('responseCode')) {
113      Log.error(TAG, 'workerResponse is error');
114      return;
115    }
116    let requestCode = workerResponse.requestCode;
117    let responseCode = workerResponse.responseCode;
118    Log.debug(TAG, `requestCode: ${WorkerUtil.getStringByWorkerCode(requestCode)}, responseCode: ${responseCode}`);
119    switch (requestCode) {
120      case RequestCode.P2P_START_DISCOVERY:
121        this.handleP2pDiscoveryResult(responseCode, <object>workerResponse.data);
122        break;
123      default:
124        Log.error(TAG, 'onMessage response. error code');
125    }
126  };
127}